0

I've got an array of URLs [URL1, URL2, URL3,...] : each element is a link to one of the chunks of the same file. Each chunk is separately encrypted, with the same key as all the other chunks.

I download each chunk (in a forEach function) with a XMLHttpRequest. onload :

  1. each chunk is first decrypted
  2. then each chunk is converted to an ArrayBuffer (source)
  3. each ArrayBuffer is pushed to an array (source)
  4. when the three first steps are done for each chunk (callback by a var incremented on step#1 === the array.length), a blob is constructed with the array
  5. the blob is saved as file with FileReader API & filesaver.js

If it's a one chunk's file, everything works fine.

But with multiple chunks, steps #1 & #2 are ok, but only the last ArrayBuffer seems to be pushed to the array. What am I missing?

Below my code

// var for incrementation in forEach funtion
var chunkdownloaded = 0;
// 'clearfileurl' is the array of url's chunks :[URL1, URL2, URL3,...]
clearfileurl.forEach(function(entry) {
    var xhr = new XMLHttpRequest();
    var started_at = new Date();
    xhr.open('GET', entry, true);
    xhr.responseType = 'text';

    // request progress
    xhr.onprogress = function(pe) {
        if (pe.lengthComputable) {
            downloaderval.set((pe.loaded / pe.total) * 100);
        }
    };

    // on request's success
    xhr.onload = function(e) {
        if (this.status == 200) {

            chunkdownloaded+=1;
            var todecrypt = this.response;

            // decrypt request's response: get a dataURI
            try { 
                var bytesfile  = CryptoJS.AES.decrypt(todecrypt.toString(), userKey);
                var decryptedfile = bytesfile.toString(CryptoJS.enc.Utf8);
            } catch(err) {
                console.log (err);
                return false;
            }

            //convert a dataURI to a Blob
            var MyBlobBuilder = function() {
                this.parts = [];
            }

            MyBlobBuilder.prototype.append = function(dataURI) {
            //function dataURItoBlob(dataURI) {
                // convert base64 to raw binary data held in a string
                var byteString = atob(dataURI.split(',')[1]);

                // separate out the mime component
                // var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];

                // write the bytes of the string to an ArrayBuffer
                var ab = new ArrayBuffer(byteString.length);
                var ia = new Uint8Array(ab);
                for (var i = 0; i < byteString.length; i++) {
                    ia[i] = byteString.charCodeAt(i);
                }
                this.parts.push(ab);
                console.log('parts', this.parts)
                this.blob = undefined; // Invalidate the blob
            }

            MyBlobBuilder.prototype.getBlob = function() {
                if (!this.blob) {
                    console.log (this.parts);
                    this.blob = new Blob(this.parts);
                }
                return this.blob;
            };

            var myBlobBuilder = new MyBlobBuilder();
            myBlobBuilder.append(decryptedfile);

            // if all chunks are downloaded
            if (chunkdownloaded === clearfileurl.length) {
                // get the blob
                var FinalFile = myBlobBuilder.getBlob();

                // launch consturction of a file with'FinalFile' inside FileReader API
                var reader = new FileReader();
                reader.onload = function(e){
                    // build & save on client the final file with 'file-saver' library
                    var FileSaver = require('file-saver');
                    var file = new File([FinalFile], clearfilename, {type: clearfiletype});
                    FileSaver.saveAs(file); 
                };
                reader.readAsText(FinalFile);

            } else {
                console.log('not yet');
            }
        } 
    };
    // sending XMLHttpRequest
    xhr.send();
});
Community
  • 1
  • 1
Ontokrat
  • 189
  • 1
  • 14
  • 1
    Well you are creating one instance of MyBlobBuilder per xhr load events, so they'll all ultimately contain an single decryptedFile. Create only one instance of this object, in the common scope (i.e out of the onload handler) and only call its append method in the onload handler. (Note: I didn't read carefully the code, so there may be some other mistakes, but this one is the more obvious one). Hmm yes, nothing insures the order in which your decrypted files will be appended, so you'd be better appending an object in the forEach loop, and reconstruct your full array once every has been done. – Kaiido May 03 '17 at 06:40
  • @Kaiido You're right, 고맙습니다. Of course, I needed to create only one instance of MyBlobBuilder in the common scope. Can you help on the question of reconstruct the array of decrypted files in the right order (the one from the original URLs array: `[URL1, URL2, URL3,...]`) ? – Ontokrat May 03 '17 at 14:20

1 Answers1

1

You need to take out the declaration of MyBlobBuilder, try this:

// var for incrementation in forEach funtion
var chunkdownloaded = 0;

//convert a dataURI to a Blob
var MyBlobBuilder = function() {
    this.parts = [];
}

MyBlobBuilder.prototype.append = function(dataURI, index) {
//function dataURItoBlob(dataURI) {
    // convert base64 to raw binary data held in a string
    var byteString = atob(dataURI.split(',')[1]);

    // separate out the mime component
    // var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];

    // write the bytes of the string to an ArrayBuffer
    var ab = new ArrayBuffer(byteString.length);
    var ia = new Uint8Array(ab);
    for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }
    this.parts[index] = ab;
    console.log('parts', this.parts)
    this.blob = undefined; // Invalidate the blob
}

MyBlobBuilder.prototype.getBlob = function() {
    if (!this.blob) {
        console.log (this.parts);
        this.blob = new Blob(this.parts);
    }
    return this.blob;
};

var myBlobBuilder = new MyBlobBuilder();

// 'clearfileurl' is the array of url's chunks :[URL1, URL2, URL3,...]
clearfileurl.forEach(function(entry, index) {
    var xhr = new XMLHttpRequest();
    var started_at = new Date();
    xhr.open('GET', entry, true);
    xhr.responseType = 'text';

    // request progress
    xhr.onprogress = function(pe) {
        if (pe.lengthComputable) {
            downloaderval.set((pe.loaded / pe.total) * 100);
        }
    };

    // on request's success
    xhr.onload = function(e) {
        if (this.status == 200) {

            chunkdownloaded+=1;
            var todecrypt = this.response;

            // decrypt request's response: get a dataURI
            try { 
                var bytesfile  = CryptoJS.AES.decrypt(todecrypt.toString(), userKey);
                var decryptedfile = bytesfile.toString(CryptoJS.enc.Utf8);
            } catch(err) {
                console.log (err);
                return false;
            }


            myBlobBuilder.append(decryptedfile, index);

            // if all chunks are downloaded
            if (chunkdownloaded === clearfileurl.length) {
                // get the blob
                var FinalFile = myBlobBuilder.getBlob();

                // launch consturction of a file with'FinalFile' inside FileReader API
                var reader = new FileReader();
                reader.onload = function(e){
                    // build & save on client the final file with 'file-saver' library
                    var FileSaver = require('file-saver');
                    var file = new File([FinalFile], clearfilename, {type: clearfiletype});
                    FileSaver.saveAs(file); 
                };
                reader.readAsText(FinalFile);

            } else {
                console.log('not yet');
            }
        } 
    };
    // sending XMLHttpRequest
    xhr.send();
});

*edit I also updated the append function to ensure that the files are in the correct order

Joe Lissner
  • 2,181
  • 1
  • 15
  • 21
  • Your clean and concise answer works fine: a special thanks for the 'extra' to ensure that the files are in the correct order - very very much appreciated. Now I spotted a problem in my slice in chunks function (not all chunks seem to be written) - another problem... – Ontokrat May 03 '17 at 15:10