Javascript File Reader - javascript

I may just not be understanding the file reader api, but
When I try to run:
for (var i = 0, f1, f2; f1 = files[sorted_index[i]], f2 = files[sorted_index[i+1]]; i=i+2) {
var file_one;
var file_two;
if(f1.size < f2.size){
file_one = f1;
file_two = f2;
} else {
file_one = f2;
file_two = f1;
}
var file_one_contents;
var file_two_contents;
var reader = new FileReader();
reader.readAsText(file_one);
reader.onload = readSuccess;
function readSuccess(evt){
file_one_contents = evt.target.result;
}
var reader = new FileReader();
reader.readAsText(file_two);
reader.onload = readSuccess2;
function readSuccess2(evt2){
file_two_contents = evt2.target.result;
}
console.log(file_one_contents);
console.log(file_two_contents);
The console log only contains undefined in it.
The goal of the script it two read in two CSVs and take the data from the pair of files and do some computations.
Thanks!

The API is asynchronous. The "success" functions are called when the operation completes, and that won't be immediate.
Move your console.log() calls to inside the handler functions.
edit — If you need to wait to start doing stuff until both files are ready, you can do something like this:
var countdown = 2;
var reader = new FileReader();
reader.readAsText(file_one);
reader.onload = readSuccess;
function readSuccess(evt){
file_one_contents = evt.target.result;
countdown--;
if (countdown === 0) go();
}
var reader = new FileReader();
reader.readAsText(file_two);
reader.onload = readSuccess2;
function readSuccess2(evt2){
file_two_contents = evt2.target.result;
countdown--;
if (countdown === 0) go();
}
There are more sophisticated ways to do it, of course, but that simple trick just waits until the counter is zero before calling "go()", which represents the function that'd work on processing the files.

I had a similar problem which solved the file read waiting by using ".onloadend" instead of "onload". In the code below x is bound to a "div" element
reader.onloadend = function (evt) {
x.innerHTML = evt.target.result;
}
With "onload" it was all undefined.

Related

How to access a local variable in filereader onload callback

I am doing a file upload operation in React, and I need to read the file uploaded from the user and do some state changes according to this file. What I have right now is shown below and I need to need to access the variable startInt within the onload callback, but it is still not defined here using the IIFE
const file = document.getElementById("fileUpload").files[0];
if (file) {
const reader = new FileReader();
reader.readAsText(file, "UTF-8");
reader.onload = ((theFile) => {
const form = document.getElementById('fileUploadForm');
const start = datetimeToISO(form.Start.value);
const startInt = new Date(start).getTime();
return (e) => {
console.log(e.target.result);
//startInt is not defined here
}
})(file);
}
I followed this guide if it helps: https://stackoverflow.com/a/16937439/6366329
If you could point out my mistake that would be great. Many thanks in advance
you can access local var (but not class const like this.state.* or this .props.*).
so something like this you need:
var file = document.getElementById('inputID').files[0]
var Images = this.props.motherState.Images // Images is array
var reader = new FileReader();
reader.readAsDataURL(file); //
reader.onload = function () {
//console.log(reader.result);
if (file.type.match(/image.*/))
Images.push(reader.result) // its ok
// but this.props.motherState.Images.push(reader.result)
// return error like this:
// Images not define in this.props.motherState.Images
};
reader.onerror = function (error) {
//console.log('Error: ', error);
};

Waiting for FileReader to complete before continuing

So I am starting to work with HTML5 and JS for the first time to do some experiments for a future game. I am using FileReader to read in a local text file that will represent a map, and using a script to return an array of arrays based on the text read in.
The problem is, I am having trouble with undefined variables.. because my code is executing before FileReader is able to read in the input file and do its processing. This is my block that reads in:
document.getElementById('input').addEventListener('change', function() {
var file = this.files[0];
var reader = new FileReader();
reader.onload = function(e) {
let map = readMap(e.target.result);
}
reader.readAsText(file);
}, false);
readMap() is an external function that simply takes the raw text and returns a 2d array based on it. This all works great.. and I tested that by logging inside of the readMap function. The issue is that I am referencing this 'map' variable later on in this file in order to read it, render it, etc. I immediately get 'Uncaught ReferenceError: map is not defined' when loading my code. This makes perfect sense... since the above block did not yet get to execute.. as it is still waiting on the user to select an input file.
Here is one of the few functions that need access to the 'map' later on in the code:
function drawMap() {
let x = 0;
let y = 0;
for(var i=0; i<map.length;i++) {
for(var j=0; j<map[i].length;j++) {
if(map[i][j] === 1) {
ctx.fillRect(x,y,BLOCK_SIZE,BLOCK_SIZE);
x+=BLOCK_SIZE;
}
else if(map[i][j] === 'S') {
player.x = x;
player.y = y;
x+=BLOCK_SIZE;
}
else {
x+=BLOCK_SIZE;
}
}
x=0;
y+=BLOCK_SIZE;
}
}
Im not sure if that helps, but I just wanted to give an example of how I need to use this array.
Being fairly new to JS, I am really not yet finished wrapping my head around the idea of onLoads and callbacks... but my feeling is that they are the way to go about making this work. I assume I need to somehow tell the rest of my code not to execute until the 'map' object is finished being created.
I hope that was clear. Any help is appreciated!
Call the function drawMap() or any other function only after you have received the data, thus within reader.onload callback, if you want to access this map later, make map variable global e.g.:
var map;
document.getElementById('input').addEventListener('change', function() {
var file = this.files[0];
var reader = new FileReader();
reader.onload = function(e) {
map = readMap(e.target.result);
drawMap();
}
reader.readAsText(file);
}, false);
Or you can also pass map variable to drawMap function:
document.getElementById('input').addEventListener('change', function() {
var file = this.files[0];
var reader = new FileReader();
reader.onload = function(e) {
let map = readMap(e.target.result);
drawMap(map);
}
reader.readAsText(file);
}, false);
function drawMap(map) {
let x = 0;
let y = 0;
for(var i=0; i<map.length;i++) {
for(var j=0; j<map[i].length;j++) {
if(map[i][j] === 1) {
ctx.fillRect(x,y,BLOCK_SIZE,BLOCK_SIZE);
x+=BLOCK_SIZE;
}else if(map[i][j] === 'S') {
player.x = x;
player.y = y;
x+=BLOCK_SIZE;
} else {
x+=BLOCK_SIZE;
}
}
x=0;
y+=BLOCK_SIZE;
}
}
there is also FileReaderInstance.onloadend event
reader.onloadend = () => drawMap()

File reader execute multiple times in javascript

I'm trying to load images in to page for preview before uploading with javascript.
I have following code:
holder.onclick = function(event) {
function chooseFile(name) {
var chooser = $(name);
chooser.unbind('change');
chooser.change(function(evt) {
function loadFile(file, callback) {
var reader = new FileReader();
(reader.onload = function(file) {
console.log(f);
var output = document.createElement('input');
output.type = 'image';
output.classList.add('image-responsive');
output.classList.add('col-xs-12');
output.name = f;
output.id = f;
output.src = reader.result;
var x = document.getElementById('OrigName');
x.appendChild(output);
return callback(output);
})(f = file.name);
reader.readAsDataURL(file);
}
for (var i = 0; i < evt.target.files.length; i++) {
console.log(i);
var file = evt.target.files[i];
loadFile(file, function(output) {
// console.log(output);
});
}
});
chooser.trigger('click');
}
chooseFile('#fileDialog');
}
Problem is, whenever i load image, code inside reader.onload method execute twice, and in console i 2x result of console.log(f) and 2 errors that 'localhost/null is not found'.
When i remove (f=file.name), script execute as it should be, but then i don't have file.name variable inside reader scope.
EDIT:
Here's JSFiddle of my problem:
https://jsfiddle.net/onedevteam/udmz34z0/6/
Can someone help me fix this?
Problem is, whenever i load image, code inside reader.onload method execute twice
This is because in your code you have this.
(reader.onload = function(file) {
//...
//...
})(f = file.name); // <---- self executing function.
reader.readAsDataURL(file);
Here you are using "Self Executing function" for the reader.onload, So what happens is it will execute once when it hits this line of code, And again when reader.readAsDataURL(file) has completed reading. So remove the "self executing function " and you logic will run only once
When i remove (f=file.name), script execute as it should be, but then i don't have file.name variable inside reader scope.
to get the file name just add it in a variable and use it like this.
var fileName = file.name;
reader.onload = function() {
//...
//...
output.name = fileName ;
output.id = fileName ;
}; // <-- self executing function REMOVED
Also I feel there is no need to save the file name into a variable because the variable file passed into function is sufficient to get the job done. So below would be the final code as per my suggestion.
function loadFile(file, callback) {
var reader = new FileReader();
reader.onload = function() {
console.log(file.name); //
var output = document.createElement('input');
output.type = 'image';
output.classList.add('image-responsive');
output.classList.add('col-xs-12');
output.name = file.name; //
output.id = file.name; //
output.src = reader.result;
var x = document.getElementById('OrigName');
x.appendChild(output);
return callback(output);
};
reader.readAsDataURL(file);
}
You're calling reader.onload at least twice. You have this function inside another function loadFile(), and you call it immediately (which is why you only see this behavior when you have (f=file.name) there), but then also inside the chooser.change function you have that for-loop that calls loadFile(). Perhaps ou could set the file.name variable somewhere other than (f=file.name) and then make reader.onload not execute automatically.
The way you have your code structured, your onload handler will be executed twice, once when you define it, and then again when the "load" event fires. When you wrap a function definition inside parens:
(reader.onload = function (file) { ... })(f = filename)
you're saying "define this function and execute it immediately."
What you really want is a function that returns a function, like this:
function makeOnLoadHandler (filename) {
return function (file) {
// ... do whatever you need to with file and filename
};
}
reader.onload = makeOnLoadHandler(someFileName);
The outer function, makeOnLoadHandler(), creates a closure around your filename variable, and when the inner function handles the reader's load event, it will see the filename that you passed in when you called makeOnLoadHandler.

Array.push not updating an array variable

Here is the code I am working on:
<!--HTML Code for referencing the file -->
<input type="file" name="file" id="file">
<script>
var store = [];
document.getElementById('file').onchange = function(){
var file = this.files[0];
var reader = new FileReader();
// Define the body of reader function
reader.onload = function(progressEvent){
// By lines
var lines = this.result.split('\n');
for(var line = 0; line < lines.length; line++){
// Store it in an array
store.push(lines[line]);
//console.log(store.length); // This line on being uncommented
// shows that store is being modified. The values getting printed
// are 1,2,3, ...... upto 16 (length of the input file)
}
};
// Read the file and store it in the var "store"
reader.readAsText(file);
console.log(store.length); // The problem appears here!!!!!
};
</script>
The problem is, even after choosing a file containing 16 sample numbers, the console prints the store.length value as 0. Why is the push command not affecting the var "store" ?
You're setting up an event handler on the onchange property, but you're doing a console.log(store.length) outside, so you will never get the results you expect that way.
The event handler function will be triggered when the value of your element with id "file" changes, so you need to print the length of the store, inside the function, like this:
document.getElementById('file').onchange = function(){
var file = this.files[0];
var reader = new FileReader();
// Define the body of reader function
reader.onload = function(progressEvent){
// By lines
var lines = this.result.split('\n');
for(var line = 0; line < lines.length; line++){
// Store it in an array
store.push(lines[line]);
//console.log(store[line]);
}
console.log(store.length);
};
I would recommend also declaring the store as local to that function, that way you'll always get a brand new array, otherwise you need to manually re-initialize it or empty it before you start adding things to it or on subsequent change events, your "store" array will be filled with everything from the previous changes.
Makes sense?
FileReader is asynchronous. You either want to use FileReaderSync or do this:
var store = [];
document.getElementById('file').onchange = function() {
var file = this.files[0];
var reader = new FileReader();
// Define the body of reader function
reader.onload = function(progressEvent) {
// By lines
var lines = this.result.split('\n');
for (var line = 0; line < lines.length; line++) {
// Store it in an array
store.push(lines[line]);
}
};
reader.onloadend = function() {
console.log(store.length);
};
// Read the file and store it in the var "store"
reader.readAsText(file);
};
<input type="file" name="file" id="file">
The onload event of the FileReader is asyncrhonous. Which means it's not executed in the natural flow of the program.
To check the final length of the store variable, you should do this:
var store = [];
document.getElementById('file').onchange = function(){
var file = this.files[0];
var reader = new FileReader();
// Define the body of reader function
reader.onload = function(progressEvent){
// By lines
var lines = this.result.split('\n');
for(var line = 0; line < lines.length; line++){
// Store it in an array
store.push(lines[line]);
}
// Correctly reads the final length of the store variable.
console.log(store.length);
};
reader.readAsText(file);
};
Hope it helps.

HTML5 FileReader in Opera

I am trying to read multiple local ogg audio files using the file reader api in opera 12 by converting them into data urls and storing them into an array which I intend to access later.
function music(list)
{
var playlistlength=0;
try
{
for (var temp=0; temp < list.files.length; temp++)
{
f[playlistlength][0] = list.files.item(temp).name;
var freader = new FileReader();
freader.onload = function(frevent){
f[playlistlength][1]=frevent.target.result;
};
freader.readAsDataURL(list.files[temp]);
playlistlength++;
}
}
catch(e)
{
alert("Error");
}
}
However upon inspection in the debugger, in the for loop , no statement is executed and it jumps to the catch block, unable to figure it out.Can someone explain why its happening and any alternatives if possible? Thanks in advance.
This works on jsFiddle. I made it from your code, is your use of it similar ?
HTML :
​<input type="file" id="browser"/>​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​
Javascript :
function music(list)
{
var playlistlength=0;
var f = new Array();
try
{
for (var temp=0; temp < list.files.length; temp++)
{
var music = new Array();
music[0] = list.files.item(temp).name;
var freader = new FileReader();
freader.onload = function(frevent){
music[1]=this.result;
f.push(music);
alert(f);
};
freader.readAsDataURL(list.files[temp]);
playlistlength++;
}
alert(f);
}
catch(e)
{
alert(e);
}
}
function FileSelectHandler(e) {
var list = e.target || e.originalEvent.dataTransfer;
music(list);
}
document.getElementById("browser").onchange = FileSelectHandler;
Why it didn't work ?
First you tried to set a value in f[playlistlength][1], because of
the onload function asynchronously executing after the
playlistlength++. It crashed because this reference didn't exist.
f was probably wrong declared ​

Categories