Skip to content

Instantly share code, notes, and snippets.

@alediaferia
Last active November 27, 2022 01:36
Show Gist options
  • Star 43 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save alediaferia/cfb3a7503039f9278381 to your computer and use it in GitHub Desktop.
Save alediaferia/cfb3a7503039f9278381 to your computer and use it in GitHub Desktop.
A tiny snippet for reading files chunk by chunk in plain JavaScript
/*
Copyright (c) 2015-2020 Alessandro Diaferia
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
* Valid options are:
* - chunk_read_callback: a function that accepts the read chunk
as its only argument. If binary option
is set to true, this function will receive
an instance of ArrayBuffer, otherwise a String
* - error_callback: an optional function that accepts an object of type
FileReader.error
* - success: an optional function invoked as soon as the whole file has been
read successfully
* - binary: If true chunks will be read through FileReader.readAsArrayBuffer
* otherwise as FileReader.readAsText. Default is false.
* - chunk_size: The chunk size to be used, in bytes. Default is 64K.
*/
function parseFile(file, options) {
var opts = typeof options === 'undefined' ? {} : options;
var fileSize = file.size;
var chunkSize = typeof opts['chunk_size'] === 'undefined' ? 64 * 1024 : parseInt(opts['chunk_size']); // bytes
var binary = typeof opts['binary'] === 'undefined' ? false : opts['binary'] == true;
var offset = 0;
var self = this; // we need a reference to the current object
var readBlock = null;
var chunkReadCallback = typeof opts['chunk_read_callback'] === 'function' ? opts['chunk_read_callback'] : function() {};
var chunkErrorCallback = typeof opts['error_callback'] === 'function' ? opts['error_callback'] : function() {};
var success = typeof opts['success'] === 'function' ? opts['success'] : function() {};
var onLoadHandler = function(evt) {
if (evt.target.error == null) {
offset += evt.target.result.length;
chunkReadCallback(evt.target.result);
} else {
chunkErrorCallback(evt.target.error);
return;
}
if (offset >= fileSize) {
success(file);
return;
}
readBlock(offset, chunkSize, file);
}
readBlock = function(_offset, length, _file) {
var r = new FileReader();
var blob = _file.slice(_offset, length + _offset);
r.onload = onLoadHandler;
if (binary) {
r.readAsArrayBuffer(blob);
} else {
r.readAsText(blob);
}
}
readBlock(offset, chunkSize, file);
}
@bevancollins
Copy link

I believe that readAsBinaryString should be used instead of readAsArrayBuffer

@petercj1
Copy link

petercj1 commented Aug 6, 2016

@bevancollins - readAsBinaryString is non-standard. See https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsBinaryString:

This method has been removed from the FileAPI standard. FileReader.readAsArrayBuffer() should be used instead.

@fresheneesz
Copy link

fresheneesz commented Aug 17, 2016

chunk_read_callback, error_callback, chunk_size: camelCase please. Great solution tho! Didn't realize files could be sliced

@fresheneesz
Copy link

fresheneesz commented Aug 17, 2016

Here's a simpler version btw:

var EventEmitter = require("events").EventEmitter

// file - The file to read from
// options - Possible options:
    // type - (Default: "Text") Can be "Text" or "ArrayBuffer" ("DataURL" is unsupported at the moment - dunno how to concatenate dataUrls
    // chunkSize - (Default: 64K) The number of bytes to get chunks of
// Returns an EventEmitter that emits the following events:
    // data(data) - Returns a chunk of data
    // error(error) - Returns an error. If this event happens, reading of the file stops (no end event will happen).
    // end() - Indicates that the file is done and there's no more data to read
// derivedfrom here http://stackoverflow.com/questions/14438187/javascript-filereader-parsing-long-file-in-chunks
function read(file, options) {
    var emitter = new EventEmitter()

    if(options === undefined) options = {}
    if(options.type === undefined) options.type = "Text"
    if(options.chunkSize === undefined) options.chunkSize = 64000

    var offset = 0, method = 'readAs'+options.type//, dataUrlPreambleLength = "data:;base64,".length

    var onLoadHandler = function(evt) {
        if(evt.target.error !== null) {
            emitter.emit('error', evt.target.error)
            return;
        }

        var data = evt.target.result

        offset += options.chunkSize
        emitter.emit('data', data)
        if (offset >= file.size) {
            emitter.emit('end')
        } else {
            readChunk(offset, options.chunkSize, file)
        }
    }

    var readChunk = function(_offset, length, _file) {
        var r = new FileReader()
        var blob = _file.slice(_offset, length + _offset)
        r.onload = onLoadHandler
        r[method](blob)
    }

    readChunk(offset, options.chunkSize, file)

    return emitter
}

@doonfrs
Copy link

doonfrs commented Jul 7, 2017

Hi,
in chrome (at least)
evt.target.result.length for binary chunks return undefined, the line 29 should be:
offset += binary ?evt.target.result.byteLength: evt.target.result.length;
otherwise you will get the first chunk in binary mode, the rest will be empty.

@salazarr-js
Copy link

you really save my live ❤️ works like a charm, I use a URL.createObjectURL to preview the file (.mp4)

@eldoy
Copy link

eldoy commented May 31, 2018

What's the best way of determining if the file is binary or text?

@FullstackJack
Copy link

FullstackJack commented Mar 5, 2019

I love it! This is a great starting point for my Google Cloud Storage resumable file upload.

@RehabHussein
Copy link

@salazarr-js can u please elaborate, how did you do that ?

@helloimbernardo
Copy link

@alediaferia I would like to use this code in a project and I am wondering what is the license for it. Thanks in advance.

@alediaferia
Copy link
Author

@bernawastaken hey! feel free to use it, I added a MIT license at the top of the file.

@helloimbernardo
Copy link

Thanks ! @alediaferia

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment