溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Vue解析剪切板圖片并實(shí)現(xiàn)發(fā)送功能

發(fā)布時(shí)間:2020-09-10 13:34:36 來源:腳本之家 閱讀:139 作者:神奇的程序員 欄目:web開發(fā)

前言

我們?cè)谑褂肣Q進(jìn)行聊天時(shí),從別的地方Ctrl+C一張圖片,然后在聊天窗口Ctrl+V,QQ就會(huì)將你剛才復(fù)制的圖片粘貼到即將發(fā)送的消息容器里,按下Enter鍵,這張圖片將會(huì)發(fā)送出去。接下來跟各位開發(fā)者分享下這項(xiàng)功能在Vue中如何來實(shí)現(xiàn)。先跟大家展示下最終實(shí)現(xiàn)的效果。在線體驗(yàn)地址

Vue解析剪切板圖片并實(shí)現(xiàn)發(fā)送功能

實(shí)現(xiàn)思路

  • 頁面掛載時(shí)監(jiān)聽剪切板粘貼事件
  • 監(jiān)聽文件流
  • 讀取文件流中的數(shù)據(jù)
  • 創(chuàng)建img標(biāo)簽
  • 將獲取到的base64碼賦值到img標(biāo)簽的src屬性
  • 將生成的img標(biāo)簽append到即將發(fā)送的消息容器里
  • 監(jiān)聽回車事件
  • 獲取可編輯div容器中的所有子元素
  • 遍歷獲取到的元素,找出img元素
  • 判斷當(dāng)前img元素是否有alt屬性(表情插入時(shí)有alt屬性),
  • 如果沒有alt屬性當(dāng)前元素就是圖片
  • 將base64格式的圖片轉(zhuǎn)成文件上傳至服務(wù)器
  • 上傳成功后,將服務(wù)器返回的圖片地址推送到websocket服務(wù)
  • 客戶端收到推送后,渲染頁面

實(shí)現(xiàn)過程

本片文章主要講解剪切板圖片的解析以及將base64圖片轉(zhuǎn)換成文件上傳至服務(wù)器,下方代碼中的axios的封裝以及websocket的配置與使用可參考我的另外兩篇文章:Vue合理配置axios并在項(xiàng)目中進(jìn)行實(shí)際應(yīng)用和Vue合理配置WebSocket并實(shí)現(xiàn)群聊

監(jiān)聽剪切板事件(mounted生命周期中),將圖片渲染到即將發(fā)送到消息容器里

const that = this;
document.body.addEventListener('paste', function (event) {
 // 自己寫的一個(gè)全屏加載插件,文章地址:https://juejin.im/post/5e3307145188252c30002fa7
 that.$fullScreenLoading.show("讀取圖片中");
 // 獲取當(dāng)前輸入框內(nèi)的文字
 const oldText = that.$refs.msgInputContainer.textContent;
 // 讀取圖片
 let items = event.clipboardData && event.clipboardData.items;
 let file = null;
 if (items && items.length) {
 // 檢索剪切板items
 for (let i = 0; i < items.length; i++) {
  if (items[i].type.indexOf('image') !== -1) {
  file = items[i].getAsFile();
  break;
  }
 }
 }
 // 預(yù)覽圖片
 const reader = new FileReader();
 reader.onload = function(event) {
 // 圖片內(nèi)容
 const imgContent = event.target.result;
 // 創(chuàng)建img標(biāo)簽
 let img = document.createElement('img');//創(chuàng)建一個(gè)img
 // 獲取當(dāng)前base64圖片信息,計(jì)算當(dāng)前圖片寬高以及壓縮比例
 let imgObj = new Image();
 let imgWidth = "";
 let imgHeight = "";
 let scale = 1;
 imgObj.src = imgContent;
 imgObj.onload = function() {
  // 計(jì)算img寬高
  if(this.width<400){
  imgWidth = this.width;
  imgHeight = this.height;
  }else{
  // 輸入框圖片顯示縮小10倍
  imgWidth = this.width/10;
  imgHeight = this.height/10;
  // 圖片寬度大于1920,圖片壓縮5倍
  if(this.width>1920){
   // 真實(shí)比例縮小5倍
   scale = 5;
  }
  }
  // 設(shè)置可編輯div中圖片寬高
  img.width = imgWidth;
  img.height = imgHeight;
  // 壓縮圖片,渲染頁面
  that.compressPic(imgContent,scale,function (newBlob,newBase) {
  // 刪除可編輯div中的圖片名稱
  that.$refs.msgInputContainer.textContent = oldText;
  img.src = newBase; //設(shè)置鏈接
  // 圖片渲染
  that.$refs.msgInputContainer.append(img);
  that.$fullScreenLoading.hide();
  });
 };
 };
 reader.readAsDataURL(file);
});

base64圖片壓縮函數(shù)

 // 參數(shù): base64地址,壓縮比例,回調(diào)函數(shù)(返回壓縮后圖片的blob和base64)
 compressPic:function(base64, scale, callback){
 const that = this;
 let _img = new Image();
 _img.src = base64;
 _img.onload = function() {
  let _canvas = document.createElement("canvas");
  let w = this.width / scale;
  let h = this.height / scale;
  _canvas.setAttribute("width", w);
  _canvas.setAttribute("height", h);
  _canvas.getContext("2d").drawImage(this, 0, 0, w, h);
  let base64 = _canvas.toDataURL("image/jpeg");
  // 當(dāng)canvas對(duì)象的原型中沒有toBlob方法的時(shí)候,手動(dòng)添加該方法
  if (!HTMLCanvasElement.prototype.toBlob) {
  Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
   value: function (callback, type, quality) {
   let binStr = atob(this.toDataURL(type, quality).split(',')[1]),
    len = binStr.length,
    arr = new Uint8Array(len);
   for (let i = 0; i < len; i++) {
    arr[i] = binStr.charCodeAt(i);
   }
   callback(new Blob([arr], {type: type || 'image/png'}));
   }
  });
  }else{
  _canvas.toBlob(function(blob) {
   if(blob.size > 1024*1024){
   that.compressPic(base64, scale, callback);
   }else{
   callback(blob, base64);
   }
  }, "image/jpeg");
  }
 }
 }

完善消息發(fā)送函數(shù),獲取輸入框里的所有子元素,找出base64圖片將其轉(zhuǎn)為文件并上傳至服務(wù)器(此處需要注意:base64轉(zhuǎn)文件時(shí),需要用正則表達(dá)式刪掉base64圖片的前綴),將當(dāng)前圖片地址推送至websocket服務(wù)。

對(duì)下述代碼有不理解的地方,可閱讀我的另一篇文章:Vue實(shí)現(xiàn)圖片與文字混輸,

sendMessage: function (event) {
 if (event.keyCode === 13) {
 // 阻止編輯框默認(rèn)生成div事件
 event.preventDefault();
 let msgText = "";
 // 獲取輸入框下的所有子元素
 let allNodes = event.target.childNodes;
 for (let item of allNodes) {
  // 判斷當(dāng)前元素是否為img元素
  if (item.nodeName === "IMG") {
  if (item.alt === "") {
   // 是圖片
   let base64Img = item.src;
   // 刪除base64圖片的前綴
   base64Img = base64Img.replace(/^data:image\/\w+;base64,/, "");
   //隨機(jī)文件名
   let fileName = (new Date()).getTime() + ".jpeg";
   //將base64轉(zhuǎn)換成file
   let imgFile = this.convertBase64UrlToImgFile(base64Img, fileName, 'image/jpeg');
   let formData = new FormData();
   // 此處的file與后臺(tái)取值時(shí)的屬性一樣,append時(shí)需要添加文件名,否則一直時(shí)blob
   formData.append('file', imgFile, fileName);
   // 將圖片上傳至服務(wù)器
   this.$api.fileManageAPI.baseFileUpload(formData).then((res) => {
   const msgImgName = `/${res.fileName}/`;
   // 消息發(fā)送: 發(fā)送圖片
   this.$socket.sendObj({
    msg: msgImgName,
    code: 0,
    username: this.$store.state.username,
    avatarSrc: this.$store.state.profilePicture,
    userID: this.$store.state.userID
   });
   // 清空輸入框中的內(nèi)容
   event.target.innerHTML = "";
   });
  } else {
   msgText += `/${item.alt}/`;
  }
  } else {
  // 獲取text節(jié)點(diǎn)的值
  if (item.nodeValue !== null) {
   msgText += item.nodeValue;
  }
  }
 }
 // 消息發(fā)送: 發(fā)送文字,為空則不發(fā)送
 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
  });
  // 清空輸入框中的內(nèi)容
  event.target.innerHTML = "";
 }
 }
}

base64圖片轉(zhuǎn)flie

// base64轉(zhuǎn)file
convertBase64UrlToImgFile: function (urlData, fileName, fileType) {
 // 轉(zhuǎn)換為byte
 let bytes = window.atob(urlData);
 // 處理異常,將ascii碼小于0的轉(zhuǎn)換為大于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);
 }
 // 轉(zhuǎn)換成文件,添加文件的type,name,lastModifiedDate屬性
 let blob = new Blob([ab], {type: fileType});
 blob.lastModifiedDate = new Date();
 blob.name = fileName;
 return blob;
}

axios文件上傳接口的封裝(注意:需要設(shè)置"Content-Type":"multipart/form-data"})
/*
* 文件管理接口
* */
import services from '../plugins/axios'
import base from './base'; // 導(dǎo)入接口域名列表

const fileManageAPI = {
 // 單文件上傳
 baseFileUpload(file){
 return services._axios.post(`${base.lkBaseURL}/uploads/singleFileUpload`,file,{headers:{"Content-Type":"multipart/form-data"}});
 }
};
export default fileManageAPI;

解析websocket推送的消息

// 消息解析
messageParsing: function (msgObj) {
 // 解析接口返回的數(shù)據(jù)并進(jìn)行渲染
 let separateReg = /(\/[^/]+\/)/g;
 let msgText = msgObj.msgText;
 let finalMsgText = "";
 // 將符合條件的字符串放到數(shù)組里
 const resultArray = msgText.match(separateReg);
 if (resultArray !== null) {
  for (let item of resultArray) {
   // 刪除字符串中的/符號(hào)
   item = item.replace(/\//g, "");
   // 判斷是否為圖片: 后綴為.jpeg
   if(this.isImg(item)){
    // 解析為img標(biāo)簽
    const imgTag = `<img src="${base.lkBaseURL}/upload/image/${item}" alt="聊天圖片">`;
    // 替換匹配的字符串為img標(biāo)簽:全局替換
    msgText = msgText.replace(new RegExp(`/${item}/`, 'g'), imgTag);
   }
  }
  finalMsgText = msgText;
 } else {
  finalMsgText = msgText;
 }
 msgObj.msgText = finalMsgText;
 // 渲染頁面
 this.senderMessageList.push(msgObj);
 // 修改滾動(dòng)條位置
 this.$nextTick(function () {
  this.$refs.messagesContainer.scrollTop = this.$refs.messagesContainer.scrollHeight;
 });
}

判斷當(dāng)前字符串是否為有圖片后綴

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

踩坑記錄

直接將base64格式的圖片通過websocket發(fā)送至服務(wù)端

結(jié)果很明顯,服務(wù)端websocket服務(wù)報(bào)錯(cuò),報(bào)錯(cuò)原因:內(nèi)容超過最大長度。

前端通過post請(qǐng)求將base64碼傳到服務(wù)端,服務(wù)端直接將base64碼解析為圖片保存至服務(wù)器

從下午2點(diǎn)折騰到晚上6點(diǎn),一直在找Java解析base64圖片存到服務(wù)器的方案,最終選擇了放棄,采用了前端轉(zhuǎn)換方式,這里的問題大概是前端傳base64碼到后端時(shí),http請(qǐng)求會(huì)進(jìn)行轉(zhuǎn)義,導(dǎo)致后端解析得到的base64碼是錯(cuò)誤的,所以一直沒有成功。

項(xiàng)目地址:chat-system

總結(jié)

以上所述是小編給大家介紹的Vue解析剪切板圖片并實(shí)現(xiàn)發(fā)送功能,希望對(duì)大家有所幫助!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI