部落格 RSS 訂閱

轉換不再使用 cordova-plugin-file-transfer
作者:Fil Maj
2017 年 10 月 18 日

在 Cordova 剛開始發展時,為了解決下載二進位檔案的問題,建立了 file-transfer 外掛程式。當時,沒有很好的選項可以使用符合標準的 Web API 來解決這個問題。Web 技術歷經曲折才找到解決方案(請參閱 Firefox 的 sendAsBinary 和現已停用的 FileSystem APIBlobBuilder 等等),但是現在您可以使用我們的好朋友 XMLHttpRequest 的最新功能,結合一些較新的 JavaScript 類型和物件,來解決這個問題。對於 Cordova 來說,這是一個令人興奮的時刻,因為這個專案的夢想一直是最終減少專案維護的 API 範圍,並看到常規的 Web API 能夠處理這些用例。

因此,Cordova 正在逐步淘汰 file-transfer 外掛程式。「逐步淘汰」是什麼意思?總結來說:

  • Cordova 開發社群將不再對 file-transfer 外掛程式進行任何工作。
  • 如果您願意,可以繼續使用 file-transfer 外掛程式 - 在可預見的未來,它應該可以正常運作。
  • 我們強烈建議 Cordova 使用者轉換為使用符合標準的方式來發送和接收二進位資料

不過,Apache Cordova 的所有成員都不想讓各位感到無助,所以我們認為最好向您展示如何使用這些較新的 XHR 功能來執行 file-transfer 讓您執行的操作,但它可以在任何現代 Web 瀏覽器中運作!

需求

根據您與底層裝置檔案系統的互動深度,以及在哪些平台上,您可能仍然需要依賴 Cordova File 外掛程式。如果您的應用程式的 JavaScript 中仍然有 requestFileSystemroot.fs 的參考,您肯定需要 File 外掛程式,因為這些不是符合標準的 API。請注意並小心!

平台支援

JavaScript 中的二進位類型以及擴展的 XHR 功能,在以下 Cordova 支援的平台上提供,無需任何其他外掛程式:

  • Android 4.4 或更新版本。
  • iOS 10 或更新版本。
  • Windows UWP (8.1、10 或更新版本皆可運作)。
  • Windows Phone 8 或更新版本。

一如既往,請查看 caniuse.com 以取得所需的詳細支援,例如 BlobTyped Arrays擴展的 XHR 功能

TL;DR

標準很好,但實際上您必須複製貼上哪些內容才能取代先前的 FileTransfer 範例?我們已經為您準備好了

以下是 FileTransfer 的「將二進位檔案下載到應用程式快取」範例的替代方案

window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (fs) {
    console.log('file system open: ' + fs.name);
    fs.root.getFile('bot.png', { create: true, exclusive: false }, function (fileEntry) {
        console.log('fileEntry is file? ' + fileEntry.isFile.toString());
        var oReq = new XMLHttpRequest();
        // Make sure you add the domain name to the Content-Security-Policy <meta> element.
        oReq.open("GET", "https://cordova.dev.org.tw/static/img/cordova_bot.png", true);
        // Define how you want the XHR data to come back
        oReq.responseType = "blob";
        oReq.onload = function (oEvent) {
            var blob = oReq.response; // Note: not oReq.responseText
            if (blob) {
                // Create a URL based on the blob, and set an <img> tag's src to it.
                var url = window.URL.createObjectURL(blob);
                document.getElementById('bot-img').src = url;
                // Or read the data with a FileReader
                var reader = new FileReader();
                reader.addEventListener("loadend", function() {
                   // reader.result contains the contents of blob as text
                });
                reader.readAsText(blob);
            } else console.error('we didnt get an XHR response!');
        };
        oReq.send(null);
    }, function (err) { console.error('error getting file! ' + err); });
}, function (err) { console.error('error getting persistent fs! ' + err); });

以下是 FileTransfer 的「上傳檔案」範例的類似替代方案

window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (fs) {
    console.log('file system open: ' + fs.name);
    fs.root.getFile('bot.png', { create: true, exclusive: false }, function (fileEntry) {
        fileEntry.file(function (file) {
            var reader = new FileReader();
            reader.onloadend = function() {
                // Create a blob based on the FileReader "result", which we asked to be retrieved as an ArrayBuffer
                var blob = new Blob([new Uint8Array(this.result)], { type: "image/png" });
                var oReq = new XMLHttpRequest();
                oReq.open("POST", "http://mysweeturl.com/upload_handler", true);
                oReq.onload = function (oEvent) {
                    // all done!
                };
                // Pass the blob in to XHR's send method
                oReq.send(blob);
            };
            // Read the file as an ArrayBuffer
            reader.readAsArrayBuffer(file);
        }, function (err) { console.error('error getting fileentry file!' + err); });
    }, function (err) { console.error('error getting file! ' + err); });
}, function (err) { console.error('error getting persistent fs! ' + err); });

請注意,以上兩個範例都依賴 File 外掛程式,因此如果您從應用程式中移除 FileTransfer 外掛程式,請務必新增 File 外掛程式!

長版本

如果您想了解一些啟用二進位資料傳輸的細節,您需要掌握兩個(可能三個)概念。MDN 有一篇關於此主題的精彩文章,值得快速閱讀,但我也會在此提供摘要。

JavaScript 中的二進位類型

長期以來,沒有辦法直接表示二進位資料並在 JavaScript 中存取記憶體中的底層位元組。我們可以將此資料編碼為不同的格式(base64,有人要用嗎?),這很酷,但還是讓我直接處理位元組吧。就我們的目的而言,我們特別關注兩個物件:ArrayBufferBlob。為什麼我們關心這兩個物件?因為我們可以讓 XHR 以這些類型傳回下載的資料,或將這些類型直接傳遞給 XHR 的 send 方法。

XHR

有兩個較新的 XHR 功能,最初是其開發過程中所謂的「XHR2」的一部分,我們需要利用它們將所有這些功能整合在一起。

對於下載二進位資料,我們需要將 responseType 屬性設定為 arraybufferblob - 這會告訴 XHR 我們想要傳回的資料類型。設定了 responseType 後,我們就可以存取唯讀的 response 屬性,以取得代表 XHR 擷取之資料的 ArrayBufferBlob 物件。

對於上傳二進位資料,則更簡單:將 BlobArrayBuffer 直接傳遞給 XHR 的 send 方法。就這樣。

摘要

二進位類型和擴展的 XHR 功能在現代桌面瀏覽器和較新的行動瀏覽器(以及 WebViews)中都獲得良好支援。對於現有的 Cordova 使用者,只要您的應用程式以「平台支援」下列出的平台和作業系統版本組合為目標,您就可以順利使用!請記住,如果您依賴某些 File 外掛程式 API(例如 requestFileSystemrootgetFile),則需要確保將 File 外掛程式新增至您的應用程式。

祝您編碼愉快,符合標準!