js獲取剪切板內容,js控制圖片粘貼。

在用戶執行粘貼操做的時候, js可以得到剪切板的內容,本文討論一下這個問題。

目前只有Chrome支持獲取剪切板中的圖片數據。還好須要這個功能的產品目前只支持ChromeSafari,一些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類型的數據了。操作系統

屬性

itemsDataTransferItem有兩個屬性kindtypecode

屬性 說明
kind 通常爲string或者file
type 具體的數據類型,例如具體是哪一種類型字符串或者哪一種類型的文件,即MIME-Type

方法

方法 參數 說明
getAsFile 若是kindfile,能夠用該方法獲取到文件
getAsString 回調函數 若是kindstring,能夠用該方法獲取到字符串,字符串須要用回調函數獲得,回調函數的第一個參數就是剪切板中的字符串

在原型上還有一些其餘方法,不過在處理剪切板操做的時候通常用不到了。htm

types介紹

通常types中常見的值有text/plaintext/htmlFiles

說明
text/plain 普通字符串
text/html 帶有樣式的html
Files 文件(例如剪切板中的數據)

簡單demo

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/plaintext/html進行分別的處理。

問題來了

一切看似都很順利,若是用戶粘貼了圖片,經過上面的方法咱們是能夠獲取到,能夠對圖片進行上傳等操做了。

首先要說一下js經過剪切板能獲取到的圖片是怎麼來的,它必須是用QQ截圖或者系統截圖功能截下來的圖片,或者是網頁上某個圖片單擊右鍵複製圖片等。

可是若是用戶複製MacFinder中的一個圖片文件,實際上js是沒有辦法獲取到這個圖片的。可是js確實會得到一個圖片類型的文件,這個圖片實際上圖片在電腦中的圖標標識,說的比較抽象,直接上圖。

圖片描述

若是複製的是JPEG圖片,粘貼過來的倒是Mac上的文件縮略圖,後面依次是PNGGIFZIPDMGMac目錄的文件縮略圖。

很明顯,這不是咱們期待獲得的粘貼的結果,咱們期待獲得文件,但實際上卻獲得該文件在操做系統上的縮略圖。

不過粘貼事件帶來的數據還有一個字符串,就是該文件的名字,因此能夠用下面的方法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;
    }

這麼多的判斷條件,基本能夠肯定經過剪切板過來的是粘貼的文件。我剛纔測試了WindowsChrome,不會有這個問題,固然也不能經過複製文件的方法獲得任何文件。

問題又來了

當我打算寫這篇博客的時候,Chrome開發版已經升級到了49,上面的Bug忽然消失了,囧。
因此上面的Hack應該加上版本限制了。

var ua = window.navigator.userAgent;
ua.match(/Macintosh/i) && Number(ua.match(/Chrome\/(\d{2})/i)[1]) < 49

應該在上面的Hack再加上這兩個判斷,便是Mac下的Chrome49版本如下就要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);
相關文章
相關標籤/搜索