how to store dataURL outside reader.onload function [duplicate] - javascript

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 6 years ago.
so i want store the dataURl of a file outside the reader.onload function
but when i assign it to a variable hello and try to print it , it still remins undefined.
here is the code
<input type='file' accept='image/*' onchange='openFile(event)'><br>
<img id='output'>
<script>
var openFile = function(event) {
var input = event.target;
var reader = new FileReader();
reader.onload = function(){
var dataURL = reader.result;
var output = document.getElementById('output');
output.src = dataURL;
};
reader.readAsDataURL(input.files[0]);
var hello=reader.result;
console.log(hello);
console.log(reader.result);
};
</script>
but the same reader.result can be printed from inside the the on load function
i want a way to store the dataURL of the file outside the reader.onload function so that i can use it for future use anyway possible ?

Here is your script with some logging pointing out the order things will run:
var openFile = function(event) {
console.log("1. onchange triggered");
var input = event.target;
var reader = new FileReader();
console.log("2. Set handler for onload event");
reader.onload = function(){
console.log("5. onload handler called. Now the file is ready");
var dataURL = reader.result;
var output = document.getElementById('output');
console.log("6. Set img src to image");
output.src = dataURL;
};
console.log("3. Start reading file");
reader.readAsDataURL(input.files[0]);
console.log("4. Try to look at the file read result, even though the file has not finished being read yet...");
var hello=reader.result;
console.log(hello);
console.log(reader.result);
};
JS fiddle: https://jsfiddle.net/ncwv0ocL/1/
The problem is that readAsDataURL starts loading the file in the background, while the rest of your code executes. You can only get the result after the onload handler has been called.
One option to make it a bit clearer might be to do something like this:
var openFile = function(event) {
var input = event.target;
var reader = new FileReader();
reader.onload = fileLoaded; // Tell the FileReader to call this function when the file is loaded
reader.readAsDataURL(input.files[0]);
// Code here should not depend on the state of the FileReader
};
var fileLoaded = function (event) {
var dataURL = event.target.result; // The event parameter gives us the target of the event (the FileReader)
var output = document.getElementById('output');
output.src = dataURL;
// Do your other stuff here with dataURL
console.log(dataURL);
};
One thing I like about this is that the code gets executes in the order it appears on the page :)
See it in action here: https://jsfiddle.net/ncwv0ocL/2/

The problem is trying to view the result after the reader is finished. The solution would be to call a function from our reader's onload that would provide any functionality we need, i.e. updating our output field.
var openFile = function(e) {
var input = e.target
var reader = new FileReader()
// Our doSomething function would do whatever is needed after we get our value.
// This allows us to dynamically update anything as opposed to having to check a value to see if it has been updated
var doSomething = function(dataURL) {
var output = document.getElementById('output')
output.src = dataURL
console.log(dataURL)
}
reader.onload = function() {
// Once we have the data, we pass it to our action that will control what we do with the result
doSomething(reader.result)
}
reader.readAsDataURL(input.files[0]);
}
So, our process becomes Upload File >> Read File >> Notify Application
Here is an example of the solution on CodePen

Related

Can't return value of fileReader result in AngularJS service

I'm trying to write a service which reads the image file from an HTML input element. I want this service to return the HTML img object with the updated attribute from the read image file (the base64 string). My service is now this:
.service('ReadLocalImageService', [function () {
this.openLocalImage = function (file) {
var img = document.createElement("img");
var fileReader = new FileReader();
fileReader.onload = function (e) {
img.src = e.target.result;
};
fileReader.readAsDataURL(file);
return img.src;
};
}]);
The img.src in this case is returned empty, like this:
If I put a console.log(img.src) inside the fileReader.onload, it prints out what I want. It seems like the function openLocalImage is returning the img.src before e.target.result is assigned to it.
I couldn't manage to work this around nor find the correct topic about this problem. Could anyone help me solve this or explain me why it doesn't work?
Its because img was not loaded yet. Here is a solution
.service('ReadLocalImageService', [function () {
this.openLocalImage = function (file, callback) {
var img = document.createElement("img");
var fileReader = new FileReader();
fileReader.onload = function (e) {
img.src = e.target.result;
callback(img.src);
};
fileReader.readAsDataURL(file);
};
}]);
We will pass a callback function and receive img.src as its param. To use it
ReadLocalImageService.openLocalImage(myImage, function(imgSrc) {
// use imgSrc
});

How to save text file contents to Javascript variable?

I'm trying to read a text file of over 150,000 lines of text. I want to be able to read the text file and pass it as a parameter for processFileContent.
I tried it this way, but it doesn't work. Also, is there a better way to do this for such big data?
function readFile(file) {
var reader = new FileReader();
reader.onload = function (evt) {
var data = evt.target.result;
};
reader.readAsText(file);
return data;
}
document.getElementById('file').addEventListener('change', readFile, false);
var data = readFile();
function processFileContent(data) {
var list = data.split('\n');
...
FileReader.onload event returns results asynchronously. You can use a callback or Promise to return result of FileReader to processFileContent. Also file at readFile would be event object, not .files property of event.target.
function readFile(event) {
var file = event.target.files[0];
if (file) {
new Promise(function(resolve, reject) {
var reader = new FileReader();
reader.onload = function (evt) {
resolve(evt.target.result);
};
reader.readAsText(file);
reader.onerror = reject;
})
.then(processFileContent)
.catch(function(err) {
console.log(err)
});
}
}
document.getElementById('file')
.addEventListener('change', readFile, false);
function processFileContent(data) {
var list = data.split('\n');
...
One of your problems is with scoping. You declared data as a local variable in the onload event handler, and tried returning it from the outer function, in which it was undefined. To fix this, you need to move the variable declaration out of the event handler.
You also are expecting a file as an argument to your event handler it seems, however events pass Event objects to their event handlers. To get the file, you need to get event.target.files[0]. This should fix it.
function readFile(event) {
var file = event.target.files[0]; // getting the file Blob
var reader = new FileReader();
var data; // moved declaration
reader.onload = function (evt) {
data = evt.target.result;
};
reader.readAsText(file);
return data;
}

Image in localstorage function doesn't work

I'm wrting a function which takes an image from a file input from a form and enables me to put it in localstorage. The function I wrote to achieve this:
function getImage() {
var pic = document.getElementById("image").files[0];
var imgUrl;
var reader = new FileReader();
reader.onload = function(e) {
var imgURL = reader.result;
saveDataToLocalStorage(imgURL);
return imgUrl;
}
}
Then in another function I call this function and create a JSON entry in which I store values from other form inputs including the image. It looks like this:
var imgUrl = getImage();
// Create new JSON entry
var json_entry = {'title': titleField.val(),
'image': imgUrl,
'content': contentField.val(),
'location': location};
Sadly the value of imgUrl is undefined.. There are no console errors. What am I doing wrong? And how can I fix this?
I honestly don't know much about the FileReader object, but I can see just from glancing at your JS that (at least) one thing is off:
var imgUrl = getImage();
Your getImage function doesn't return anything; so imgUrl is definitely going to be undefined above.
If you want to do something with the result property of your FileReader, then you need to do so w/ a callback since you're handling the (asynchronous) onload event:
function getImage(callback) {
// What are you doing with this?
var pic = document.getElementById("image").files[0];
var reader = new FileReader();
reader.onload = function(e) {
var imgURL = reader.result;
saveDataToLocalStorage(imgURL);
// Note the difference here: rather than return from the event handler
// (which effectively does nothing) we pass the result to a callback.
callback(imgUrl);
}
// I assume you actually need to load something with the FileReader?
}
And then:
getImage(function(imgUrl) {
var json_entry = {
'title': titleField.val(),
'image': imgUrl,
'content': contentField.val(),
'location': location
};
});
It looks like you are forgetting to set the reader to readAsDataUrl. Likely the value is coming back as undefined because localStorage does not inherently know how to serialize binary data. Setting the reader to readAsDataUrl changes reader.result onload.
var reader = new FileReader();
reader.onload = function(e) {
var imgURL = reader.result;
saveDataToLocalStorage(imgURL);
callback(imgUrl);
};
// add this line
reader.readAsDataURL(pic);
Have a look at this article, especially the section titled Reading Files. Note in the linked example the author uses e.target.result instead of reader.result. This should be the same value.

Passing file contents to outside variable

I'm using a FileReader and the HTML file dialog to read a file in my script. How do I pass this file's contents out of the FileReader.onload function?
function readFileData(evt) {
var file = evt.target.files[0];
var reader = new FileReader();
reader.onload = function(e) {
var contents = e.target.result;
}
reader.readAsText(file);
}
document.getElementById('file').addEventListener
('change', readFileData, false);
/* I want to access the contents here */
I tried sticking returns in the readFileData and onload functions, but I'm not sure what they return to.
I assume that you know, its async and all.
So, the short answer is: No, you can not do that.
However, if you want the contents to be globally accessible for any future calls, you could something like this:-
var contents;// declared `contents` outside
function readFileData(evt) {
var file = evt.target.files[0];
var reader = new FileReader();
reader.onload = function(e) {
contents = e.target.result; //<-- I removed the `var` keyword
}
reader.readAsText(file);
}
document.getElementById('file').addEventListener('change', readFileData, false);
var reasonableTimeToWaitForFileToLoad = 100000;
console.log(contents); //`contents` access first attempt: prints undefined
setTimeout(function() {
console.log(contents);//`contents` access second attempt: prints the contents 'may be if the time allows.'
}, reasonableTimeToWaitForFileToLoad);
var contents;
function readFileData(evt) {
var file = evt.target.files[0];
var reader = new FileReader();
reader.onload = function(e) {
contents = e.target.result;
}
reader.readAsText(file);
reader.onloadend=function(e) {
console.log(contents)
}
}
document.getElementById('file').addEventListener('change', readFileData, false);
This is a scoping issue. When you're declaring contents within the onload, it's no longer available after that function has run. You need to declare contents outside of that scope first.
var contents;
function readFileData(evt) {
var file = evt.target.files[0];
var reader = new FileReader();
reader.onload = function(e) {
contents = e.target.result;
}
reader.readAsText(file);
}
document.getElementById('file').addEventListener
('change', readFileData, false);
//make changes here
//contents should have the correct value
Firstly, you have to realize that reading a file with a FileReader is an asynchronous task, and you cannot work with it in a synchronous manner.
There are many ways to handle this, but many of them are not suited for recommendations ;)
I would do it one of these 2 ways:
1: you can call a function from within the onload event handler and pass the file contents as a parameter
2: you can trigger an event from within the onload event handler and pass the file contents as event data
Just declare contents outside both functions and assign to it inside the inner function:
var contents;
var outer_fn = function() {
var inner_fn = function() {
contents = '42';
}
inner_fn();
}
outer_fn();
// here, `contents' is '42'
I faced a similar challenge and this is what I used to solve the issue.
var contents;
function readFileData(evt) {
var file = evt.target.files[0];
var reader = new FileReader();
reader.onload = function(e) {
contents = e.target.result;
}
reader.readAsText(file);
//calling to access the 'contents' variable
accessFileContents();
}
document.getElementById('file').addEventListener('change', readFileData, false);
var wait4file2load = 1000;
/* To access 'contents' here */
function accessFileContents(){
setTimeout(function(){
console.log(contents);
}, wait4file2load);
}
It won't give undefined value since we are calling it after the file is completely uploaded.
I had a similar problem in Angular 7 (typescript), and this is how I solved my problem.
What I wanted to do was to access the base64 conversion that was happening inside fileReader -> reader.onload
Then pass that parameter to another method where I could convert it to a JSON object then post it to the API seeing I want to post another parameter as well in the post. (not added in this code)
What I did first was to declare what I potentially needed to access outside the Method that.
base: any = null;
base64File: any = null;
fileToTest: any = null;
Then I converted the pdf to base64 when the upload event fired
convertToBase64 (e){
this.fileToTest = e.target.files[0];
var reader = new FileReader();
reader.onload = () => {
this.base64File = reader.result.slice(28);
};
reader.onerror = function (error) {
console.log('Error: ', error);
}.bind(this.base64File);
reader.readAsDataURL(this.fileToTest);
return this.base64File;
}
Finally access the base64 file in the other method
onSubmit() {
console.log("base 64 file is visible", this.base64File);
var base =
{
"FileBase64": this.base64File,
"Path": "document",
"FileType": ".pdf"
};
console.log("JSON object visible", base);
this.uploadService.base64Post(base);
}
Everything works now, and hopefully maybe this can help someone else finding themselves with the same problem.
Using Angular 7, code is in the component file, and the post function is in the Service file. Without my comments the code is exactly like this in the component file.

jQuery variable not being updated

I am using HTML5 file API to get the width of an image.
Its working, and the console.log is outputting the correct amount.
I need to save the value to the variable fileWidth so I can access it outside the function. I have created an empty variable outside the function, and expected it to be updated with the alue inside the function, but whatever I try fails.
Here is my code:
var fileWidth;
var reader = new FileReader;
reader.onload = function() {
var img = new Image;
img.onload = function() {
fileWidth = img.width;
console.log(fileWidth);
};
img.src = reader.result;
};
reader.readAsDataURL($('#submission-file')[0].files[0]);
Can anyone see why the variable isn't being updated?
Edited Code:
var fileWidth;
var reader = new FileReader;
reader.onload = function() {
var img = new Image;
img.src = reader.result;
};
reader.onloadend = function() {
fileWidth = img.width;
};
reader.readAsDataURL($('#submission-file')[0].files[0]);
console.log(fileWidth);
You're setting fileWidth in an asynchronous callback, this method isn't guaranteed to have executed prior to your accessing the variable.
Hint
Try putting an alert in your callback and an alert right before you access the variable. See which alert is presented first.

Categories

Resources