I'm running into an odd problem using FileReader.readAsArrayBuffer
that only seems to affect Firefox (I tested in the current version - v40). I can't tell if I'm just doing something wrong or if this is a Firefox bug.
I have some JavaScript that uses readAsArrayBuffer
to read a file specified in an <input>
field. Under normal circumstances, everything works correctly. However, if the user modifies the file after selecting it in the <input>
field, readAsArrayBuffer
can get very confused.
The ArrayBuffer
I get back from readAsArrayBuffer
always has the length that the file was originally. If the user changes the file to make it larger, I don't get any of the bytes after the original size. If the user changes the file to make it smaller, the buffer is still the same size and the 'excess' in the buffer is filled with character codes 90 (capital letter 'Z' if viewed as a string).
Since this code is so simple and works perfectly in every other browser I tested, I'm thinking it's a Firefox issue. I've reported it as a bug to Firefox but I want to make sure this isn't just something obvious I'm doing wrong.
The behavior can be reproduced by the following code snippet. All you have to do is:
function ReadFile() { var input = document.getElementsByTagName("input")[0]; var output = document.getElementsByTagName("textarea")[0]; if (input.files.length === 0) { output.value = 'No file selected'; window.setTimeout(ReadFile, 1000); return; } var fr = new FileReader(); fr.onload = function() { var data = fr.result; var array = new Int8Array(data); output.value = JSON.stringify(array, null, ' '); window.setTimeout(ReadFile, 1000); }; fr.readAsArrayBuffer(input.files[0]); //These two methods work correctly //fr.readAsText(input.files[0]); //fr.readAsBinaryString(input.files[0]); } ReadFile();
<input type="file" /> <br/> <textarea cols="80" rows="10"></textarea>
In case the snippet does not work, the sample code is also available as a JSFiddle here: https://jsfiddle.net/Lv5y9m2u/
Returns partial Blob data representing the number of bytes currently loaded (as a fraction of the total), as an ArrayBuffer object, a fixed-length binary data buffer.
FileReader can only access the contents of files that the user has explicitly selected, either using an HTML <input type="file"> element or by drag and drop. It cannot be used to read a file by pathname from the user's file system. To read files on the client's file system by pathname, use the File System Access API.
readAsArrayBuffer() The FileReader interface's readAsArrayBuffer() method is used to start reading the contents of a specified Blob or File . When the read operation is finished, the readyState becomes DONE , and the loadend is triggered.
Interesting, looks like Firefox is caching the buffer size even the file is modified.
You can refer to this link, replaced readAsArrayBuffer
with is custom functionality which uses readAsBinaryString
. Its working fine in Firefox and Chrome
function ReadFile() { var input = document.getElementsByTagName("input")[0]; var output = document.getElementsByTagName("textarea")[0]; if (input.files.length === 0) { output.value = 'No file selected'; window.setTimeout(ReadFile, 1000); return; } var fr = new FileReader(); fr.onload = function () { var data = fr.result; var array = new Int8Array(data); output.value = JSON.stringify(array, null, ' '); window.setTimeout(ReadFile, 1000); }; fr.readAsArrayBuffer(input.files[0]); //These two methods work correctly //fr.readAsText(input.files[0]); //fr.readAsBinaryString(input.files[0]); } if (FileReader.prototype.readAsArrayBuffer && FileReader.prototype.readAsBinaryString) { FileReader.prototype.readAsArrayBuffer = function readAsArrayBuffer () { this.readAsBinaryString.apply(this, arguments); this.__defineGetter__('resultString', this.__lookupGetter__('result')); this.__defineGetter__('result', function () { var string = this.resultString; var result = new Uint8Array(string.length); for (var i = 0; i < string.length; i++) { result[i] = string.charCodeAt(i); } return result.buffer; }); }; } ReadFile();
I think you are hitting a bug of Firefox. However, as you pointed out, readAsArrayBuffer
behaves correctly in every supported browser except Firefox while readAsBinaryString
is supported by every browser except IE.
Therefore, it is possible to prefer readAsBinaryString
when it exists and fail back to readAsArrayBuffer
otherwise.
function readFileAsArrayBuffer(file, success, error) { var fr = new FileReader(); fr.addEventListener('error', error, false); if (fr.readAsBinaryString) { fr.addEventListener('load', function () { var string = this.resultString != null ? this.resultString : this.result; var result = new Uint8Array(string.length); for (var i = 0; i < string.length; i++) { result[i] = string.charCodeAt(i); } success(result.buffer); }, false); return fr.readAsBinaryString(file); } else { fr.addEventListener('load', function () { success(this.result); }, false); return fr.readAsArrayBuffer(file); } }
Usage:
readFileAsArrayBuffer(input.files[0], function(data) { var array = new Int8Array(data); output.value = JSON.stringify(array, null, ' '); window.setTimeout(ReadFile, 1000); }, function (e) { console.error(e); });
Working fiddle: https://jsfiddle.net/Lv5y9m2u/6/
Browser Support:
readAsBinaryString
, which is not problematic.readAsArrayBuffer
which is supported.FileReader
API is not supported.readAsBinaryString
.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With