i have implemented an Filereader with the FileReader API of JavaScript.
My problem is that I have to use it´s result, but since it´s asynchronous I can´t figure out how to get the data when it´s needed.
simplified:
function MyReader() {
reader = new FileReader();
this.myResult=[];
//callback;
parse= function (evt) {
switch (evt.target.readyState) {
case FileReader.DONE:
var result = evt.target.result;
//parsing the file and save into myResult
callback(myResult);
break;
case FileReader.LOADING:
break;
case FileReader.EMPTY:
break;
}
}}
MyReader.prototype = {
read: function (callback) {
//callback=callback;
var files = document.getElementById('files').files;
var file = files[0];
reader.onloadend = parse;
var blob = file.slice(0, file.size);
reader.readAsArrayBuffer(blob);
},
getResult: function () {
alert(myResult.length);
}};
//index.html
<script>reader = new MyReader();
reader.read();
//reader.read(function(res){alert("call: "+ res)});
reader.getResult();
alert("bla"); </script>
When I now run that I get first "bla" and then the length of the result.
But I need the result directly after I call read().
How can I ensure that the Reader is done when I need it? I already tried to introduce an "state" Variable and check that, but it didn´t worked.
The only other idea I had was to use an callback with the function, which needs the result and add that to parse, but when I added a callback it doesn´t work and I don´t want to use an callback. Is it not possible to use some kind of mutexes? (Although it would be shit if the browser blocks....)
Related
I simply want to get my HTML file input as a binary to save it in my SQL SERVER. The code below,apparently solves my problem,but i can't get "reader.readAsBinaryString(input.files[0])" and store in a variable.
It just console.log the input.files[0] ,but i need to store it.
Also,I'm not used to FileReader(),so any tips on how to use it are welcome.
Does this function uses the actual file being uploaded,or does it get the path to it?
The code is simple:
<input type="file" id="myFile" name="myFile">
JS:
$("#myFile").change(function (event) {
var input = event.target;
var reader = new FileReader();
reader.onload = function () {
console.log(reader.result);
};
fileToSave = reader.readAsBinaryString(input.files[0]);
});
readAsBinaryString doesn't return anything. What it does do is tell the FileReader to begin reading the file. When that process finishes, you can see the data you're reading in the result property of the FileReader object.
Which you've observed is available in the onload event:
reader.onload = function(){
console.log(reader.result);
};
Since that event is when the data is available, then that event is where you can read that data. For example:
reader.onload = function(){
fileToSave = reader.result;
console.log(fileToSave);
};
Of course then the next question becomes... When/where do you attempt to use the value in fileToSave. Keep in mind that this is an asynchronous operation. If you're trying to use the data in fileToSave right away then of course it won't contain the data that is later available in this onload event.
Whatever operation needs to use that data, that operation would have to happen after this event. For example:
reader.onload = function(){
fileToSave = reader.result;
console.log(fileToSave);
someOtherOperation();
};
or perhaps even:
reader.onload = function(){
const fileToSave = reader.result;
console.log(fileToSave);
someOtherOperation(fileToSave);
};
I'm trying to open different JSON files and compare each others values.
To do it, I used this to read the files. But I'm trying to save every data in a global variable 'data'. I think it's a asynchronous mistake but I'm pretty new to javascript and I didn't understand where the error come from.
Here is my code :
var data = {}
function readmultifiles(files) {
var reader = new FileReader();
function readFile(index) {
if( index >= files.length ) return;
var file = files[index];
reader.onload = function(e) {
// get file content
var bin = e.target.result;
bin = JSON.parse(bin);
for(task in bin['values']){
addData(bin['info']['date'],task,bin['values'][task]);
}
// do sth with bin
readFile(index+1);
}
reader.readAsBinaryString(file);
}
readFile(0);
console.log("readmultifiles");
console.log(data);
return data;
}
function addData(date, task, value){
if(data[task] == undefined){
data[task] = {};
}
data[task][date] = value;
}
var fileInput = document.querySelector('#file');
fileInput.addEventListener('change', function() {
console.log(fileInput);
var files = fileInput.files;
readmultifiles(files);
console.log("index");
console.log(data);
console.log(data['task_1']); // I can't display this object because 'undefinned'
});
What happens? When I'm trying to watch 'data', firefox console display me the object but I cannot watch inside the object.
Firefox display
What do I need to do to make a good solution. Should I use timers to wait?
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.
I'm trying to access my variable but it is giving me an empty array.
example:
var data = [];
$.each(files, function (index, file) {
var reader = new FileReader();
reader.onload = handleReaderLoad;
reader.readAsDataURL(file);
function handleReaderLoad(evt) {
data.push({
name: file.name,
file: evt.target.result
});
}
});
console.log(data)
At this moment data is an empty array. When i put the console.log in the foreach it is returning my data. How can i return and acces the data that is filled outside of the foreach?
handleReaderLoad will be called in an asynchrounous fashion. You can keep track of the number of files and log in the onload when the file count is complete
var data = [];
var fileCount = files.length;
var currentCount = 0;
$.each(files, function(index, file) {
var reader = new FileReader();
reader.onload = handleReaderLoad;
reader.readAsDataURL(file);
function handleReaderLoad(evt) {
data.push({
name: file.name,
file: evt.target.result
});
currentCount++;
if (currentCount == fileCount) {
console.log(data);
}
}
});
I'm not familiar with FileReader but it's obvious that method readAsDataURLis called asynchronous.
Your onload method is not called inmediatly, your code flow continues without waiting for that to trigger so at the point console.log(data) is reached, data is not filled yet.
JavaScript is a single-threaded language. This means that invoking a long-running process blocks all execution until that process completes. UI elements are unresponsive, animations pause, and no other code in the app can run. The solution to this problem is to avoid synchronous execution as much as possible.
One way to do this is to have a function execute at a later time, as with event handlers, which are invoked after another call has raised an event. Callback functions are another kind of asynchronous processing, because they call back into the code that initiated the process.
Asynchronous programming in JavaScript
This could do the trick.
var data = [];
$.each(files, function (index, file) {
var reader = new FileReader();
reader.onloadend = handleReaderLoad;
reader.readAsDataURL(file);
});
function handleReaderLoad(evt) {
data.push({
name: file.name,
file: evt.target.result
});
if(files.length == data.length)
console.log(data);
}
I've started playing around with the excellent http://blueimp.github.io/jQuery-File-Upload/ file upload project.
From the File Processing Options section of the documentation it seems that jquery.fileupload-process.js will let me parse and even modify the file's binary data (files array - with the result of the process applied and originalFiles with the original uploaded files)
(to parse, or append to it or encrypt it or to do something to it)
but for the life of me I can't seem to figure out where is the actual file data within the array so that I can pre-process it before it uploads.
What part of the data array has the "something.pdf" binary file in it? so that I can parse and transform it before upload?
//FROM: jquery.fileupload-process.js
//The list of processing actions:
processQueue: [
{
action: 'log'
}
],
add: function (e, data) {
var $this = $(this);
data.process(function () {
return $this.fileupload('process', data);
});
originalAdd.call(this, e, data);
}
},
processActions: {
log: function (data, options) {
console.log(data.files[0]); //Is it here?
console.log(data); //Is it here?
console.log(data.files[data.index]); //Is it here?
console.log(data.files[data.index].name); //Is it here?
//where?
Thank you.
The correct way to access the currently processed file is the following:
var file = data.files[data.index];
For browsers which support the File API, this is a File object.
To retrieve the actual File data, we have to use the FileReader interface:
var fileReader = new FileReader();
fileReader.onload = function (event) {
var buffer = event.target.result;
// TODO: Do something with the ArrayBuffer containing the file's data
};
fileReader.readAsArrayBuffer(file);
It may be useful, building on #Sebastian's answer, to explain where to put the FileReader, to work with the fileupload plugin. (I would have made this a comment on #Sebastian's answer, but didn't have space in the comment field).
I have built this in to the process action (using readAsText instead of readAsArrayBuffer) as follows:
alertText: function(data, options) {
var dfd = $.Deferred(),
file = data.files[data.index];
var fileReader = new FileReader();
fileReader.onload = function (event) {
var fileText = event.target.result;
alert('Text of uploaded file: '+ fileText);
};
fileReader.readAsText(file);
return dfd.promise();
}