Vue解析剪切板圖片並實現發送功能

每一份堅持都是成功的累積,只要相信本身,總會遇到驚喜😜javascript

前言

咱們在使用QQ進行聊天時,從別的地方Ctrl+C一張圖片,而後在聊天窗口Ctrl+V,QQ就會將你剛纔複製的圖片粘貼到即將發送的消息容器裏,按下Enter鍵,這張圖片將會發送出去。接下來跟各位開發者分享下這項功能在Vue中如何來實現。先跟你們展現下最終實現的效果。在線體驗地址 html

實現思路

  • 頁面掛載時監聽剪切板粘貼事件
  • 監聽文件流
  • 讀取文件流中的數據
  • 建立img標籤
  • 將獲取到的base64碼賦值到img標籤的src屬性
  • 將生成的img標籤append到即將發送的消息容器裏
  • 監聽回車事件
  • 獲取可編輯div容器中的全部子元素
  • 遍歷獲取到的元素,找出img元素
  • 判斷當前img元素是否有alt屬性(表情插入時有alt屬性),
  • 若是沒有alt屬性當前元素就是圖片
  • 將base64格式的圖片轉成文件上傳至服務器
  • 上傳成功後,將服務器返回的圖片地址推送到websocket服務
  • 客戶端收到推送後,渲染頁面

實現過程

本片文章主要講解剪切板圖片的解析以及將base64圖片轉換成文件上傳至服務器,下方代碼中的axios的封裝以及websocket的配置與使用可參考個人另外兩篇文章:Vue合理配置axios並在項目中進行實際應用Vue合理配置WebSocket並實現羣聊前端

  • 監聽剪切板事件(mounted生命週期中),將圖片渲染到即將發送到消息容器裏java

    /** * 監聽粘貼監聽事件: 實現圖片粘貼 */
    let that = this;
    document.body.addEventListener('paste', function (e) {
        // 監聽文件流
        let reader = new FileReader();
        let clipboard = e.clipboardData;
        reader.onload = function (evt) {
            // 建立一個img
            let img = document.createElement('img');
            img.src = evt.target.result;//設置連接
            // 圖片渲染
            that.$refs.msgInputContainer.append(img);
        };
        let file = clipboard.items[0];
        if (file.kind == 'file') {
            reader.readAsDataURL(file.getAsFile());//啓動文件流事件
        }
    });
    複製代碼
  • 完善消息發送函數,獲取輸入框裏的全部子元素,找出base64圖片將其轉爲文件並上傳至服務器(此處須要注意:base64轉文件時,須要用正則表達式刪掉base64圖片的前綴),將當前圖片地址推送至websocket服務。node

    對下述代碼有不理解的地方,可閱讀個人另外一篇文章:Vue實現圖片與文字混輸ios

    sendMessage: function (event) {
        if (event.keyCode === 13) {
            // 阻止編輯框默認生成div事件
            event.preventDefault();
            let msgText = "";
            // 獲取輸入框下的全部子元素
            let allNodes = event.target.childNodes;
            for (let item of allNodes) {
                // 判斷當前元素是否爲img元素
                if (item.nodeName === "IMG") {
                    if (item.alt === "") {
                        // 是圖片
                        let base64Img = item.src;
                        // 刪除base64圖片的前綴
                        base64Img = base64Img.replace(/^data:image\/\w+;base64,/, "");
                        //隨機文件名
                        let fileName = (new Date()).getTime() + ".jpeg";
                        //將base64轉換成file
                        let imgFile = this.convertBase64UrlToImgFile(base64Img, fileName, 'image/jpeg');
                        let formData = new FormData();
                        // 此處的file與後臺取值時的屬性同樣,append時須要添加文件名,不然一直時blob
                        formData.append('file', imgFile, fileName);
                        // 將圖片上傳至服務器
                        this.$api.fileManageAPI.baseFileUpload(formData).then((res) => {
                            const msgImgName = `/${res.fileName}/`;
                            // 消息發送: 發送圖片
                            this.$socket.sendObj({
                                msg: msgImgName,
                                code: 0,
                                username: this.$store.state.username,
                                avatarSrc: this.$store.state.profilePicture,
                                userID: this.$store.state.userID
                            });
                            // 清空輸入框中的內容
                            event.target.innerHTML = "";
                        });
                    } else {
                        msgText += `/${item.alt}/`;
                    }
                } else {
                    // 獲取text節點的值
                    if (item.nodeValue !== null) {
                        msgText += item.nodeValue;
                    }
                }
            }
            // 消息發送: 發送文字,爲空則不發送
            if (msgText.trim().length > 0) {
                this.$socket.sendObj({
                    msg: msgText,
                    code: 0,
                    username: this.$store.state.username,
                    avatarSrc: this.$store.state.profilePicture,
                    userID: this.$store.state.userID
                });
                // 清空輸入框中的內容
                event.target.innerHTML = "";
            }
        }
    }
    複製代碼
  • base64圖片轉flieweb

    // base64轉file
    convertBase64UrlToImgFile: function (urlData, fileName, fileType) {
        // 轉換爲byte
        let bytes = window.atob(urlData);
        // 處理異常,將ascii碼小於0的轉換爲大於0
        let ab = new ArrayBuffer(bytes.length);
        let ia = new Int8Array(ab);
        for (let i = 0; i < bytes.length; i++) {
            ia[i] = bytes.charCodeAt(i);
        }
        // 轉換成文件,添加文件的type,name,lastModifiedDate屬性
        let blob = new Blob([ab], {type: fileType});
        blob.lastModifiedDate = new Date();
        blob.name = fileName;
        return blob;
    }
    複製代碼
  • axios文件上傳接口的封裝(注意:須要設置"Content-Type":"multipart/form-data"})正則表達式

    /* * 文件管理接口 * */
    import services from '../plugins/axios'
    import base from './base'; // 導入接口域名列表
    
    const fileManageAPI = {
        // 單文件上傳
        baseFileUpload(file){
            return services._axios.post(`${base.lkBaseURL}/uploads/singleFileUpload`,file,{headers:{"Content-Type":"multipart/form-data"}});
        }
    };
    
    export default fileManageAPI;
    
    複製代碼
  • 解析websocket推送的消息axios

    // 消息解析
    messageParsing: function (msgObj) {
        // 解析接口返回的數據並進行渲染
        let separateReg = /(\/[^/]+\/)/g;
        let msgText = msgObj.msgText;
        let finalMsgText = "";
        // 將符合條件的字符串放到數組裏
        const resultArray = msgText.match(separateReg);
        if (resultArray !== null) {
            for (let item of resultArray) {
                // 刪除字符串中的/符號
                item = item.replace(/\//g, "");
                // 判斷是否爲圖片: 後綴爲.jpeg
                if(this.isImg(item)){
                    // 解析爲img標籤
                    const imgTag = `<img src="${base.lkBaseURL}/upload/image/${item}" alt="聊天圖片">`;
                    // 替換匹配的字符串爲img標籤:全局替換
                    msgText = msgText.replace(new RegExp(`/${item}/`, 'g'), imgTag);
                }
            }
            finalMsgText = msgText;
        } else {
            finalMsgText = msgText;
        }
        msgObj.msgText = finalMsgText;
        // 渲染頁面
        this.senderMessageList.push(msgObj);
        // 修改滾動條位置
        this.$nextTick(function () {
            this.$refs.messagesContainer.scrollTop = this.$refs.messagesContainer.scrollHeight;
        });
    }
    複製代碼
  • 判斷當前字符串是否爲有圖片後綴後端

    // 判斷是否爲圖片
    isImg: function (str) {
        let objReg = new RegExp("[.]+(jpg|jpeg|swf|gif)$", "gi");
        return objReg.test(str);
    }
    複製代碼

踩坑記錄

  • 直接將base64格式的圖片經過websocket發送至服務端

    結果很明顯,服務端websocket服務報錯,報錯緣由:內容超過最大長度。

  • 前端經過post請求將base64碼傳到服務端,服務端直接將base64碼解析爲圖片保存至服務器

    從下午2點折騰到晚上6點,一直在找Java解析base64圖片存到服務器的方案,最終選擇了放棄,採用了前端轉換方式,這裏的問題大概是前端傳base64碼到後端時,http請求會進行轉義,致使後端解析獲得的base64碼是錯誤的,因此一直沒有成功。

寫在最後

  • 文中若有錯誤,歡迎在評論區指正,若是這篇文章幫到了你,歡迎點贊和關注😊
  • 本文首發於掘金,如需轉載請評論區留言💌
相關文章
相關標籤/搜索