Access variable outside foreach Jquery - javascript

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);
}

Related

How do i send a input type file as a binary?

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);
};

FileReader onload and assign value in loop?

i'm having difficulties on getting file reader content assignment. Is there anyway to wait the file reader finish onload and assign the file content before it push to the array?
I have a list of input file type button as below:
#for (int i = 0; i < #Model.LetterList.Count(); i++)
{
<tr>
<td>
<input type="file" id="LetterAttachment" name="LetterAttachment" accept="application/pdf">
</td>
</tr>
}
When i click submit, i want to assign the file content value into my form list in loop, below is my javascript code :
var attach=""; // empty variable for assign file content
function ApproverAction(action) {
var formList = [];
$("input[name='LetterAttachment']").each(function () {
if (this.files && this.files[0]) {
// I perform file reader here to assign the file content into attach....
var FR = new FileReader();
FR.onload = function (e) {
attach = e.target.result;
}
FR.readAsDataURL(this.files[0]);
var form = {
ID: newGuid(),
FileContents: attach, <<< ---- However it showing empty
DocumentName: this.files[0].name,
DocumentSize: this.files[0].size,
DocumentContentType: 'application/pdf',
SourceType: 'OnlineAssessment',
CreatedDate: '#DateTime.Now'
}
formList.push(form);
}
});
console.log(formList);
}
However i can't get the result quite correctly for output :
Any help and tips is highly appreciated! Thanks!
Use a promise for each file that resolves in the onload function and push those promises into an array
Then use Promise.all() to send the data once all promises have resolved. Note that the error handling will need to be improved depending on process flow you want
function ApproverAction(action) {
var filePromises = [];
$("input[name='LetterAttachment']").each(function() {
if (this.files && this.files[0]) {
// reference to this to use inside onload function
var _input = this;
var promise = new Promise(function(resolve, reject) {
var FR = new FileReader();
FR.onload = function(e) {
var form = {
ID: newGuid(),
FileContents: e.target.result;,
DocumentName: _input.files[0].name,
DocumentSize: _input.files[0].size,
DocumentContentType: 'application/pdf',
SourceType: 'OnlineAssessment',
CreatedDate: '#DateTime.Now'
}
// resolve promise with object
resolve(form);
}
FR.readAsDataURL(this.files[0]);
if(FB.error){
// needs more robust error handling, for now just reject promise
reject(FB.error)
}
// push promise to array
filePromises.push(promise)
}
});
}
});
// return a new promise with all the data
return Promise.all(filePromises)
}
Usage with promise returned from function
ApproverAction(action).then(function(formList){
// do something with the data array
console.log(formList);
}).catch(function(err){
console.error("Ooops something went wrong')
});
This is because the function you provided within the FR.onload is executed asynchronously. So, the code after it will be executed before the function is called thus the value of FileContents in the JSON is empty.
What you can do is either do all the stuff you want to do within the function or use some other function like readAsText.
...
var FR = new FileReader();
FR.readAsDataURL(this.files[0]);
var form = {
ID: newGuid(),
FileContents: FR.readAsText(this.files[0),
DocumentName: this.files[0].name,
DocumentSize: this.files[0].size,
DocumentContentType: 'application/pdf',
SourceType: 'OnlineAssessment',
CreatedDate: '#DateTime.Now'
}
...
Refer to this example for onLoad and readAsText.

Comparaison between different input files in javascript, cannot reach global variable

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?

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.

FileReader JS Api Call Fire before it's ready

I'm trying to read a file, create a "FileContainer", and a DataUrl from a fileReader so i can send it to a web api.
My problem is that the Api call fires before my object is created, so i send Null to the api. which means that if i send a small file, it works.
My code for the reader look something like this
var reader = new FileReader();
reader.onload = (function (theFile) {
return function (e) {
var newFile = {
name: theFile.name,
type: theFile.type,
size: theFile.size,
lastModifiedDate: theFile.lastModifiedDate
}
var binaryString = e.target.result;
updloadedFile(newFile, binaryString, selectedFolder, scope, fileProgrss);
}
};
})(f);
reader.readAsDataURL(f)
And for my http.post call
function updloadedFile(file, data, selectedFolder, scope, fileProgrss) {
var dummyobj = {
Name: file.name,
Extension: file.name.split('.')[0],
Path: selectedFolder.Path,
DataString: data,
}
$http.post('/api/Files/Upload/',
JSON.stringify(dummyobj),
{
headers: {
'Content-Type': 'application/json'
}
}
).success(function (data2) {
}).error(function (data, status, headers, config) {
});
}
You need to wrap your FileReader in a promise
function fileReader(file) { // perform async operation
var deferred = $q.defer();
var reader = new FileReader();
reader.onload = function() {
// Your reader.onload code here
deferred.resolve(reader.result);
};
reader.readAsDataURL(f);
return deferred.promise;
};
You can then call the uploadedFile() function like this:
fileReader().then(function(result){
updloadedFile(..., ..., ..., ...)
});
By using the promise object, you can access the result of of the async task when it completes by using then. then runs as soon the result is available.
If you want to read more about promises, this SO thread does a great job explaining it.
I am guessing that the issue here is, you are listening to onload, apparantly it gets called on each read operation, thus might be null in beginning( reference), try changing it to onloadend.
I don't really see the need for wrapping the FileReader API call within a promise. You could do that but according to the docs (https://developer.mozilla.org/en-US/docs/Web/API/FileReader) FileReader.onload gets called when the data is ready. Doing a additional deffered.resolved to be able to use then is just a nice to have.
I have created a fiddle. Even with big files the http call is made only if the data is available. I don't really see an error in your code. May you can try it out with the fiddle.
http://jsfiddle.net/tjruzpmb/212/

Categories