--- layout: post author: name: Fil Maj url: https://twitter.com/filmaj title: "Transition off of cordova-plugin-file-transfer" categories: blog tags: plugins --- Early on in Cordova's existence, the [file-transfer plugin](https://github.com/apache/cordova-plugin-file-transfer) was created to solve the [problem of downloading binary files](https://issues.apache.org/jira/browse/CB-22). At the time, there weren't great options for solving this using standards-compliant web APIs. The web took a twisty path to get to a solution (see [Firefox's `sendAsBinary`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/sendAsBinary) and the now-defunct [FileSystem API](https://dev.w3.org/2009/dap/file-system/file-dir-sys.html)'s [BlobBuilder](https://developer.mozilla.org/en-US/docs/Web/API/BlobBuilder), among others), but today you can use our good friend [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)'s newest features, combined with some newer JavaScript types and objects, [to solve this problem](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data). This is an exciting moment for Cordova as the dream for this project was always to eventually reduce the surface area of APIs the project maintains, and instead see regular web APIs be able to handle these use cases. As a result, [Cordova is sunsetting the file-transfer plugin](https://issues.apache.org/jira/browse/CB-13052). What does "sunsetting" mean? In summary: * No more work will be done on the file-transfer plugin by the Cordova development community. * You can continue to use the file-transfer plugin if you wish - it should work fine as-is for the foreseeable future. * We highly suggest Cordova users transition to using the [standards-compliant way of sending and receiving binary data](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data). All of us at Apache Cordova don't want to leave y'all hanging, though, so we thought it'd be a good idea to show you how to use these newer XHR features to do what file-transfer lets you do, but in a way that will work in any modern web browser to boot! ## Requirements Based on how deeply you interact with the underlying device filesystem, and on which platforms, you may still need to rely on the [Cordova File plugin](https://github.com/apache/cordova-plugin-file). If you still have references to `requestFileSystem` or `root.fs` in your application's JavaScript, you will definitely need the File plugin because these are not standards-compliant APIs. Take note and care! ## Platform Support Binary types in JavaScript, as well as the extended XHR features, are available on the following Cordova-supported platforms without requiring any additional plugins: * Android 4.4 or newer. * iOS 10 or newer. * Windows UWP (8.1, 10 or newer all work). * Windows Phone 8 or newer. As always, check [caniuse.com](https://caniuse.com) for detailed support for the required bits, like [`Blob`](http://caniuse.com/#feat=blobbuilder), [Typed Arrays](http://caniuse.com/#feat=typedarrays), and [extended XHR features](http://caniuse.com/#feat=xhr2). ## TL;DR Standards are great and all, but what do you actually have to copy-paste to replace the previous FileTransfer examples? We have you covered: Here's a replacement for [FileTransfer's "Download a Binary File" example](https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-file-transfer/index.html#download-a-binary-file-to-the-application-cache): ```js 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 element. oReq.open("GET", "http://cordova.apache.org/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 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); }); ``` Here's a similar replacement for [FileTransfer's "Upload a File" example](https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-file-transfer/index.html#uploadFile): ```js 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); }); ``` Note that both the above examples rely on the File plugin, so if you remove the FileTransfer plugin from your app, make sure to add the File plugin! ## The Long Version If you want to understand some of the nuts and bolts enabling binary data transferring, you'll need to grasp two (possibly three) concepts. MDN has an absolutely fantastic [article](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data) on the topic that is worth a quick read, but I'll provide a summary here, too. ### Binary Types in JavaScript For the longest time, there was no way to directly represent binary data and access the underlying bytes in memory within JavaScript. We could encode this data in different formats ([base64](https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding), anyone?), and that was cool, but just let me play with the bytes already. For our purposes, we are interested in two objects in particular: [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays) and [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob). Why do we care about these two? Because we can have XHRs return downloaded data as these types, or pass these types directly to XHRs' `send` method. ### XHR There are two newer XHR features, originally as part of what was referred to as "XHR2" during its [development](https://www.w3.org/TR/XMLHttpRequest2/), that we need to leverage to tie this all together. For downloading binary data, we need to set the [`responseType`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType) property to either `arraybuffer` or `blob` - this tells XHR what type we want the data we are retrieving back in. With `responseType` set, we can then access the read-only `response` property to get either the `ArrayBuffer` or `Blob` object representing the data retrieved by XHR. For uploading binary data, it is simpler: pass a `Blob` or `ArrayBuffer` directly to XHR's [`send`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data#Sending_binary_data) method. That's it. ## Summary Binary types and extended XHR features are well supported in modern desktop browsers, and on recent-ish mobile browsers (and WebViews). For existing Cordova users, as long as your app targets the platform and OS version combinations listed above under Platform Support, you should be good to go! Remember that if you rely on certain File plugin APIs like `requestFileSystem`, `root`, or `getFile`, you'll need to ensure the File plugin is added to your app. Happy standards-compliant coding!