How to make FileReader Asyncronous without Using FileReaderSync in Javascript? - javascript

I am using FileReader for reading multiple files and folders through drag and drop in chrome using javascript. It is working properly, but problem is that i want to call function after all file and folder read completes. I can't because it is asyncronous operation and FileReaderSync is not supported in chrome. So is there any way to doing this?
this is my code,
entry.file(function (file) {
var reader = new FileReader();
reader.onloadend = function (e) {
data.push(this.result);
};
reader.readAsText(file);
});

As an example of what can be used here. This is can't be used as "Copy&Paste" variant :
var filesCount = "give here number of files";
var callbackFunction = function(){
if(data.length == filesCount ){
Console.log( "Assume this as the end of all files reading" );
}
}
entry.file(function (file) {
var reader = new FileReader();
reader.onloadend = function (e) {
data.push(this.result);
callbackFunction();
};
reader.readAsText(file);
});

Related

Uncaught TypeError: this.files is undefined thrown by fr.readAsText(this.files[0]) (FileReader)

I am experiencing an unexpected error when trying to load a javascript object using FileReader, unexpected because it works fine in another project, which is listed below. My intention is to perform a few operations on the javascript object right after reading it by executing a function that has several parameters, and that's why I changed the code structure because I didn't know how to put parameters in the code that works. Any tips are welcome.
Code that doesn't work
function wczytajSklad(sklad, id, objSklad, gdzie) {
var fr = new FileReader();
fr.onload = function(){
let wczytanySklad = fr.result;
sklad = JSON.parse(wczytanySklad);
ukryjInputFile(id);
pokazCheckAndRadio(objSklad, gdzie);
}
fr.readAsText(this.files[0]);
}
A place where i try to run function
<input type="file" name="gospodarze" id="wczytajSkladGospodarzy" onchange="wczytajSklad(skladGospodarzy, 'wczytajSkladGospodarzy', skladGospodarzy, 'skladGospodarzy')">
Code that works
document.getElementById('inputfile')
.addEventListener('change', function() {
var fr = new FileReader();
fr.onload = function(){
wczytanyPlik = fr.result;
arrPoints = JSON.parse(wczytanyPlik);
rysuj();
}
fr.readAsText(this.files[0]);
})

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

Alter file contents before uploading

I would like to encrypt the contents of a file before uploading to our server .. we are using fineuploader.
What I would like to do is:
function onSubmitted(id)
{
var file = uploader.getFile(id);
var reader = new FileReader();
reader.onload = function (e) {
//encrypt here
// but how do I save the file back to fineuploader?
//then continue to submit/upload
};
reader.readAsArrayBuffer(file);
}
is there any way of doing this out of the box or will I need to resort to rampant hackery :) :)
How about returning false to reject the file from onSubmit, encrypt it, then re-submit the encrypted version via the addBlobs API method. For example:
callbacks: {
onSubmit: function(id) {
if (!fileOrBlob.blob || !fileOrBlob.blob.encrypted) {
var fileOrBlob = uploader.getFile(id),
reader = new FileReader();
reader.onload = function (e) {
//encrypt here
encryptedBlob.encrypted = true;
uploader.addBlobs({name: fileOrBlob.name, blob: encryptedBlob});
};
reader.readAsArrayBuffer(file);
return false;
}
}
}
NOTE: The above has not been tested. If you run into serious issues, I'll check back in about 9 hours.

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.

JavaScript Async readAsDataURL multiple files

I have a list of files I need to save and in addition to the name I need to send the readAsDataURL to the server as well.
The problem is I am not sure how to do it with the async nature of readAsDataURL. Because to save the DATAURL to the array I need to look up the name of the file which is in the files list. and I cannot pass the file to the async method of readAsDataURL. How do you write this properly to work? The end result is I want a list of files sent to the server in one JSZip file.
function saveFileList(files)
{
for (var i = 0, file; file = files[i]; i++) {
var fr = new FileReader();
fr.onload = function(e){
if (e.target.readyState == FileReader.DONE) {
var tt = e.target.result.split(",")[1];
//update the record in the list with the result
}
};
var pp = fr.readAsDataURL(file);
}
If you have FileList and you need to get array of base64 string, you need do this
export async function fileListToBase64(fileList) {
// create function which return resolved promise
// with data:base64 string
function getBase64(file) {
const reader = new FileReader()
return new Promise(resolve => {
reader.onload = ev => {
resolve(ev.target.result)
}
reader.readAsDataURL(file)
})
}
// here will be array of promisified functions
const promises = []
// loop through fileList with for loop
for (let i = 0; i < fileList.length; i++) {
promises.push(getBase64(fileList[i]))
}
// array with base64 strings
return await Promise.all(promises)
}
use it like this
const arrayOfBase64 = await fileListToBase64(yourFileList)
You need another function around it, so you can pass the file in. Try this:
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
if(reader.readyState == FileReader.DONE)
alert(theFile.name); // The file that was passed in.
}
};
})(file);
reader.readAsDataURL(file);
An alternative to Russell G's answer:
var reader = new FileReader();
reader.onload = function(event){
payload = event.target.result;
var filename = file.name, filetype = file.type;//etc
//trigger a custom event or execute a callback here to send your data to server.
};
reader.onerror = function(event){
//handle any error in here.
};
reader.readAsDataURL(file);

Categories

Resources