Lag by taking an instance of an array using es6 - javascript

I got an empty array while I'm logging for an instance of an array!
onSelectMultipleImage = (event): Promise<any> => {
return new Promise(resolve => {
const files = <File[]>event.target.files;
let file: File;
let counter = -1;
const response = [];
while (file = files[++counter]) {
const reader: FileReader = new FileReader();
reader.onload = ((f) => {
return () => {
response.push({
file: f,
base64: reader.result
})
}
})(file);
reader.readAsDataURL(file);
console.log(counter);
if(counter == files.length -1) {
console.log('inside the while');
resolve(response);
}
}
});
};
onImagesSelect = async (event: Event) => {
this.newImages = await this.helper.onSelectMultipleImage(event) || [];
console.log(this.newImages, "Original"); // [file: File, base64: "base 64 long string..."]
console.log([...this.newImages],"instance"); // [] empty array
setTimeout(() => {console.log([...this.newImages, 'instance'])}, 2000); // [file: File, base64: "base 64 long string..."]
}
So why I'm getting the presented result? It's something causing by the base64 presented inside the array? if yes what is the solution?

It doesn't wait reader.onload to be completed. So resolve(response) is called before response.push.
You can create a promise to read one file and return them with Promise.all like following code.
readFile = (file: File): Promise<any> => {
return new Promise(resolve => {
const reader: FileReader = new FileReader();
reader.onload = (f) => {
resolve({
file: f,
base64: reader.result
});
};
reader.readAsDataURL(file);
})
}
onSelectMultipleImage = (event): Promise<any> => {
const files = <File[]>event.target.files;
return Promise.all(files.map(file => readFile(file)));
};

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

File uploader crashing for files larger than 300kb when using readAsDataURL on FileReader object

I am currently using the following method to convert files to base64 which crashes the browser(chrome) or abandons the job(firefox) when uploading larger files:
export const convertFilesToBase64: (
files: File[]
) => Promise<Base64FileObject[]> = async (files: File[]) => {
const toBase64 = async (file: File) =>
await new Promise((resolve, reject) => {
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => resolve(reader.result)
reader.onerror = (error) => reject(error)
})
if (files.length > 0) {
return await Promise.all(
files.map(async (file: File) => {
const database64 = (await toBase64(file)) as string
return {
filename: file.name,
url: URL.createObjectURL(file),
size: file.size,
dataBase64: database64.replace(/^data:.*\/.*,/, ''),
}
})
)
} else {
return []
}
}
Smaller files seem to work without any issues. It currently seems to fail on the reader.readAsDataURL(file) line. Not sure if I am missing something obvious here?

blob to base64 conversion synchrorously in Javascript

I am calling a javascript function to initialize a variable value in Oracle Visual Builder (VBCS). The function takes the binary data as input and needs to return Base64 converted string synchronously so that the Base64 converted string is assigned to the VBCS variable.
The function does not return the Base64 converted string. How do I make it return the Base64 string to the calling function?
PageModule.prototype.convertbase64 = function (data) {
const blob = new Blob([data], {
type: "application/octet-stream"
});
function blobToBase64(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(blob);
reader.onloadend = () => resolve(reader.result.toString().substr(reader.result.toString().indexOf(',') + 1));
reader.onerror = error => reject(error);
console.log(new Date());
});
};
const retstring = blobToBase64(blob).then(finalString => { return finalString });
console.log('retstring value', retstring);
return retstring;
};
The problem is in this line
const retstring = blobToBase64(blob).then(finalString => { return finalString });
You should wait for the result of blobToBase64 and make function convertbase64 async.
const retstring = await blobToBase64(blob);
Here is full example.
PageModule.prototype.convertbase64 = async function (data) {
const blob = new Blob([data], {
type: "application/octet-stream"
});
function blobToBase64(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(blob);
reader.onloadend = () => resolve(reader.result.toString().substr(reader.result.toString().indexOf(',') + 1));
reader.onerror = error => reject(error);
console.log(new Date());
});
};
const retstring = await blobToBase64(blob);
console.log('retstring value', retstring);
return retstring;
};

Check when all images are done uploading?

I have an array of objects. In each object there are an array of files..
Im looping through all files and uploading them one by one, which works as expected.. However, i want to show a "success" modal when all files from each object are done uploading..
Im struggling a bit here... The code I have so far:
Im thinking im doing something wrong when I do the check on the:
if (allFiles.length === filesToQuestions.length) {
triggerUploadFiles() {
let allFiles = [];
let filesToQuestions = this.filesToQuestions;
filesToQuestions.forEach((item) => {
let files = item.images;
let payload = {
instanceId: item.instanceId,
answerId: item.answerId,
path: item.path,
fileType: item.fileType,
optionId: item.optionId
};
if (files.length > 0) {
files.map(async(file) => {
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = async(e) => {
// Make a fileInfo object for storing later
let fileInfo = {
questionId: this.questionId,
name: file.name,
type: file.type,
data: e.target.result,
file: file
};
fileInfo.type.includes('video') ? fileInfo.type = 'video' : fileInfo.type = 'image';
if (payload.instanceId && payload.path) {
const {response} = await uploadFileToCloudinary(fileInfo.data, payload);
try {
this.$store.dispatch('surveys/submitFilesToSurvey', {
instanceId: payload.instanceId,
answerId: payload.answerId,
fileName: response.public_id,
type: fileInfo.type,
optionId: payload.optionId
}).then((response) => {
console.log('file submitted', response);
allFiles.push(fileInfo);
});
} catch (e) {
console.log('could not upload file');
}
}
// If all files have been proceed
if (allFiles.length === filesToQuestions.length) {
const delayForCompletedStatus = 2000;
deletePendingSurveyByID(this.tempSurveyId);
setTimeout(() => {
this.isUploadingFiles = false;
this.$store.dispatch('modals/toggleModal', 'showModalSurveyCreated');
}, delayForCompletedStatus);
}
};
});
}
});
}
You can use Promise.all() to check if all asynchronous events are finished.
The Promise.all() method returns a single Promise that fulfills when all of the promises passed as an iterable have been fulfilled or when the iterable contains no promises. It rejects with the reason of the first promise that rejects.
Promise.all(files.map((file) => {
return new Promise((resolve, reject) => {
...
reader.onload = e => {
// upload file
...
resolve();
}
...
});
})
.then() {
// all asynchronous events are finished!
}
FYI, I added a simple example of using Promise.all.
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('first'), 1000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => resolve('second'), 2000);
});
const promiseList = [promise1, promise2];
Promise.all(promiseList).then(function(values) {
console.log(values);
});

How to return base64 data from a fetch promise?

I want to convert images from a URL to base 64, and store that in a state for later use.
How do I return the data from fetch()?
I can get it to work using CORS safe images and an HTML canvas, but this will not work for public domains.
openImage = (index) => {
const url = formatURLs[index];
const base64 = this.getBase64Image(url);
this.setState(prevState => ({
currentImage: index,
currentImagebase64: base64
}));
}
getBase64Image(url) {
fetch(url).then(r => r.blob()).then(blob => {
var reader = new FileReader();
reader.onload = function() {
var b64 = reader.result.replace(/^data:.+;base64,/, '');
return b64;
};
reader.readAsDataURL(blob);
});
}
When I console.log(reader.result), it will output base64 as expected.
However when I return b64, it returns to openImage as 'undefined'.
getBase64Image is async so when calling it in a sync way it will return undefined.
You can try something like that
openImage = async (index) => {
const url = formatURLs[index];
const base64 = await this.getBase64Image(url);
this.setState(prevState => ({
currentImage: index,
currentImagebase64: base64
}));
}
async getBase64Image(url) => {
const response = await fetch(url);
const blob = await response.blob();
const reader = new FileReader();
await new Promise((resolve, reject) => {
reader.onload = resolve;
reader.onerror = reject;
reader.readAsDataURL(blob);
});
return reader.result.replace(/^data:.+;base64,/, '')
}
You have to set state inside .then()
getBase64Image(url) {
fetch(url).then(r => r.blob()).then(blob => {
var reader = new FileReader();
reader.onload = function() {
var b64 = reader.result.replace(/^data:.+;base64,/, '');
this.setState({
currentImagebase64: b64
});
};
reader.readAsDataURL(blob);
});
}

Categories