在用戶執行粘貼操做的時候,
js
可以得到剪切板的內容,本文討論一下這個問題。
目前只有Chrome
支持獲取剪切板中的圖片數據。還好須要這個功能的產品目前只支持Chrome
和Safari
,一些Chrome
的新特性是能夠盡情使用了,仍是可以覆蓋到大部分用戶的。因此本文只討論Chrome
如何使用和如何阻止Safari
,原理大概瞭解了,再研究其餘瀏覽器相關的問題就容易多了。javascript
paste
事件能夠用js
給頁面中的元素綁定paste
事件的方法,當用戶鼠標在該元素上或者該元素處於focus
狀態,綁定到paste
事件的方法就運行了。html
綁定的元素不必定是input
,普通的div
也是能夠綁定的,若是是給document
綁定了,就至關於全局了,任什麼時候候的粘貼操做都會觸發。java
先寫一下事件綁定的代碼瀏覽器
pasteEle.addEventListener("paste", function (e){ if ( !(e.clipboardData && e.clipboardData.items) ) { return; } });
粘貼事件提供了一個clipboardData
的屬性,若是該屬性有items
屬性,那麼就能夠查看items
中是否有圖片類型的數據了。Chrome
有該屬性,Safari
沒有。函數
clipboardData
介紹介紹一下clipboardData
對象,它其實是一個DataTransfer
類型的對象,DataTransfer
是拖動產生的一個對象,但實際上粘貼事件也是它。測試
clipboardData
的屬性介紹spa
屬性 | 類型 | 說明 |
---|---|---|
dropEffect | String | 默認是 none |
effectAllowed | String | 默認是 uninitialized |
files | FileList | 粘貼操做爲空List |
items | DataTransferItemList | 剪切板中的各項數據 |
types | Array | 剪切板中的數據類型 該屬性在Safari下比較混亂 |
items
介紹items
是一個DataTransferItemList
對象,天然裏面都是DataTransferItem
類型的數據了。操作系統
items
的DataTransferItem
有兩個屬性kind
和type
code
屬性 | 說明 |
---|---|
kind | 通常爲string 或者file |
type | 具體的數據類型,例如具體是哪一種類型字符串或者哪一種類型的文件,即MIME-Type |
方法 | 參數 | 說明 |
---|---|---|
getAsFile | 空 | 若是kind 是file ,能夠用該方法獲取到文件 |
getAsString | 回調函數 | 若是kind 是string ,能夠用該方法獲取到字符串,字符串須要用回調函數獲得,回調函數的第一個參數就是剪切板中的字符串 |
在原型上還有一些其餘方法,不過在處理剪切板操做的時候通常用不到了。htm
types
介紹通常types
中常見的值有text/plain
、text/html
、Files
。
值 | 說明 |
---|---|
text/plain | 普通字符串 |
text/html | 帶有樣式的html |
Files | 文件(例如剪切板中的數據) |
pasteEle.addEventListener("paste", function (e){ if ( !(e.clipboardData && e.clipboardData.items) ) { return ; } for (var i = 0, len = e.clipboardData.items.length; i < len; i++) { var item = e.clipboardData.items[i]; if (item.kind === "string") { item.getAsString(function (str) { // str 是獲取到的字符串 }) } else if (item.kind === "file") { var pasteFile = item.getAsFile(); // pasteFile就是獲取到的文件 } } });
注意若是是string
類型的數據,可能針對具體是text/plain
、text/html
進行分別的處理。
一切看似都很順利,若是用戶粘貼了圖片,經過上面的方法咱們是能夠獲取到,能夠對圖片進行上傳等操做了。
首先要說一下js經過剪切板能獲取到的圖片是怎麼來的,它必須是用QQ截圖或者系統截圖功能截下來的圖片,或者是網頁上某個圖片單擊右鍵複製圖片等。
可是若是用戶複製Mac
的Finder
中的一個圖片文件,實際上js是沒有辦法獲取到這個圖片的。可是js確實會得到一個圖片類型的文件,這個圖片實際上圖片在電腦中的圖標標識,說的比較抽象,直接上圖。
若是複製的是JPEG
圖片,粘貼過來的倒是Mac
上的文件縮略圖,後面依次是PNG
、GIF
、ZIP
、DMG
、Mac目錄
的文件縮略圖。
很明顯,這不是咱們期待獲得的粘貼的結果,咱們期待獲得文件,但實際上卻獲得該文件在操做系統上的縮略圖。
不過粘貼事件帶來的數據還有一個字符串,就是該文件的名字,因此能夠用下面的方法Hack掉。
var cbd = e.clipboardData; if(cbd.items && cbd.items.length === 2 && cbd.items[0].kind === "string" && cbd.items[1].kind === "file" && cbd.types && cbd.types.length === 2 && cbd.types[0] === "text/plain" && cbd.types[1] === "Files"){ return; }
這麼多的判斷條件,基本能夠肯定經過剪切板過來的是粘貼的文件。我剛纔測試了Windows
的Chrome
,不會有這個問題,固然也不能經過複製文件的方法獲得任何文件。
當我打算寫這篇博客的時候,Chrome
開發版已經升級到了49,上面的Bug忽然消失了,囧。
因此上面的Hack應該加上版本限制了。
var ua = window.navigator.userAgent; ua.match(/Macintosh/i) && Number(ua.match(/Chrome\/(\d{2})/i)[1]) < 49
應該在上面的Hack再加上這兩個判斷,便是Mac
下的Chrome
49版本如下就要return
。
因爲公司IM系統正在遷移到V2消息系統,並且現有的文件類庫沒有辦法知足業務需求,要本身封裝一個文件上傳庫。
而後副總找到產品經理,說新版怎麼不支持Excel
的粘貼,臨時排期一天修復這個問題,當時是這樣解決的,若是items
長度是1而且是文件類型(單純粘貼一個文件),則上傳,若是items
長度是4且第4個是文件類型(通過測試是Excel的粘貼結果),則上傳。
當時擔憂因爲用戶各類誤操做,粘貼了不應粘貼的東西,文件上傳錯誤,用了這種白名單機制去過濾,可是萬一之後有比Excel
粘貼獲得的數據更其餘的類型,就須要單獨寫代碼兼容,因此,如今改爲了若是判斷是有Bug的狀況,直接return
,屬於黑名單機制,這樣之後再發現黑名單的狀況,再添加。
// demo 程序將粘貼事件綁定到 document 上 document.addEventListener("paste", function (e) { var cbd = e.clipboardData; var ua = window.navigator.userAgent; // 若是是 Safari 直接 return if ( !(e.clipboardData && e.clipboardData.items) ) { return; } // Mac平臺下Chrome49版本如下 複製Finder中的文件的Bug Hack掉 if(cbd.items && cbd.items.length === 2 && cbd.items[0].kind === "string" && cbd.items[1].kind === "file" && cbd.types && cbd.types.length === 2 && cbd.types[0] === "text/plain" && cbd.types[1] === "Files" && ua.match(/Macintosh/i) && Number(ua.match(/Chrome\/(\d{2})/i)[1]) < 49){ return; } for(var i = 0; i < cbd.items.length; i++) { var item = cbd.items[i]; if(item.kind == "file"){ var blob = item.getAsFile(); if (blob.size === 0) { return; } // blob 就是從剪切板得到的文件 能夠進行上傳或其餘操做 } } }, false);