我們或多或少都使用過各式各樣的富文本編輯器,其中有一個很方便功能,復制一張圖片然後粘貼進文本框,這張圖片就被上傳了,那麼這個方便的功能是如何實現的呢?
原理分析
提取操作:復制=>粘貼=>上傳
在這個操作過程中,我們需要做的就是:監聽粘貼事件=>獲取剪貼板裡的內容=>發請求上傳
為方便理解下文,需要先明白幾點:
test代碼及截圖如下:
chrome:
<textarea ></textarea> <div contenteditable style="width: 100px;height: 100px; border:1px solid"> </div> <script> document.addEventListener('paste', function (event) { console.log(event) }) </script>
firefox:
ie11:(不截圖了,可自行試驗,其他浏覽器同理<( ̄▽ ̄)/,因為懶...)
監聽了paste事件,也知道了表現形式,接下來就是如何獲取數據了:
chrome有特定的方法,利用clipboardData.items、getAsFile()、new FileReader()等api可以在paste回調函數裡獲取到剪貼板裡圖片的base64編碼字符串(無論是截圖粘貼的還是網頁圖片復制粘貼的),ie11,firefox沒有這樣的api,不過依然有辦法可以獲取,因為數據已經表現在img的src裡了,對於截圖粘貼的,直接取img的src屬性值(base64),對於網頁粘貼的,則把地址傳給後台,然後根據地址down下來,存在自己的服務器,最後把新地址返回來交給前端展示就ok了。為了保持一致性便於管理,統一將所有情況(截圖、網頁)中的img的src屬性替換為自己存儲的地址。因此可以得到以下核心代碼(注釋很全哦~~):
html展示:
<head> <meta charset="UTF-8"> <title>Document</title> <style> body { display: -webkit-flex; display: flex; -webkit-justify-content: center; justify-content: center; } #tar_box { width: 500px; height: 500px; border: 1px solid red; } </style>
前端js處理邏輯:
document.addEventListener('paste', function (event) { console.log(event) var isChrome = false; if ( event.clipboardData || event.originalEvent ) { //not for ie11 某些chrome版本使用的是event.originalEvent var clipboardData = (event.clipboardData || event.originalEvent.clipboardData); if ( clipboardData.items ) { // for chrome var items = clipboardData.items, len = items.length, blob = null; isChrome = true; //items.length比較有意思,初步判斷是根據mime類型來的,即有幾種mime類型,長度就是幾(待驗證) //如果粘貼純文本,那麼len=1,如果粘貼網頁圖片,len=2, items[0].type = 'text/plain', items[1].type = 'image/*' //如果使用截圖工具粘貼圖片,len=1, items[0].type = 'image/png' //如果粘貼純文本+HTML,len=2, items[0].type = 'text/plain', items[1].type = 'text/html' // console.log('len:' + len); // console.log(items[0]); // console.log(items[1]); // console.log( 'items[0] kind:', items[0].kind ); // console.log( 'items[0] MIME type:', items[0].type ); // console.log( 'items[1] kind:', items[1].kind ); // console.log( 'items[1] MIME type:', items[1].type ); //阻止默認行為即不讓剪貼板內容在div中顯示出來 event.preventDefault(); //在items裡找粘貼的image,據上面分析,需要循環 for (var i = 0; i < len; i++) { if (items[i].type.indexOf("image") !== -1) { // console.log(items[i]); // console.log( typeof (items[i])); //getAsFile() 此方法只是living standard firefox ie11 並不支持 blob = items[i].getAsFile(); } } if ( blob !== null ) { var reader = new FileReader(); reader.onload = function (event) { // event.target.result 即為圖片的Base64編碼字符串 var base64_str = event.target.result //可以在這裡寫上傳邏輯 直接將base64編碼的字符串上傳(可以嘗試傳入blob對象,看看後台程序能否解析) uploadImgFromPaste(base64_str, 'paste', isChrome); } reader.readAsDataURL(blob); } } else { //for firefox setTimeout(function () { //設置setTimeout的原因是為了保證圖片先插入到div裡,然後去獲取值 var imgList = document.querySelectorAll('#tar_box img'), len = imgList.length, src_str = '', i; for ( i = 0; i < len; i ++ ) { if ( imgList[i].className !== 'my_img' ) { //如果是截圖那麼src_str就是base64 如果是復制的其他網頁圖片那麼src_str就是此圖片在別人服務器的地址 src_str = imgList[i].src; } } uploadImgFromPaste(src_str, 'paste', isChrome); }, 1); } } else { //for ie11 setTimeout(function () { var imgList = document.querySelectorAll('#tar_box img'), len = imgList.length, src_str = '', i; for ( i = 0; i < len; i ++ ) { if ( imgList[i].className !== 'my_img' ) { src_str = imgList[i].src; } } uploadImgFromPaste(src_str, 'paste', isChrome); }, 1); } }) function uploadImgFromPaste (file, type, isChrome) { var formData = new FormData(); formData.append('image', file); formData.append('submission-type', type); var xhr = new XMLHttpRequest(); xhr.open('POST', '/upload_image_by_paste'); xhr.onload = function () { if ( xhr.readyState === 4 ) { if ( xhr.status === 200 ) { var data = JSON.parse( xhr.responseText ), tarBox = document.getElementById('tar_box'); if ( isChrome ) { var img = document.createElement('img'); img.className = 'my_img'; img.src = data.store_path; tarBox.appendChild(img); } else { var imgList = document.querySelectorAll('#tar_box img'), len = imgList.length, i; for ( i = 0; i < len; i ++) { if ( imgList[i].className !== 'my_img' ) { imgList[i].className = 'my_img'; imgList[i].src = data.store_path; } } } } else { console.log( xhr.statusText ); } }; }; xhr.onerror = function (e) { console.log( xhr.statusText ); } xhr.send(formData); }
用express.js搭的簡易後台的接收邏輯:
router.post('/', upload.array(), function (req, res, next) { //1.獲取客戶端傳來的src_str字符串=>判斷是base64還是普通地址=>獲取圖片類型後綴(jpg/png etc) //=>如果是base64替換掉"前綴"("data:image\/png;base64," etc) //2.base64 轉為 buffer對象 普通地址則先down下來 //3.寫入硬盤(後續可以將地址存入數據庫) //4.返回picture地址 var src_str = req.body.image, timestamp = new Date().getTime(); if ( src_str.match(/^data:image\/png;base64,|^data:image\/jpg;base64,|^data:image\/jpg;base64,|^data:image\/bmp;base64,/) ) { //處理截圖 src_str為base64字符串 var pic_suffix = src_str.split(';',1)[0].split('/',2)[1], base64 = src_str.replace(/^data:image\/png;base64,|^data:image\/jpg;base64,|^data:image\/jpg;base64,|^data:image\/bmp;base64,/, ''), buf = new Buffer(base64, 'base64'), store_path = 'public/images/test_' + timestamp + '.' + pic_suffix; fs.writeFile(store_path, buf, function (err) { if (err) { throw err; } else { res.json({'store_path': store_path}); } }); } else {// 處理非chrome的網頁圖片 src_str為圖片地址 var temp_array = src_str.split('.'), pic_suffix = temp_array[temp_array.length - 1], store_path = 'public/images/test_' + timestamp + '.' + pic_suffix, wstream = fs.createWriteStream(store_path); request(src_str).pipe(wstream); wstream.on('finish', function (err) { if( err ) { throw err; } else { res.json({"store_path": store_path}); } }); } });
需要node環境:安裝node=>npm intall=>node app.js)
以上就是本文的全部內容,希望對大家的學習有所幫助。