call function when filereader.onload has finished reading the files - javascript

submitTCtoDB(updateTagForm:any){
for(let i=0;i<this.selectedFileList.length;i++){
let file=this.selectedFileList[i];
this.readFile(file, function(selectedFileList) {
this.submitTC(updateTagForm,selectedFileList);
});
}
}
}
readFile(file, callback){
let fileReader: FileReader = new FileReader();
fileReader.onload= () => {
this.fileContent=fileReader.result;
if(this.fileContent.indexOf("END DATA | BEGIN RESULTS") != -1){
alert("Multiple testcases found in "+file.name+" file. Please separate/save testcases in Calc Builder. Then reimport");
const index: number = this.selectedFileList.indexOf(file);
if (index > -1) {
this.selectedFileList.splice(index, 1);
console.log(file.name+"removed from the list");
}
}
fileReader.readAsText(file);
}
callback(this.selectedFileList);
}
submitTC(updateTagForm:any,selectedFileList){
//process the selectedFileList came after the readFile has finished erading the files
}
i want to execute the submitTC function after the fileReader has finished reading the files..not sure if readFile() callback is implemented correctly.Please help writing this logic.
Flow> When user clicks submitTCtoDB is called and then readFile should work and read the files and return the selectedFileList after splicing the unwanted elements from it..then submitTC will take that list and proceed further.
Please help.

You should use es6 arrow function => instead of function declarations to get the instance of the current class method.
submitTCtoDB(updateTagForm:any){
for(let i=0;i<this.selectedFileList.length;i++){
let file=this.selectedFileList[i];
this.readFile(file, (selectedFileList) => {
this.submitTC(updateTagForm,selectedFileList);
});
}
}
To know whether the file reading is completed or not you can use FileReader.result property. It is null until the file reading completed it's process.
According to mozilla docs
An appropiate string or ArrayBuffer based on which of the reading methods was used to initiate the read operation. The value is null if the reading is not yet complete or was unsuccessful.

Related

How to use FileReader.readAsText() synchronously?

I am trying to read a CSV file using FileReader.readAsText() in JavaScript. But I am not able to get the value synchronously. I tried multiple approaches. But none is working. Below is the code I have so far:
1st Approach:
<input
id = "inputfile"
type = "file"
name = "inputfile"
onChange = {uploadFile} >
const uploadFile = (event: React.ChangeEvent < HTMLInputElement > ) => {
let resultSyncOutput = '';
connst files = event?.target.files;
if (files && files.length > 0) {
readAsTextCust(files[0]).then(resultStr => {
resultSyncOutput = resultStr;
});
}
// At this line I am not able to get the value of resultSyncOutput with the content of file sychronously
//Do something with the result of reading file.
someMethod(resultSyncOutput);
}
async function readAsTextCust(file) {
let resultStr = await new Promise((resolve) => {
let fileReader = new FileReader();
fileReader.onload = (e) => resolve(fileReader.result);
fileReader.readAsText(file);
});
console.log(resultStr);
return resultStr;
}
This is the first approach I tried ie, using async/await. I also tried to do it without aysnc/await and still was not able to succeed. It is critical for this operation to be synchronous.
NB: I checked a lot of answers in Stack Overflow and none provides a solution to this problem. So please do not mark this as duplicate.
Answer to this particualr question is not provided anywhere

How to read file using FileReader.readAsText synchronously in JavaScript?

I am trying to read a CSV file using FileReader.readAsText() in JavaScript. But I am not able to get the value synchronously. I tried multiple approaches. But none is working. Below is the code I have so far:
1st Approach:
<input
id = "inputfile"
type = "file"
name = "inputfile"
onChange = {uploadFile} >
const uploadFile = (event: React.ChangeEvent < HTMLInputElement > ) => {
let resultSyncOutput = '';
connst files = event?.target.files;
if (files && files.length > 0) {
readAsTextCust(files[0]).then(resultStr => {
resultSyncOutput = resultStr;
});
}
// At this line I am not able to get the value of resultSyncOutput with the content of file sychronously
//Do something with the result of reading file.
someMethod(resultSyncOutput);
}
async function readAsTextCust(file) {
let resultStr = await new Promise((resolve) => {
let fileReader = new FileReader();
fileReader.onload = (e) => resolve(fileReader.result);
fileReader.readAsText(file);
});
console.log(resultStr);
return resultStr;
}
This is the first approach I tried ie, using async/await. I also tried to do it without aysnc/await and still was not able to succeed. It is critical for this operation to be synchronous. Also use of Ajax is not allowed in project.
NB: I checked a lot of answers in Stack Overflow and none provides a solution to this problem. So please do not mark this as duplicate. Answer to this particular question is not provided anywhere.
Please help me even if this looks simple to you
I found the solution resultSyncOutput = await readAsTextCust(files[0]); and declaring the calling function as async worked.
You need to set a callback function for the onload event callback to get the results. Also notice that you need to call readAsText on the uploaded CSV file.
You can use FileReader API inside input's onChange callback this way:
<input
id = "inputfile"
type = "file"
name = "inputfile"
onChange = function (event: React.ChangeEvent<HTMLInputElement>) {
const reader = new FileReader();
reader.onload = (e) => {
console.log(e.target?.result) // this is the result string.
};
reader.readAsText(event.target.files?.[0] as File);
};
No need for async/await as well.

How to read a file with JavaScript to WebAssembly?

How can I pass a File to be read within the WebAssembly memory context?
Reading a file in the browser with JavaScript is easy:
<input class="file-selector" type="file" id="files" name="files[]" />
I was able to bootstrap WebAssembly code written in Rust with the crate stdweb, add an event listener to the DOM element and fire up a FileReader:
let reader = FileReader::new();
let file_input_element: InputElement = document().query_selector(".file-selector").unwrap().unwrap().try_into().unwrap();
file_input_element.add_event_listener(enclose!( (reader, file_input_element) move |event: InputEvent| {
// mystery part
}));
In JavaScript, I would get the file from the element and pass it to the reader, however, the API of stdweb needs the following signature:
pub fn read_as_array_buffer<T: IBlob>(&self, blob: &T) -> Result<(), TODO>
I have no idea how to implement IBlob and I am sure that I am missing something obvious either with the stdweb API or in my understanding of WebAssembly/Rust. I was hoping that there is something less verbose than converting stuff to UTF-8.
It works when the FileReader itself is passed from JavaScript to WebAssembly. It also seems like a clean approach because the data has to be read by the JavaScript API anyway - no need to call JS from WASM.
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Read to wasm</title>
</head>
<body>
<input type="file" id="file-input"/>
<script src="reader.js"></script>
<script>
var fileReader = new FileReader();
fileReader.onloadend = e => Rust.reader
.then(reader=> {
window.alert(reader.print_result(fileReader));
});
var fileInputElement = document.getElementById("file-input");
fileInputElement.addEventListener("change", e => fileReader.readAsText(fileInputElement.files[0]));
</script>
</body>
</html>
main.rs
#![feature(proc_macro)]
#[macro_use]
extern crate stdweb;
use stdweb::js_export;
use stdweb::web::FileReader;
use stdweb::web::FileReaderResult;
#[js_export]
fn print_result(file_reader: FileReader) -> String {
match file_reader.result() {
Some(value) => match value {
FileReaderResult::String(value) => value,
_ => String::from("not a text"),
}
None => String::from("empty")
}
}
fn main() {
stdweb::initialize();
stdweb::event_loop();
}
The following code is what I use to interact with another javascript library to read a sql file all without using javascript. This is based on the wasm-bindgen library, and I believe may be helpful to newer folks stumbling onto this answer.
[wasm_bindgen]
pub fn load_accounts_from_file_with_balances(file_input : web_sys::HtmlInputElement) {
//Check the file list from the input
let filelist = file_input.files().expect("Failed to get filelist from File Input!");
//Do not allow blank inputs
if filelist.length() < 1 {
alert("Please select at least one file.");
return;
}
if filelist.get(0) == None {
alert("Please select a valid file");
return;
}
let file = filelist.get(0).expect("Failed to get File from filelist!");
let file_reader : web_sys::FileReader = match web_sys::FileReader::new() {
Ok(f) => f,
Err(e) => {
alert("There was an error creating a file reader");
log(&JsValue::as_string(&e).expect("error converting jsvalue to string."));
web_sys::FileReader::new().expect("")
}
};
let fr_c = file_reader.clone();
// create onLoadEnd callback
let onloadend_cb = Closure::wrap(Box::new(move |_e: web_sys::ProgressEvent| {
let array = js_sys::Uint8Array::new(&fr_c.result().unwrap());
let len = array.byte_length() as usize;
log(&format!("Blob received {}bytes: {:?}", len, array.to_vec()));
// here you can for example use the received image/png data
let db : Database = Database::new(array);
//Prepare a statement
let stmt : Statement = db.prepare(&sql_helper_utility::sql_load_accounts_with_balances());
stmt.getAsObject();
// Bind new values
stmt.bind();
while stmt.step() { //
let row = stmt.getAsObject();
log(&("Here is a row: ".to_owned() + &stringify(row).to_owned()));
}
}) as Box<dyn Fn(web_sys::ProgressEvent)>);
file_reader.set_onloadend(Some(onloadend_cb.as_ref().unchecked_ref()));
file_reader.read_as_array_buffer(&file).expect("blob not readable");
onloadend_cb.forget();
}
I managed to access the file object and pass it to the FileReaderin the following way:
let reader = FileReader::new();
let file_input_element: InputElement = document()
.query_selector(".file-selector")
.unwrap()
.unwrap()
.try_into()
.unwrap();
file_input_element.add_event_listener(
enclose!( (reader, file_input_element) move |event: InputEvent| {
let file = js!{return #{&file_input_element}.files[0]};
let real_file: stdweb::web::Blob = file.try_into().unwrap();
reader.read_as_text(&real_file);
}
This code compiles. However, the data never gets available via reader.result().

Javascript testing with mocha the html5 file api?

I have simple image uploader in a website and a javascript function which uses FileReader and converts image to base64 to display it for user without uploading it to actual server.
function generateThumb(file) {
var fileReader = new FileReader();
fileReader.readAsDataURL(file);
fileReader.onload = function (e) {
// Converts the image to base64
file.dataUrl = e.target.result;
};
}
Now I am trying to write tests for this method using Mocha and Chai. My thinking is that I want to check if the file.dataUrl was successfully created and it is base64. So I would like to mock the local file somehow in testing environment (not sure how to do this). Or I should not test this at all and assume that this is working ?
The answer here depends a little. Does your "generateThumbs" method have other logic than loading a files contents and assign that to a property of a passed in object? Or does it have other logic such as generating the thumbnail from the image data, reading out file properties and assigning them to the file object? and so on?
If so then I would infact suggest you mock out the FileReader object with your own, so that you can control your test results. However, it isn't the FileReaders functionality you want to test, it is your own logic. So you should assume that FileReader works and test that your code that depends upon it works.
Now since the method you posted was a bit on the small side, In that case I would just assume it works, rename the method and work on testing the rest of your code. But there is a place for having such a mock, and I must admit it was quite fun to figure out how to mock the event target, so I will give an example here, using your method as a basis:
//This implements the EventTarget interface
//and let's us control when, where and what triggers events
//and what they return
//it takes in a spy and some fake return data
var FakeFileReader = function(spy, fakeData) {
this.listeners = {};
this.fakeData = fakeData;
this.spy = spy;
this.addEventListener('load', function () {
this.spy.loaded = true;
});
};
//Fake version of the method we depend upon
FakeFileReader.prototype.readAsDataURL = function(file){
this.spy.calledReadAsDataURL = true;
this.spy.file = file;
this.result = this.fakeData;
this.dispatchEvent({type:'load'}); //assume file is loaded, and send event
};
FakeFileReader.prototype.listeners = null;
FakeFileReader.prototype.addEventListener = function(type, callback) {
if(!(type in this.listeners)) {
this.listeners[type] = [];
}
this.listeners[type].push(callback);
};
FakeFileReader.prototype.removeEventListener = function(type, callback) {
if(!(type in this.listeners)) {
return;
}
var stack = this.listeners[type];
for(var i = 0, l = stack.length; i < l; i++) {
if(stack[i] === callback){
stack.splice(i, 1);
return this.removeEventListener(type, callback);
}
}
};
FakeFileReader.prototype.dispatchEvent = function(event) {
if(!(event.type in this.listeners)) {
return;
}
var stack = this.listeners[event.type];
event.target = this;
for(var i = 0, l = stack.length; i < l; i++) {
stack[i].call(this, event);
}
};
// Your method
function generateThumb(file, reader){
reader.readAsDataURL(file);
reader.addEventListener('load', function (e) {
file.dataUrl = base64(e.target.result);
});
}
Now we have a nice little object that behaves like a FileReader, but we control it's behavior, and we can now use our method and inject it into, thus enabling us to use this mock for testing and the real thing for production.
So we can now write nice unit tests to test this out:
I assume you have mocha and chai setup
describe('The generateThumb function', function () {
var file = { src: 'image.file'};
var readerSpy = {};
var testData = 'TESTDATA';
var reader = new FakeFileReader(readerSpy, testData);
it('should call the readAsDataURL function when given a file name and a FileReader', function () {
generateThumb(file, reader);
expect(readerSpy.calledReadAsDataURL).to.be.true;
expect(readerSpy.loaded).to.be.true;
});
it('should load the file and convert the data to base64', function () {
var expectedData = 'VEVTVERBVEE=';
generateThumb(file, reader);
expect(readerSpy.file.src).to.equal(file.src);
expect(readerSpy.file.dataUrl).to.equal(expectedData);
});
});
Here is a working JSFiddle example:
https://jsfiddle.net/workingClassHacker/jL4xpwwv/2/
The benefits here are you can create several versions of this mock:
what happens when correct data is given?
what happens when incorrect data is given?
what happens when callback is never called?
what happens when too large of a file is put in?
what happens when a certain mime type is put in?
You can offcourse massively simplify the mock if all you need is that one event:
function FakeFileReader(spy, testdata){
return {
readAsDataURL:function (file) {
spy.file = file;
spy.calledReadAsDataURL = true;
spy.loaded = true;
this.target = {result: testdata};
this.onload(this);
}
};
}
function generateThumb(file, reader){
reader.onload = function (e) {
file.dataUrl = base64(e.target.result);
};
reader.readAsDataURL(file);
}
Which is how I would actually do this. And create several of these for different purposes.
Simple version:
https://jsfiddle.net/workingClassHacker/7g44h9fj/3/

How can I use chrome.downloads.onDeterminingFilename to change downloaded file names if the files have extension of jpg or png?

How can I use chrome.downloads.onDeterminingFilename to change downloaded file names if the files have extension of JPG or PNG?
I am looking at the example here:
chrome.downloads.onDeterminingFilename.addListener(function(item, __suggest) {
function suggest(filename, conflictAction) {
__suggest({filename: filename,
conflictAction: conflictAction,
conflict_action: conflictAction});
}
var rules = localStorage.rules;
try {
rules = JSON.parse(rules);
} catch (e) {
localStorage.rules = JSON.stringify([]);
}
for (var index = 0; index < rules.length; ++index) {
var rule = rules[index];
if (rule.enabled && matches(rule, item)) {
if (rule.action == 'overwrite') {
suggest(item.filename, 'overwrite');
} else if (rule.action == 'prompt') {
suggest(item.filename, 'prompt');
} else if (rule.action == 'js') {
eval(rule.action_js);
}
break;
}
}
});
This is confusing. How does chrome.downloads.onDeterminingFilename from above detect the name of the file? And once it detects, how did it change the file? Can anyone break down what these codes mean above?
Ref: http://developer.chrome.com/extensions/samples
As soon as the filename is determined onDeterminingFilename event
is triggered upon which a callback function is called and it takes 2
parameters
item object which contains data such as download id, url, filename, referrer etc. (see
https://developer.chrome.com/extensions/downloads#type-DownloadItem)
__suggest which must be called to pass suggestion either synchronously or asynchronously.
A suggest function is defined which will be used to call __suggest
based on the specific rule
All the rules are accessed from the local memory and a for loop runs
to iterate across them.
For each iteration based on the action data in the rule the
suggest function is called with specific filename and
conflictAction.
Basically the filename is aceessed using item.filename and a new filename is suggested by calling __suggest where the value for the key filename contains the new filename.

Categories

Resources