Cordova – file-transfer, unzip and present adventures

For a hybrid project I have a very simple requirement: download zip file, unzip it, serve content on a page from artifacts (html text, images).

I started with Intel XDK, an environment I did some other work with. A great integrated development environment, but some of its big problems: lagging behind in version of Cordova, fixed set of selected plugins, no possibility to include other plugins. Because I wanted to unzip, I tried it with a Javascript only solution zip.js. The problem I had was that it worked if I downloaded the zip file over http using importHttpContent(), but I could’t get it working with loading from the local file system. Another problem I had was with where files ended up on different devices: in / on wp8, on /storage/sdcard0 on Android, and on iOS in even another location.

I decided to go with plain Cordova, with the command-line commands. That was a good decision. It was now possible to use the latest version (3.4 at time of writing), and use the new file and file-transfer plugins that now support a huge improvement to hybrid development: the cdvfile protocol. Files can now be addressed with respect to one multi-platform root: cdvfile://, rather than through device-specific paths. There is also a great plugin available to unzip that understands the new cdvfile:// notation as well. I ended up with a simple example to showcase the download, unzip and present case. To get it working execute the following commands with a Cordova 3.4 installation:

  1. cordova create Cordova-TransferUnzipPresent com.svdoever.tranferunzippresent TransferUnzipPresent
  2. cd Cordova-TransferUnzipPresent
  3. cordova platform add android
  4. cordova plugin add org.apache.cordova.file
  5. cordova plugin add org.apache.cordova.file-transfer
  6. cordova plugin add org.chromium.zip
  7. Replace code in www\index.html with the code below
  8. cordova emulate android

Works with iOS platform as well. The zip plugin does not work on wp8 (yet).

The code:

<!-- Code by Serge van den Oever, http://weblogs.asp.net/soever -->
<!-- Using Cordova 3.4 with the following plugins: -->
<!-- 'org.apache.cordova.file' (version 1.0.1), 'org.apache.cordova.file-transfer' (0.4.2), 'org.chromium.zip' (2.0.0)  -->
<!DOCTYPE html>
<html>
<head>
    <title>Cordova Download Unzip Display Sample</title>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0">
    <script src="cordova.js"></script>
</head>

<body> <button id="btnLoad">Load</button> <button id="btnUnzip">Unzip</button> <hr/> <div id="statusPlace"></div> <hr/> <img id="imgPlace" src="http://lorempixel.com/320/200"> <br/> <div id="txtPlace">TEXT COMES HERE</div>

<script type="application/javascript"> document.addEventListener("deviceready", onDeviceReady, false);

function registerHandlers() {
    document.getElementById("btnLoad").onclick = function() {
        var that = this,
                App = new DownloadApp(),
                fileName = "ft-p.zip",
                uri = encodeURI("https://dl.dropboxusercontent.com/u/7197720/ftpack.zip"),
                folderName = "content";
        console.log("load button clicked");
        document.getElementById("statusPlace").innerHTML += "&lt;br/&gt;Loading: " + uri;
        App.load(uri, folderName, fileName,
                /*progress*/function(percentage) { document.getElementById("statusPlace").innerHTML += "&lt;br/&gt;" + percentage + "%"; },
                /*success*/function(entry) { document.getElementById("statusPlace").innerHTML += "&lt;br/&gt;Zip saved to: " + entry.toURL(); },
                /*fail*/function() { document.getElementById("statusPlace").innerHTML += "&lt;br/&gt;Failed load zip: " + that.uri; }
        );
    };
    document.getElementById("btnUnzip").onclick = function() {
        var that = this,
                App = new DownloadApp(),
                fileName = "ft-p.zip",
                folderName = "content";
        console.log("zip button clicked");
        App.unzip(folderName, fileName,
                /*success*/function() { alert("Unzipped and assigned"); },
                /*fail*/function(error) { alert("Unzip failed: " + error.code); }
        );
    };
}

function onDeviceReady() {
    // navigator.splashscreen.hide();
    document.getElementById("statusPlace").innerHTML += "&lt;br/&gt;deviceready event received";
    registerHandlers();
}

var DownloadApp = function() {
}

DownloadApp.prototype = {
    load: function(uri, folderName, fileName, progress, success, fail) {
        var that = this;
        that.progress = progress;
        that.success = success;
        that.fail = fail;
        filePath = "";

        that.getFilesystem(
                function(fileSystem) {
                    console.log("GotFS");
                    that.getFolder(fileSystem, folderName, function(folder) {
                        filePath = folder.toURL() + "/" + fileName;
                        that.transferFile(uri, filePath, progress, success, fail);
                    }, function(error) {
                        console.log("Failed to get folder: " + error.code);
                        typeof that.fail === 'function' &amp;&amp; that.fail(error);
                    });
                },
                function(error) {
                    console.log("Failed to get filesystem: " + error.code);
                    typeof that.fail === 'function' &amp;&amp; that.fail(error);
                }
        );
    },

    getFilesystem:function (success, fail) {
        window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
        window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, success, fail);
    },

    getFolder: function (fileSystem, folderName, success, fail) {
        fileSystem.root.getDirectory(folderName, {create: true, exclusive: false}, success, fail)
    },

    transferFile: function (uri, filePath, progress, success, fail) {
        var that = this;
        that.progress = progress;
        that.success = success;
        that.fail = fail;

        var transfer = new FileTransfer();
        transfer.onprogress = function(progressEvent) {
            if (progressEvent.lengthComputable) {
                var perc = Math.floor(progressEvent.loaded / progressEvent.total * 100);
                typeof that.progress === 'function' &amp;&amp; that.progress(perc); // progression on scale 0..100 (percentage) as number
            } else {
            }
        };

        transfer.download(
                uri,
                filePath,
                function(entry) {
                    console.log("File saved to: " + entry.toURL());
                    typeof that.success === 'function' &amp;&amp; that.success(entry);
                },
                function(error) {
                    console.log("An error has occurred: Code = " + error.code);
                    console.log("download error source " + error.source);
                    console.log("download error target " + error.target);
                    console.log("download error code " + error.code);
                    typeof that.fail === 'function' &amp;&amp; that.fail(error);
                }
        );
    },

    unzip: function(folderName, fileName, success, fail) {
        var that = this;
        that.success = success;
        that.fail = fail;

        zip.unzip("cdvfile://localhost/persistent/" + folderName + "/" + fileName,
                  "cdvfile://localhost/persistent/" + folderName,
                function(code) {
                    console.log("result: " + code);
                    that.getFilesystem(
                            function(fileSystem) {
                                console.log("gotFS");
                                that.getFolder(fileSystem, folderName + "/ftpack", function (folder) {
                                    document.getElementById("imgPlace").src = folder.nativeURL + "/img.jpg";
                                    folder.getFile("text.html", {create: false}, function (fileEntry) {
                                        fileEntry.file(function(file) {
                                            var reader = new FileReader();
                                            reader.onloadend = function (evt) {
                                                console.log("Read as text");
                                                console.log(evt.target.result);
                                                document.getElementById("txtPlace").innerHTML = evt.target.result;
                                                typeof that.success === ' function &amp;&amp; that.success();'
                                            };
                                            reader.readAsText(file);
                                        }, function(error) {
                                            console.log("Failed to get file");
                                            typeof that.fail === 'function' &amp;&amp; that.fail(error);
                                        });
                                    }, function (error) {
                                        console.log("failed to get file: " + error.code);
                                        typeof that.fail === 'function' &amp;&amp; that.fail(error);
                                    });
                                }, function (error) {
                                    console.log("failed to get folder: " + error.code);
                                    typeof that.fail === 'function' &amp;&amp; that.fail(error);
                                });
                            }, function(error) {
                                console.log("failed to get filesystem: " + error.code);
                                typeof that.fail === 'function' &amp;&amp; that.fail(error);
                            });
                }
        );
    }
}

</script> </body> </html>

No Comments