兼容chrome、firefox、ie11 js實現ctrl+v粘貼上傳圖片

我們或多或少都使用過各式各樣的富文本編輯器,其中有一個很方便功能,復制一張圖片然后粘貼進文本框,這張圖片就被上傳了,那么這個方便的功能是如何實現的呢?
原理分析
提取操作:復制=粘貼=上傳
在這個操作過程中,我們需要做的就是:監聽粘貼事件=獲取剪貼板里的內容=發請求上傳
為方便理解下文,需要先明白幾點:
我們只能上傳網頁圖(在網頁上右鍵圖片,然后復制)和截圖(截圖工具截的圖片,eg:qq截圖),不能粘貼上傳系統里的圖片(從桌面上、硬盤里復制),他們是存在完全不同的地方的 。截圖工具截的圖與在網頁點擊右鍵復制的圖是有些不同的 , 因此處理方式也不一樣 。知悉paste event這個事件:當進行粘貼(右鍵paste/ctrl v)操作時,該動作將觸發名為’paste’的剪貼板事件,這個事件的觸發是在剪貼板里的數據插入到目標元素之前 。如果目標元素(光標所在位置)是可編輯的元素(eg:設置了contenteditable屬性的div 。textarea并不行 。),粘貼動作將把剪貼板里的數據,以最合適的格式 , 插入到目標元素里;如果目標元素不可編輯,則不會插入數據,但依然觸發paste event 。數據在粘貼的過程中是只讀的 。此段是翻譯于w3.org_the-paste-action 。可惜的是,經過試驗,發現chrome(當前最新版)、firefox(當前最新版)、ie11對paste事件的實現并不是完全按照w3c來的,各自也有區別(w3c的paste標準也因此只是草案階段) 。
test代碼及截圖如下:
chrome:

textarea /textarea div contenteditable style="width: 100px;height: 100px; border:1px solid" /div script document.addEventListener(’paste’, function (event) {console.log(event) })/scriptevent有clipboardData屬性,且clipboardData有item屬性,clipboardData.item中的元素(對象)有type和kind屬性;無論在哪進行粘貼,均可觸發paste事件;在div(未特殊聲明時,本文div均指設置了contenteditable屬性的div) 里粘貼截圖,不顯示圖片 。img.src為base64編碼字符串;在div里粘貼網頁圖片,直接顯示圖片,img.src為圖片地址 。【兼容chrome、firefox、ie11 js實現ctrl v粘貼上傳圖片】firefox:
event有clipboardData屬性,clipboardData沒有item屬性;只有在textarea里或者可編輯的div(里才粘貼才觸發paste事件;在div里粘貼截圖,直接顯示圖片,img.src為base64編碼字符串;在div里粘貼網頁圖片,表現同chrome 。
ie11:(不截圖了 , 可自行試驗 , 其他瀏覽器同理( ̄▽ ̄)/,因為懶...)

event沒有clipboardData屬性;只在可編輯的div中粘貼才觸發paste事件;在div里粘貼截圖,直接顯示圖片,img.src為base64編碼字符串;在div里粘貼網頁圖片,表現同chrome 。
監聽了paste事件 , 也知道了表現形式 , 接下來就是如何獲取數據了:
chrome有特定的方法,利用clipboardData.items、getAsFile()、new FileReader()等api可以在paste回調函數里獲取到剪貼板里圖片的base64編碼字符串(無論是截圖粘貼的還是網頁圖片復制粘貼的) , ie11,firefox沒有這樣的api , 不過依然有辦法可以獲取,因為數據已經表現在img的src里了 , 對于截圖粘貼的,直接取img的src屬性值(base64),對于網頁粘貼的 , 則把地址傳給后臺,然后根據地址down下來,存在自己的服務器,最后把新地址返回來交給前端展示就ok了 。為了保持一致性便于管理,統一將所有情況(截圖、網頁)中的img的src屬性替換為自己存儲的地址 。因此可以得到以下核心代碼(注釋很全哦~~):
html展示:
headmeta charset="UTF-8"titleDocument/titlestylebody {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.originalEventvar clipboardData = https://www.questions.com.cn/dnjc/(event.clipboardData || event.originalEvent.clipboardData);if ( clipboardData.items ) {// for chromevaritems = 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; ilen; 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 firefoxsetTimeout(function () {//設置setTimeout的原因是為了保證圖片先插入到div里,然后去獲取值var imgList = document.querySelectorAll(’#tar_box img’),len = imgList.length,src_str = ’’,i;for ( i = 0; ilen; 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 ie11setTimeout(function () {var imgList = document.querySelectorAll(’#tar_box img’),len = imgList.length,src_str = ’’,i;for ( i = 0; ilen; 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 = https://www.questions.com.cn/dnjc/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; ilen; 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)

以上就是本文的全部內容,希望對大家的學習有所幫助 。
您可能感興趣的文章:js兼容火狐顯示上傳圖片預覽效果的方法上傳圖片js判斷圖片尺寸和格式兼容IEjavascript上傳圖片前預覽圖片兼容大多數瀏覽器javascript 客戶端驗證上傳圖片的大?。嬡軮E和火狐)JS上傳圖片預覽插件制作(兼容到IE6)

相關經驗推薦