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

jopen 8年前發布 | 43K 次閱讀 JavaScript開發 JavaScript

在用戶執行粘貼操作的時候, js 能夠獲得剪切板的內容,本文討論一下這個問題。

目前只有 Chrome 支持獲取剪切板中的圖片數據。還好需要這個功能的產品目前只支持 Chrome 和 Safari ,一些 Chrome 的新特性是可以盡情使用了,還是能夠覆蓋到大部分用戶的。所以本文只討論 Chrome 如何使用和如何阻止 Safari ,原理大概了解了,再研究其他瀏覽器相關的問題就容易多了。

paste 事件

可以用 js 給頁面中的元素綁定 paste 事件的方法,當用戶鼠標在該元素上或者該元素處于 focus 狀態,綁定到 paste 事件的方法就運行了。

綁定的元素不一定是 input ,普通的 div 也是可以綁定的,如果是給 document 綁定了,就相當于全局了,任何時候的粘貼操作都會觸發。

事件對象

獲取事件對象

先寫一下事件綁定的代碼

pasteEle.addEventListener("paste", function (e){
    if ( !(e.clipboardData && e.clipboardData.items) ) {
        return ;
    }
});

粘貼事件提供了一個 clipboardData 的屬性,如果該屬性有 items 屬性,那么就可以查看 items 中是否有圖片類型的數據了。 Chrome 有該屬性, Safari 沒有。

clipboardData 介紹

介紹一下 clipboardData 對象,它實際上是一個 DataTransfer 類型的對象, DataTransfer 是拖動產生的一個對象,但實際上粘貼事件也是它。

clipboardData 的屬性介紹

屬性 類型 說明
dropEffect String 默認是 none
effectAllowed String 默認是 uninitialized
files FileList 粘貼操作為空List
items DataTransferItemList 剪切板中的各項數據
types Array 剪切板中的數據類型 該屬性在Safari下比較混亂

items 介紹

items 是一個 DataTransferItemList 對象,自然里面都是 DataTransferItem 類型的數據了。

屬性

items 的 DataTransferItem 有兩個屬性 kind 和 type

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

方法

方法 參數 說明
getAsFile 如果 kind 是 file ,可以用該方法獲取到文件
getAsString 回調函數 如果 kind 是 string ,可以用該方法獲取到字符串,字符串需要用回調函數得到,回調函數的第一個參數就是剪切板中的字符串

在原型上還有一些其他方法,不過在處理剪切板操作的時候一般用不到了。

types 介紹

一般 types 中常見的值有 text/plain 、 text/html 、 Files 。

說明
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/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);

來自: http://segmentfault.com/a/1190000004288686

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!