For what reasons can reader.readAsArrayBuffer fail? - javascript

I am using the Filereader
const arrayBufferPromiseFromBlob = function (blob) {
//argument must be blob or file Object
return new Promise(function (resolve, reject) {
const reader = new FileReader();
reader.onload = function (event) {
resolve(reader.result);
};
reader.onerror = function (error) {
reject(error);
};
reader.readAsArrayBuffer(blob);
});
};
sometimes it rejects (onerror), even though it was given a valid blob, what can be the reason this happens ?

In the calling code using arrayBufferPromiseFromBlob , attach a .catch() to the promise and log the error.

Related

Blob to Base64 in javascript not returning anything from FileReader

I am using FileReader in typescript to convert a blob to a base64 image that will then be displayed in the template of my application.
adaptResultToBase64(res: Blob): string {
let imageToDisplay : string | ArrayBuffer | null = '';
const reader = new FileReader();
reader.onloadend = function () {
imageToDisplay = reader.result;
return imageToDisplay;
};
reader.readAsDataURL(res);
return imageToDisplay;
}
Whilst the data logged inside the read.onloadend function displays the base64 string I cannot pass it out of the function.
I have tried adding a callback but where it is called elsewhere doesn't return anything but an empty string.
Please check this code
<input type="file" id="file">
<button id="click">click</button>
let data: string | ArrayBuffer;
document.getElementById('file').onchange = function (e: Event) {
let files: FileList | null = (<HTMLInputElement>e.target).files;
let reader: FileReader = new FileReader();
reader.onload = function (e: ProgressEvent<FileReader>) {
console.log(e.target.result);
data = e.target.result;
};
if (files.length > 0) {
reader.readAsDataURL(files?.[0]);
}
};
document.getElementById('click').onclick = function () {
console.log(data); // result if present otherwise null is returned
};
Using a separate method view. The return value is a Promise.
function adaptResultToBase64(res: Blob): Promise<string> {
let reader: FileReader = new FileReader();
return new Promise((resolve, reject) => {
reader.onloadend = () => {
resolve(reader.result as string);
}
reader.onerror = () => {
reject("Error reading file.");
}
reader.readAsDataURL(res);
})
}
To get the result
adaptResultToBase64(/* Blob value */)
.then(resp => console.log(resp))
.catch(error => console.log(error));
See here for specifics on Promise
MDN
learn.javascript.ru
The basic result I needed and did not realise that the reader.onload is actually a callback for read.readAsDataUrl and finishes everything inside it async.
adaptResultToBase64(res:Blob){
const reader = new FileReader();
reader.onload = function () {
// Was missing code here that needed to be called asynchronously.
adapterToNewObject(reader.result.toString())
};
reader.readAsDataURL(res);
}
}
I was performing this in Angular so for anyone else who runs into this problem here it is using Angular syntax:
In your class:
export class Component {
adaptedResult:Result
getBase64() {
this.http.get().subscribe((result: Blob) => {
const reader = new FileReader();
reader.onload = () => {
this.adaptedResult = this.adapter(reader.result) // Assign or use reader.result value, this is an example of using an adapter function.
};
reader.readAsDataURL(result);
});
}
adapter(base64:string){
return {
name:'image',
image:base64'
}
}
}

Problem with async/await with FileReader in JavaScript

I'm using FileReader in a Vue.js project, and I have a problem with this code:
async uploadDocuments(files) {
for (let file of files) {
let fileName = file.name;
let fileContent;
let reader = new FileReader();
reader.onload = async () => {
fileContent = reader.result;
await this.submitFile(fileContent, fileName, fileType)
.then(/* THEN block */)
.catch(/* CATCH block */);
};
reader.onerror = (error) => { console.warn(error); };
reader.readAsDataURL(file);
}
console.log("COMPLETED");
}
async submitFile(fileContent, fileName, fileType) {
// this function should handle the file upload and currently contains a timeout
await new Promise((resolve) => setTimeout(resolve, 3000));
}
This is the desired execution order (example with two files):
(wait 3s)
THEN block (file 1)
(wait 3s)
THEN block (file 2)
COMPLETED
But this is the actual execution order:
COMPLETED
(wait 3s)
THEN block
THEN block
The "THEN block" is correctly executed after the timeout, but the execution of the code in the for loop continues without waiting the execution of the onload function.
How can I make the reader asynchronous? I tried many solutions (such as wrapping the for loop in a promise and putting the resolve() function inside .then()) but none of them works.
I'd recommend to "promisify" the Reader thing and then use Promise.all until all the files are uploaded.
uploadDocuments = async (event, files) => {
const filePromises = files.map((file) => {
// Return a promise per file
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = async () => {
try {
const response = await this.submitFile(
reader.result,
file.name,
fileType
);
// Resolve the promise with the response value
resolve(response);
} catch (err) {
reject(err);
}
};
reader.onerror = (error) => {
reject(error);
};
reader.readAsDataURL(file);
});
});
// Wait for all promises to be resolved
const fileInfos = await Promise.all(filePromises);
console.log('COMPLETED');
// Profit
return fileInfos;
};
Try this.
Add await before the reader.onload it will hold until then or catch block execute successfully
async uploadDocuments(event) {
for (let file of files) {
let fileName = file.name;
let fileContent;
let reader = new FileReader();
await reader.onload = async () => {
fileContent = reader.result;
await this.submitFile(fileContent, fileName, fileType)
.then(/* THEN block */)
.catch(/* CATCH block */);
};
reader.onerror = (error) => { console.warn(error); };
reader.readAsDataURL(file);
}
console.log("COMPLETED");
}
async submitFile(fileContent, fileName, fileType) {
// this function should handle the file upload and currently contains a timeout
await new Promise((resolve) => setTimeout(resolve, 3000));
}```

Rxjs chain to convert observable<File> to observable<string> (base64)

I have a working code to convert my File object to base64:
let reader = new FileReader();
reader.readAsDataURL(myFile);
reader.onload = () => {
let resultStrOrArrayBuf = reader.result;
if (!(resultStrOrArrayBuf instanceof ArrayBuffer)) {
..do something with resultStrOrArrayBuf
}
};
However I do now have to integrate this part to an existing rxjs chain. In the chain I receive the File object and would like to go on with the base64 result of the conversion. However the conversion is done with the help of the onload event. Is there some way to convert this event to a new observable and pass this to the chain?
there is no out of the box method to convert this loaded event to rxjs observable. you will have to make your own operator.
export const dataUrlToObs = myFile => new Observable<string | ArrayBuffer>(subscriber => {
const reader = new FileReader();
reader.readAsDataURL(myFile);
reader.onload = () => {subscriber.next(reader.result); subscriber.complete(); };
reader.onerror = () => subscriber.error(reader.error);
return () => reader.abort(); // cancel function in case you unsubscribe from the obs
}
it later can be used like this:
..chain
switchMap(myFile => dataUrlToObs(myFile)),
tap(resultStrOrArrayBuf => {
if (!(resultStrOrArrayBuf instanceof ArrayBuffer)) {
..do something with resultStrOrArrayBuf
}
})
Consider the following helper function, which accepts a Blob as a parameter and return an Observable<string>:
function blobToBase64(blob: Blob): Observable<string> {
return new Observable<string>(observer => {
const reader = new FileReader();
reader.onerror = observer.error;
reader.onabort = observer.error;
reader.onload = () => observer.next(reader.result as string);
reader.onloadend = observer.complete;
reader.readAsDataURL(blob);
return {
unsubscribe: reader.abort
}
})
}
Usage:
declare const fileObservable: Observable<File>;
fileObservable
.pipe(switchMap(blobToBase64))
.subscribe(base64 => console.log(base64))

JavaScript Promise with FileReader

I'm trying to return a split array when a user specifies a text file in an input box using promises, but the console.log keeps returning undefined, despite resolve actually finding the data.
I know im using the promise wrong but i just can't figure it out, any help would be very appreciated
class TextReader {
readFile (event) {
let file = event.target.files[0]
var promise = Promise.resolve()
pFileReader(file)
promise.then(function (result) {
console.log(result)
})
function pFileReader (file) {
return new Promise((resolve, reject) => {
var reader = new FileReader()
reader.onload = function found () {
resolve(reader.result)
}
reader.readAsText(file)
})
}
}
}
This is the code in my html
<input type='file' accept='text/plain' id="file" onchange='ValidateInput(event)'/>
function ValidateInput (event) {
let myTextReader = new TextReader()
let output = myTextReader.readFile(event)
}
A promise is returned by pFileReader and you need to resolve the returned Promise and not a new Promise
class TextReader {
readFile (event) {
let file = event.target.files[0]
var promise = pFileReader(file)
promise.then(function (result) {
console.log(result)
})
function pFileReader (file) {
return new Promise((resolve, reject) => {
var reader = new FileReader()
reader.onload = function found () {
resolve(reader.result)
}
reader.readAsText(file)
})
}
}
}

Reading file in React action

I am new in javascript and have problem with it's asynchronous behavior. I am trying to read file in React action. Important part looks like this:
if(file){
const reader = new FileReader();
reader.readAsDataURL(inquiryFile);
reader.onload = function() {
body = JSON.stringify({
fileToUpload: reader.result,
});
return dispatch(basicRequest(body));
};
reader.onerror = function(error) {
console.error('Error uploadingFile: ', error);
};
}
else{
return dispatch(basicRequest());
}
The component, which is responsible for calling this action needs to dispatch another action depending on either success or error result.
return submitFileAction(masterId, data).then((result) => {
if (!result.error) {
console.log('success');
} else {
console.log('error');
}
});
Problem is, that result returning to 'then part' is undefined and filereader.onload is called after I get error.
I would like to ask how to await result from filereader.
Thanks.
You probably want to wrap the FileReader into a Promise.
if (file) {
return new Promise(function(resolve, reject){
...
reader.onload = function(){
...
resolve(dispatch(basicRequest(body)));
};
reader.onerror = function(error) { reject(error); };
});
}
The error would be handled as:
return submitFileAction(masterId, data).then((result) => {
console.log('success');
}).catch((error) => {
console.log('error');
});
This presumes dispatch() also returns a Promise.

Categories