How to manually trigger file reader onerror event? - javascript

I'm testing all the scenarios on reading files using new FileReader();
When the read is done, onload() should be triggered.
I have no idea how to trigger onerror() callback
Any idea?
var promise = Promise.create();
var fileReader = new FileReader();
fileReader.onload = function () {
};
fileReader.onerror = function(event) {
// I want to stay here ..........
// Who can tell me how to trigger this onerror callback?
debugger
};
fileReader.readAsText(inputFile);
return promise;

Try simply calling abort:
var promise = Promise.create();
var fileReader = new FileReader();
fileReader.onload = function () {
fileReader.abort()
};
fileReader.onerror = function(event) {
// I want to stay here ..........
// Who can tell me how to trigger this onerror callback?
debugger
};
fileReader.readAsText(inputFile);
return promise;
If that doesn't work you can try a permissions error with something like:
touch testfile
chmod 000 testfile
And open that file from your reader.

Maybe what you're trying to find out is how to use Async functions:
async function FileReader(){
// your code for the file reading
return;
};
then use await in your onload() function to wait for it to return:
async function onload(){
await firstFunction(); // This waits for fileReader() function to finish...
// do what you want to show when fileReading is done
};
Hope this helps!

You can run your code in debugger mode and add a breakpoint at line "fileReader.readAsText(inputFile)". When the debugger stops, you rename manually the file in your filesystem. FileReader then throws an error.
The accepted answer doesn't work in my environment.

Related

How to use Clipboard API to write image to clipboard in Safari

The following code (adapted from here) successfully writes an image file to the clipboard upon a button click in Chrome:
document.getElementById('copy-button').addEventListener('click', async () => {
try {
const data = await fetch('image.png')
const blob = await data.blob()
await navigator.clipboard.write(
[new ClipboardItem({[blob.type]: blob})]
)
console.log('success')
} catch (err) {
console.log(`${err.name}: ${err.message}`)
}
})
(Similar code also works with chaining the promises with .then() or copying the contents of a <canvas> using .toBlob() with a callback function)
However, this fails in Safari, throwing a NotAllowedError. I suspect this is something to do with the asynchronous making of the blob causing Safari think that the call to write() is 'outside the scope of a user gesture (such as "click" or "touch" event handlers)' as described here, since control is released from the event handler during the await portions.
For example, the following code pre-loads the blob into a global variable when the script first runs, and the call to write() does not need to wait for any other async code to finish executing:
let imageBlob
(async function () {
const data = await fetch('image.png')
const blob = await data.blob()
imageBlob = blob
console.log('Image loaded into memory')
})()
document.getElementById('image-button-preload').addEventListener('click', () => {
const clipItem = new ClipboardItem({[imageBlob.type]: imageBlob})
navigator.clipboard.write([clipItem]).then( () => {
console.log('success')
}, (reason) => {
console.log(reason)
})
})
But this is clearly not ideal, especially if the image data is something dynamically created (e.g. in a canvas).
So, the question: How can I generate an image blob and write this to the clipboard upon a user action which Safari/webkit will accept? (Or, is this a bug in Safari/webkit's implementation of the API)
The solution (for safari) is to assign a Promise to the value of the hashmap you pass into ClipboardItem like this:
document.getElementById('copy-button').addEventListener('click', async () => {
try {
const makeImagePromise = async () => {
const data = await fetch('image.png')
return await data.blob()
}
await navigator.clipboard.write(
[new ClipboardItem({[blob.type]: makeImagePromise() })]
)
console.log('success')
} catch (err) {
console.log(`${err.name}: ${err.message}`)
}
})
That way you're calling clipboard.write without awaiting, and Safari will await the promise for you that generates the image.
Note: Other browsers may not support passing a promise to ClipboardItem, so you'll likely want to check if the UserAgent contains Mac or iOS in it before doing this.

FileReader is not being fired on a web worker

I have the below function that convert pdfs to images, the function is within a web worker.
For some reason the fileReader.onload is not being fired, the filePdf is correct and is on the right format. Any idea?
const processFile = async (filePdf, post) => {
let PDFJS
if (!PDFJS) {
PDFJS = await import('pdfjs-dist/build/pdf.js')
}
if (!filePdf) return
const fileReader = new FileReader()
console.log(filePdf)
let pages
try {
fileReader.onload = async () => {
const pdf = await PDFJS.getDocument(fileReader.result).promise
pages = await pdfToImageMap(pdf)
}
} catch (e) {
console.log({e})
}
fileReader.readAsArrayBuffer(filePdf)
return post({type: 'done'})
}
filePdf:
Try to change your logic.
At the moment you are trying to wait for the onload, which will work. So the try block succeeds. Then you return your post function. So you've run the file reader, but didn't wait for it load and returned with the post function.
Instead wait for the fileReader to load by awaiting a promise wrapped around the load function. And inside the Promise call fileReader.readAsArrayBuffer(filePdf) to make sure that the onload function is called. In the onload function use your try / catch block to use your PDFJS framework.
Also, don't waste any values stored in variables. If the pages value is something you need, then use it and return it somehow. Otherwise don't store the value and discard it.
Try the snippet below and see if it works.
const processFile = async (filePdf, post) => {
const PDFJS = await import('pdfjs-dist/build/pdf.js')
if (!filePdf) return
console.log(filePdf)
const fileReader = new FileReader()
const pages = await new Promise(resolve => {
fileReader.onload = async () => {
try {
const pdf = await PDFJS.getDocument(fileReader.result).promise
const pages = await pdfToImageMap(pdf)
resolve(pages)
} catch (e) {
console.log({e})
}
}
fileReader.readAsArrayBuffer(filePdf)
})
return post({type: 'done', pages})
}

JavaScript Onload property

I have read some posts regarding the onload property. My understanding is that it is not a method, not an event, event listener, or trigger. It is merely a empty pointer to a function waiting to be assigned. Here is a shortened script. It assigns a image manipulation function upon event then renders the image to the web page. Instead of assign the function to onload which is the pointer to the function. Why can't I just execute the function directly? Am I making any sense here? Thanks
var reader = new FileReader();
reader.onload = function(e) {
var img = document.createElement("img");
img.src = e.target.result;
var canvas = document.createElement("canvas");
......
dataurl = canvas.toDataURL(file.type);
document.getElementById('output').src = dataurl;
}
reader.readAsDataURL(file);
Accordingly to MDN:
The FileReader.onload property contains an event handler executed when
the load event is fired, when content read with readAsArrayBuffer,
readAsBinaryString, readAsDataURL or readAsText is available.
So it's executed after reading content is done, that you're performing by calling reader.readAsDataURL(file).
onload is a field of the object FileReader which handles a reference to the function that you wanna execute when the desired file gets loaded. Essentially, it's the callback that gets called when the event load is triggered. Why using this pattern and not executing the function directly? Because loading files is an asynchronous task (which is initialized with readAsDataURL, readAsBinaryString, readAsArrayBuffer or readAsText).
You can't call the function directly because files load asynchronously. You can convert the FileReader code to return a promise though and then you can use async/await to make the code look like you're directly calling it
funciton readFileAsDataURL(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(e.target.result);
reader.onerror = reject;
reader.readAsDataURL(file);
};
}
function loadImage(url) {
return new Promise((resolve, reject) => {
const img = new Image():
img.onload = () => resolve(img);
img.onerror = reject;
img.src = url;
};
}
async function foo() {
const result = await readFileAsDataURL(file);
const img = await loadImage(result);
// you can now use img
......
}
So in essence, OnLoad is an event handler (or reference to the event handler) for FileReader when it is called to further processes data when FileReader is done loading? Is my interpretation correct?

File Reader is slow and not set state on React

When converting a. pdf file to Base64, I need to be filled in my state, but for some reason my conversion is slower than my setstate, which when being seted is always empty.
My code
async transformBase64(inputFile) {
return new Promise((resolve, reject) => {
var fileReader = new FileReader();
fileReader.readAsDataURL(inputFile)
if (fileReader.result != undefined){
resolve(this.setState({ Base64: fileReader.result }), () => {});
}else{
reject("Err")
}
});
}
What can I do to solve my problem?
It looks like you're not setting up an onload callback for your fileReader. This is needed to correctly signal back to your application that file data has loaded and is ready, seeing that the FileReader API is asynchronous.
Consider apply the following changes to your code to resolve your problem:
async transformBase64(inputFile) {
return new Promise((resolve, reject) => {
var fileReader = new FileReader();
// If error occurs, reject the promise
fileReader.onerror = () => {
reject("Err")
}
// Define an onload handler that's called when file loaded
fileReader.onload = () => {
// File data loaded, so proceed to call setState
if (fileReader.result != undefined){
resolve(this.setState({
Base64: fileReader.result
}), () => {});
}else{
reject("Err")
}
}
fileReader.readAsDataURL(inputFile)
});
}

JS Wait for few events in a function to complete

I have a function which uploads images and displays it on the page. How should I do to wait for the function to complete?
$("#images").change(function(){
updateArray(this);
addImages();
});
function updateArray(inp)
{
for(var i=0,file; file = inp.files[i]; i++)
{
var reader = new FileReader();
reader.onload = function (evt)
{
images[images.length] = evt.target.result;
}
reader.readAsDataURL(file);
}
}
I want to call addImages() after all events in updateArray() are completed
I would use jQuery's deferred/promises here. This will allow you to wait until each onload is complete, and then trigger a callback.
Here's an example:
function updateArray(inp)
{
var promises = [];
for(var i=0,file; file = inp.files[i]; i++)
{
var reader = new FileReader(),
d = new $.Deferred();
promises.push(d);
// Make sure we "capture" the correct 'd'
(function(d){
reader.onload = function (evt)
{
images.push(evt.target.result);
d.resolve();
}
}(d));
reader.readAsDataURL(file);
}
return $.when.apply($, promises);
}
This will create a new deferred object for each FileReader. When each one finishes, its respective deferred will be resolved. The $.when is used to combine all of the deferreds into one. So, in your .change(), you can simply do this:
$("#images").change(function(){
updateArray(this).done(function(){
addImages();
});
});
P.S. You can also pass parameters to resolve() to be used in the .done() callback. So, instead of appending to the images array, you can do d.resolve(evt.target.result);.
(function(d){
reader.onload = function (evt)
{
d.resolve(evt.target.result);
}
}(d));
Then in .done(), you can do:
$("#images").change(function(){
updateArray(this).done(function(){
// Each '.resolve' added a new parameter to here, let's get them all
images = [].slice.call(arguments, 0);
addImages();
});
});
There are a couple of ways that you can approach this, however if you're using jQuery (which you appear to be) I'd recommend looking into promises (see: http://api.jquery.com/promise/).
They are a more modern paradigm for deferred callbacks which have some semantic processing for executing functions based on successful completion, failure, and those which will always process.

Categories