javascripthtmlblobfilewriterarraybuffer

Which ArrayBufferView


I'm retrieving an ArrayBuffer over XHR and want to save it to the FileSystem API using a FileWriter. The FileWriter expects a Blob and the Blob constructor won't take an ArrayBuffer, it takes an ArrayBufferView.

There are many ArrayBufferViews to choose from, how do I know which is the correct to use?


Solution

  • At MDN, you can find an overview of all available ArrayBufferView subclasses:

    Type Bytes Description Equivalent C type
    Int8Array 1 8-bit twos complement signed integer int8_t
    Uint8Array 1 8-bit unsigned integer uint8_t
    Uint8ClampedArray 1 8-bit unsigned integer (clamped) uint8_t
    Int16Array 2 16-bit twos complement signed integer int16_t
    Uint16Array 2 16-bit unsigned integer uint16_t
    Int32Array 4 32-bit twos complement signed integer int32_t
    Uint32Array 4 32-bit unsigned integer uint32_t
    Float32Array 4 32-bit IEEE floating point number float
    Float64Array 8 64-bit IEEE floating point number double
    BigInt64Array 8 64-bit two's complement signed integer int64_t
    BigUint64Array 8 64-bit unsigned integer uint64_t

    Basically, this lists what memory spaces each item of the array would occupy and if it's a plain number or a FP number. I'm not sure what languages you're familiar with, but if it also covers Java, then it is basically the same choice as you'd make on byte[], short[], int[], float[] and double[] (Java is always signed, so that part doesn't matter).

    We know, binary data like images is usually represented as a byte array. A short/int/long array can also, but this is a waste of memory space. If you would store a 100KB image (note that "B" stands for byte, which is 8 bits) in an int array instead of a byte array, then it would occupy 400KB of memory, which is a waste of 300KB. So the smallest one, Int8Array, would already suffice with regard to memory space. If you'd ever like to programmatically traverse it — which is very unlikely in this case — as an unsigned array, then you may opt for the Uint8Array instead where each item holds values 0 to 255 instead of values -128 to 127.

    Here's a copy'n'paste'n'runnable kickoff example which downloads an image file from the server, saves it to the temporary local storage space and presents it as an <img> in the body (little jQuery is required for that part). This example assumes that the image.png file is located in the same base folder as the JS (or HTML, in case JS is inlined) file is been downloaded from:

    window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
    window.requestFileSystem(window.TEMPORARY, 1024*1024, startDownload, errorHandler);
    
    var errorHandler = function(error) {
        console.log('FS error: ' + error);
    }
    
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'image.png', true);
    xhr.responseType = 'arraybuffer';
    
    function startDownload(fileSystem) {
        xhr.onload = function(event) {
            var content = new Int8Array(this.response);
            fileSystem.root.getFile('/image.png', { 'create': true }, function(fileEntry) {
                fileEntry.createWriter(function(fileWriter) {
                    fileWriter.onwriteend = function(event) {
                        $('body').append('<p>Image successfully downloaded:</p>')
                                 .append('<p><img src="' + fileEntry.toURL() + '"/></p>');
                    };
    
                    var blob = new Blob([content], { type: 'image/png' });
                    fileWriter.write(blob);
                }, errorHandler);
            });
        }
    
        xhr.send();
    }
    

    Note, as of the current state, this works in Chrome, Edge and Opera only and not (yet?) in Safari nor Firefox.