Access element from JavaScript FileReader - javascript

I am trying to read a file with in JavaScript with the File APIs.
I have the following code
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
var fileBlobs = [];
var reader = new FileReader();
for (var i = 0, f; f = files[i]; i++) {
fileBlobs.push(reader.readAsBinaryString(f));
console.log(reader)
}
}
It logs me this object
FileReader {onloadend: null, onerror: null, onabort: null, onload: null, onprogress: null…}
error: null
onabort: null
onerror: null
onload: null
onloadend: null
onloadstart: null
onprogress: null
readyState: 2
result: "lat, lng, popup↵13.47262306516617336, 52.47896324136591062, 55↵13.40861762468653673, 52.54336741663770027, 44↵13.29255442595013115, 52.51117712705399754, 33↵13.38642907198692988, 52.44880630287082113, 22"
__proto__: FileReader
How can I access only the result part?

NOT tested, but i think it should work :)
var file = document.getElementById('element-id').files;
var fr = new FileReader();
var storeBlobs = [];
fr.onload = function(e) {
storeBlobs.push(e.target.result);
};
for(var i = 0, l = file.length; i < l; i++) {
var content = fr.readAsText(file[i]);
}
File returns an array of objects which can be then manipulated trough FileReader object with onload method. If user loads more than one file we want to process all of it, and we will do that with for loop. I emphasize that this code is not tested, but it should work.
We can, if we assume that user will load one file only make file object to behave like this with replacing first line of code with this:
var file = document.getElementById('element-id').files[0];

You need to handle the load event of the FileReader. In the handler, access reader.result or this.result:
reader.onload = function() {
var contents = this.result;
};
Reading the files is an asynchronous operation. So, you need to create a separate FileReader for each file. If you want to reuse the same FileReader, you can't use a for loop. Instead, you must wait to read the next file until after the current file is processed.

Related

Wait until all files are read asynchronously (FileReader) and then run code

I have a page where the user can select a folder to upload files. Before sending the files, I need to read them and check the data. My code is organized as follows:
$( '#folder-select' ).on('change', getValidFileList);
var fileList = [];
var getValidFileList = function(event) {
//Get the selected files
files = $( this ).get(0).files;
for(var i=0; i<files.length; i++) {
checkFile(files[i]);
}
//Do something with the final fileList
console.log(fileList);
};
var checkFile = function(file) {
var reader = new FileReader();
reader.onload = function (event) {
//Here I parse and check the data and if valid append it to fileList
};
reader.readAsArrayBuffer(file);
};
I would like to take the resulting fileList array to keep processing/displaying the uploaded files. I found that reader.onload() is called asynchronously, so the result of the console.log(fileList) after the for loop is an empty array (it is executed before the reader.onload() is fired). Is there any way to wait until all files are read and appended to fileList?
Just keep track of how many files has been processed compared to how many files has been given:
function getValidFileList(files, callback) {
var count = files.length; // total number of files
var fileList = []; // accepted files
//Get the selected files
for(var i = 0; i < count; i++) { // invoke readers
checkFile(files[i]);
}
function checkFile(file) {
var reader = new FileReader();
reader.onload = function(event) {
var arrayBuffer = this.result;
//Here I parse and check the data and if valid append it to fileList
fileList.push(arrayBuffer); // or the original `file` blob..
if (!--count) callback(fileList); // when done, invoke callback
};
reader.readAsArrayBuffer(file);
}
};
The --count will subtract one per reader onload hit. When =0 (or !count) it invokes the callback. Notice that the array order may not be the same as the one from files[n] it this should matter.
Then invoke it like this:
$( '#folder-select' ).on('change', function() {
getValidFileList(this.files, onDone)
});
function onDone(fileList) {
// continue from here
}

How to run code on last iteration of html5 read file method?

In this javascript/jquery code I attempt to read multiple files and store them in a dictionary.
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
var f, filename;
for (var i = 0; i<files.length; i++) {
f = files[i];
filename = escape(f.name);
if (filename.toLowerCase().endsWith(".csv")) {
var reader = new FileReader();
// Closure to capture the file information.
reader.onload = (function(e) {
var text = reader.result;
var arrays = $.csv.toArrays(text);
frequencies[filename] = arrays;
generateMenuFromData();
});
// Read in the image file as a data URL.
reader.readAsText(f);
}
}
}
I read only the .csv files. I want to run generateMenuFromData(); only on the last time the reader.onload function runs.
I can't find a good way to do this properly. Does anyone know how?
Thanks.
Increase a counter inside the event handler. If it is the same the length of the array, execute the function. A more structured approach would be to use promises, but in this simple case it would suffice:
function handleFileSelect(evt) {
var files = evt.target.files;
var f, filename, loaded = 0;
for (var i = 0; i<files.length; i++) {
f = files[i];
filename = escape(f.name);
if (filename.toLowerCase().endsWith(".csv")) {
var reader = new FileReader();
reader.onload = (function(filename, reader) {
return function(e) {
frequencies[filename] = $.csv.toArrays(reader.result);
loaded += 1; // increase counter
if (loaded === files.length) {
// execute function once all files are loaded
generateMenuFromData();
}
};
}(filename, reader)); // <-- new scope, "capture" variable values
reader.readAsText(f);
}
}
}
Now, your real problem might be that you are creating a closure inside the loop. That means when the load event handlers are called, filename and reader will refer to the values the variable had in the last iteration of the loop. All handlers share the same variables.
See also Javascript closure inside loops - simple practical example.

Examine contents of Javascript variable after some action

I have a function that is supposed to read from a file into a variable
I want to know the validity of the reads and was wondering if there was any way I could examine the contents of the variable after the upload action has been performed.
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
// Loop through the FileList
for (var i = 0, f; f = files[i]; i++) {
var reader = new FileReader();
parser=new DOMParser();
// Closure to capture the file information.
reader.onload = (function(theFile) {
return function(e) {
// Print the contents of the file
// var span = document.createElement('span');
xmlDoc=parser.parseFromString(e.target.result,"text/xml");
try{
DistributomeXML_Objects=xmlDoc.documentElement.childNodes;
}catch(error){
DistributomeXML_Objects=xmlDoc.childNodes;
}
//document.getElementById('list').insertBefore(span, null);
};
})(f);
// Read in the file
//reader.readAsDataText(f,UTF-8);
reader.readAsText(f);
}
//xmlDoc.getElementsByTagName("distributome").item(0).appendChild(node);
traverseXML(false, null, DistributomeXML_Objects, distributome.nodes, distributome.edges, distributome.references, distributomeNodes, referenceNodes);
}
I want to check if xmlDoc is valid. What would be a good way to do this without using print statements.
You can use the console and log variables content using the
console.log("my variable content",variable);
you can see it in the browser console using firebug or the native console of chrome or opera...

JS function returns null - help

I am trying to modify the example http://www.html5rocks.com/en/tutorials/file/dndfiles/#toc-reading-files to make function handleFileSelect(evt) return reader.result; I mean to make function return base64 for image or so.
I tried to write it with function but it returns null only :(
So my question is how to make the function return base64?
As for now I tried to write this snippet...
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
// Loop through the FileList and render image files as thumbnails.
for (var i = 0, f; f = files[i]; i++) {
// Only process image files.
if (!f.type.match('image.*')) {
continue;
}
var reader = new FileReader();
// Closure to capture the file information.
reader.onload = (function(theFile) {
return function(e) {
// Render thumbnail.
var span = document.createElement('span');
span.innerHTML = ['<img class="thumb" src="', e.target.result,
'" title="', theFile.name, '"/>'].join('');
document.getElementById('list').insertBefore(span, null);
};
})(f);
// Read in the image file as a data URL.
reader.readAsDataURL(f);
}
return reader.result;
}
All useful comments are appreciated :)
I don't know the FileReader object in detail, but it looks like it reads data from a URL asynchronously. That means that when your function returns reader.result, the FileReader object isn't done reading the file yet. That doesn't finish until the onload callback is called (or some other error condition occurs).
So, your function returns while the reading is still happening asynchronously. Thus the result has not yet been determined. The result would be available on side the onload callback or (I'm guessing), inside other callbacks that would signify error conditions (onabort, onerror, etc..).

HTML5 File API: get File object within FileReader callback

With the new File API in Javascript you can read files in Javascript to create dataURLs to show clientside pictures clientside. I'm wondering if you can reach the File object within the FileReader's onload callback.
I will illustrate this with an example:
var div = document.createElement('div');
div.ondrop = function(e) {
e.preventDefault();
e.stopPropagation();
var files = e.dataTransfer.files;
for ( var i=0; i<files.length; i++ ) {
var file = files[i]; // this is the file I want!!
var filereader = new FileReader();
filereader.onload = function(e) {
this; // the FileReader object
e.target; // the same FileReader object
this.result; // the dataURL, something like data:image/jpeg;base64,.....
var img = document.createElement('img');
img.src = this.result;
img.title = file.fileName; // This won't be working
document.appendChild(img);
}
}
return false;
}
What I could do - what I do right now - is wrap the contents of the for loop in a function and execute it to create a new scope and keep a file in that scope like so:
for ( var i=0; i<files.length; i++ ) {
var _file = files[i]; // this is the file I want!!
(function(file) {
// do FileReader stuff here
})(_file);
}
I was just wondering... Maybe I'm missing something. Is there a way to get the File object from within the onload function of the FileReader? Both this and e.target are the FileReader object and not the File. Is there something in this or e that is the File?? I can't find it :(
Thanks a bunch.
PS. A fiddle: http://jsfiddle.net/rudiedirkx/ZWMRd/1/
I already found a way. Maybe not better than the scope wrapper, but I think it's neater:
for ( var i=0; i<files.length; i++ ) {
var file = files[i]; // this is the file I want!!
var filereader = new FileReader();
filereader.file = file;
filereader.onload = function(e) {
var file = this.file; // there it is!
// do stuff
}
}
There is now a much easier (apparently faster) way (sync!) to get a file's data URL:
img.src = URL.createObjectURL(file);
Demo on http://jsfiddle.net/rudiedirkx/ZWMRd/8/show/ of both methods, and the original problem illustrated (drag multiple images and check the title tooltips).
I don't think this.file is still supported. When I try to run the answer code, this.file is undefined whereas if I run the code from the question I get the expected results. I think you have to use the closure (at least this is how they do it on html5rocks (Example)).

Categories

Resources