Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merging multiple parts of a file in Cordova

Within my Cordova app, I am downloading arbitrary files like images or video files. This is done with the Cordova file-transfer plugin and the "Range" Header, because I need to download the files in parts.

My Problem is, that I want to merge back the several small "Byte"-Files back together into the original file they once where to use that file. Every time I'm trying to read the resulting parts as binaryString via the FileReader and write them together in a new file, that file ends up a lot larger than the parts of the original file altogther and the resulting file is unusable.

Any help is appreciated.

Here is my code until now (long and ugly):

document.addEventListener('deviceready', deviceready, false);

var App;

var finishedFileUrl = "";

var async = {
    sequence: function(items, callback) {       
        var def = $.Deferred(),
        deferrers = [$.Deferred()];

        for(var i = 0; i < items.length; i++) {
            (function (n) {     
                deferrers[n + 1] = $.Deferred();
                deferrers[n].always(function() {
                    callback(items[n], deferrers[n + 1]);
                });
            })(i);  
        }
        deferrers[items.length].always(function() {
            def.resolve();
        });         
        deferrers[0].resolve();

        return def.promise();
    }
}

var aSmallImageArray = [
'' // Put URL to JPG accessible with Range Header Request here
];

var aByteSizeImageArray = [];

function formatDownloadArray(fileSize) {
    for(var j = 1000; j <= fileSize; j += 1000) {
        aByteSizeImageArray.push(j);
    }
    aByteSizeImageArray.push(j);
}

function deviceready() {
    console.log('dv ready');

    function registerHandlers() {
        App = new DownloadApp();
        formatDownloadArray(XXXXX);     // XXXXX should be size of JPG in bytes
        document.getElementById("startDl").onclick = function() {
            var that = this;
            console.log("load button clicked");
            var folderName = "testimagefolder";

            // sequence call
            async.sequence(aByteSizeImageArray, function(currentBytes, iter) {
                var filePath = aSmallImageArray[0];
                var fileName = aSmallImageArray[0].substr(52,99) + currentBytes;
                console.log(filePath);
                console.log(fileName);
                console.log("Starting with: " + fileName);
                var uri = encodeURI(filePath);
                var folderName = "testimagefolder";
                document.getElementById("statusPlace").innerHTML = "<br/>Loading: " + uri;
                App.load(currentBytes, uri, folderName, fileName,
                    function progress (percentage) {
                         document.getElementById("statusPlace").innerHTML = "<br/>" + percentage + "%";
                    },
                    function success (entry) {
                        console.log("Entry: " + entry);
                        document.getElementById("statusPlace").innerHTML = "<br/>Image saved to: " + App.filedir;
                        console.log("DownloadApp.filedir: " + App.filedir);
                        iter.resolve();
                    },
                    function error () {
                        document.getElementById("statusPlace").innerHTML = "<br/>Failed load image: " + uri;
                        iter.resolve();
                    }
                );              
            }).then(function afterAsync () {
                console.log("ASYNC DONE");
                var ohNoItFailed = function ohNoItFailed (exeperro) {
                    console.log(exeperro);
                }
                // now we merge the fileparts into one file to show it
                window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (FileSystem) {
                    FileSystem.root.getDirectory(folderName, {create: true, exclusive: false}, function itSuccessed (Directory) {
                        Directory.getFile(aSmallImageArray[0].substr(52,99), {create: true, exclusive: false}, function itSuccessedAgain (fileEntry) {
                            finishedFileUrl = fileEntry.toURL();
                            var directoryReader = Directory.createReader();
                            var allFiles = directoryReader.readEntries(function succesReadDir (fileEntries) {
                                async.sequence(fileEntries, function(currentFile, iterThis) {
                                    currentFile.file(function (theActualFile) {
                                        var myFileReader = new FileReader();
                                        myFileReader.onload = function (content) {
                                            console.log('FileReader onload event fired!');
                                            console.log('File Content should be: ' + content.target.result);
                                            fileEntry.createWriter(
                                            function mergeImage (writer) {
                                                writer.onwrite = function (evnt) {
                                                    console.log("Writing successful!");
                                                    iterThis.resolve();
                                                }
                                                writer.seek(writer.length);
                                                writer.write(content.target.result);
                                            }, ohNoItFailed);
                                        };
                                        myFileReader.readAsBinaryString(theActualFile);
                                    }, ohNoItFailed);
                                }).then(function afterAsyncTwo () {
                                    console.log("NOW THE IMAGE SHOULD BE TAKEN FROM THIS PATH: " + finishedFileUrl);

                                    //window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (FileSystem) {
                                        //FileSystem.root.getDirectory(folderName, {create: true, exclusive: false}, function itSuccessed (Directory) {
                                            //Directory.getFile(aSmallImageArray[0].substr(52,99), {create: true, exclusive: false}, function     itSuccessedAgain (fileEntry) {    
                                                //fileEntry.createWriter(    

                                        document.getElementById("image_here").src = finishedFileUrl;    
                                });    
                            }, ohNoItFailed);                               
                        }, ohNoItFailed);    
                    }, ohNoItFailed);    
                }, ohNoItFailed);                       
            });    
        };    
    }    
    registerHandlers();    
}    

var DownloadApp = function() {}

DownloadApp.prototype = {
    filedir: "",
    load: function(currentBytes, 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;
                        console.log("FILEPATH: " + filePath);
                        console.log("URI: " + uri);
                        that.transferFile(currentBytes, uri, filePath, progress, success, fail);
                    }, function(error) {
                        console.log("Failed to get folder: " + error.code);
                        typeof that.fail === 'function' && that.fail(error);
                    });
                },
                function(error) {
                    console.log("Failed to get filesystem: " + error.code);
                    typeof that.fail === 'function' && 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 (currentBytes, uri, filePath, progress, success, fail) {
        var that = this;
        that.progress = progress;
        that.success = success;
        that.fail = fail;
        console.log("here we go");
        console.log("filePath before Request: " + filePath);

        var previousBytes = currentBytes - 1000;

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

        transfer.download(
            uri,
            filePath,
            function success (entry) {
                console.log("File saved to: " + entry.toURL());
                typeof that.success === 'function' && that.success(entry);
            },
            function errorProblem(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' && that.fail(error);
            },
            true,
            {
                headers: {
                    "Range": "bytes=" +  previousBytes + "-" + currentBytes
                }
            }
        );
    }
}   

async code by stackoverflow-user: Paul Facklam -> Thanks a lot!

like image 516
plocks Avatar asked Oct 15 '14 15:10

plocks


2 Answers

you can build a blob from other blobs, like the ones you use FileReader on now. (File()s are Blobs)

// put three blobs into a fourth:
var b=new Blob([new Blob(["hello"]), new Blob([" "]), new Blob(["world"])]);

// verify the blob has the data we expect:
var fr=new FileReader();
fr.onload=function(){alert(this.result);};
fr.readAsBinaryString(b);  // shows: "hello world"

the binaryString flavor is used here to show how these low-order strings stack up, but the actual new blob instance should have all the orig (arbitrary) bytes from the original blobs, even if they aren't composed of simple strings...

like image 54
dandavis Avatar answered Oct 26 '22 21:10

dandavis


Using readAsArrayBuffer() instead of readAsBinaryString() did the trick!

So instead of:

myFileReader.readAsBinaryString(theActualFile);

I did:

myFileReader.readAsBinaryArray(theActualFile);

And the resulting image file is usable.

like image 45
plocks Avatar answered Oct 26 '22 22:10

plocks