sending base64 value of file to api - javascript

I was struggling with a file upload I want to save via a PUT request. I updated the question with the solution.
I have a form that allows for multiple images to be uploaded in different fields, but also other fields with strings:
I loop over all fields in order to collect the information in a JSON, then push them towards my API:
keys is a dict with the names of the keys for the JSON (API checks for those keys), for example: name|STR, price|FLOAT, ppic|IMG.
$('#checkButton').on('click', function( event ) {
let form = document.querySelector('#productForm');
let nbr = document.querySelector('#productdata').getAttribute("pid")
let return_dict = {"number": nbr};
const pendings = ["name", "pic"].map(async function ( key ) {
return_dict["name"] = form.elements[0].value;
return_dict["pic"] = await img_2_b64(form.elements[1]);
});
Promise.all(pendings).then((values) => {
write_2_DB_with_ajax_call ( return_dict )
});
});
If the key identifies the field as IMG I want to convert it to base64 and save it - this is my solution:
function img_2_b64( element ) {
return new Promise((resolve, reject) => {
let file = element.files[0];
let reader = new FileReader();
reader.onloadend = function(e) {
resolve(e.target.result);
};
reader.onerror = function() {
reject();
};
reader.readAsDataURL(file);
});

Maybe you can use promises to have more control over the information flow:
$('#checkButton').on('click', function( event ) {
let form = document.querySelector('#productForm');
let return_dict = {"number": 0, "data": d};
const pendings = keys.map(async function ( key ) {
for ( let fe = 0; fe < form.elements.length; fe++ ) {
if ( form.elements[fe].getAttribute("data-key") === key ) {
if ( key.split("|")[1] === "IMG" ) {
d[key] = await img_2_b64(form.elements[fe]);
} else {
d[key] = form.elements[fe].value;
}
};
};
});
Promise.all(pendings)
.then((values) => {
write_2_DB_with_ajax_call ( values )
});
});
and
function img_2_b64( element ) {
return new Promise((resolve, reject) => {
let fileprops = "";
let file = element.files[0];
let reader = new FileReader();
reader.onloadend = function() {
resolve(reader.readAsDataURL(file));
};
reader.onerror = function() {
reject();
};
})
}

Related

How do I use $.when() instead of await in the following function?

I have the following 2 functions:
$(document).on(`dragover drop change`, `.fileUpload`, async function(e) {
e.stopPropagation();
e.preventDefault();
const thisEl = $(this);
if (thisEl.val() === `` || e.type === `dragover`) {
return;
}
const headerRec = await getCSVHeaderRow(thisEl[ 0 ].files[ 0 ], `,`);
console.log(`header rec: `, headerRec,headerRec2);
});
function getCSVHeaderRow(file, delim) {
return new Promise((resolve, reject) => {
let reader = new FileReader();
reader.onload = function(e) {
let headerRow = [];
const rows = e.target.result.split(`\r\n`);
headerRow = rows[ 0 ];
console.log(`headerrow: `, headerRow, headerRow.split(delim));
resolve(headerRow.split(delim));
};
reader.onerror = reject;
reader.readAsText(file);
});
}
I am trying to use $.when() rather than await but the called function returns
a promise object rather than the array:
$(document).on(`dragover drop change`, `.fileUpload`, function(e) {
e.stopPropagation();
e.preventDefault();
const thisEl = $(this);
if (thisEl.val() === `` || e.type === `dragover`) {
return;
}
const headerRec2 = $.when(getCSVHeaderRow2(thisEl[ 0 ].files[ 0 ], `,`)).then(function(data) {
return data;
});
console.log(`header rec: `, headerRec,headerRec2);
});
function getCSVHeaderRow2(file, delim) {
const dfr = $.Deferred();
let reader = new FileReader();
reader.onload=function(e) {
let headerRow = [];
const rows = e.target.result.split(`\r\n`);
headerRow = rows[ 0 ];
console.log(`headerrow: `, headerRow, headerRow.split(delim));
return headerRow.split(delim);
};
return dfr.resolve(reader.readAsText(file));
}
What do I need to change in getCSVHeaderRow2() to have it return the array in the way
getCSVHeaderRow() is?

In Javascript, readFile method Returns a empty Array, But when console log data are shown in console

I want to add all images to an array. But when I the following code returns an empty array. However when I console log , the data regarding to the images are shown as like this screenshot. Can anyone give me a solution
This is my code 👇
const readImages = uploader => {
let images = []
const selectedfiles = uploader.files
for (let index = 0; index < selectedfiles.length; index++) {
const fileReader = new FileReader()
fileReader.onload = fileLoadedEvent => {
images.push(fileLoadedEvent.target.result)
}
fileReader.readAsDataURL(selectedfiles[index])
}
return images;
}//End of readImages
this._qs('#uploadImages').addEventListener('input', () => {
const images = readImages(this._qs("#uploadImages"))
console.log(images)
for (let index = 0; index < images.length; index++) {
this._qs('#previewImages').innerHTML += `<img src="${images[index]}" alt="image-${index}"/>`
}
})
The images are pushed to your array onload, meaning that it happens asynchronously. You can't act on the images until they've been returned to the browser. Best way to handle this is to have readImages return a Promise.all(), and have each, individual image load as it's own Promise that you push to the Promise.all() array.
const readImage = (file) =>
new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.onload = ({ target: { result } }) => resolve(result);
fileReader.onerror = () => reject(fileReader.error);
fileReader.readAsDataURL(file);
});
const readImages = ({ files }) => {
if (!files) {
return Promise.reject('No files provided');
}
const imgPromises = [];
files.forEach((file) => imgPromises.push(readImage(file)));
return Promise.all(imgPromises);
};
// The input 'event' target will contain the value of the field
const onInput = ({ target: { value } }) => {
readImages(value).then((imgArray) => {
const preview = document.getElementById('previewImages');
imgArray.forEach((img, index) => {
preview.innerHTML += `<img src="${img}" alt="image_${index}" />`;
});
});
};
document.getElementById('uploadImages').addEventListener('input', onInput);
Note: This is really raw code, to give you an idea on the basics, and doesn't check your final result set for errors. It also uses object destructuring, which won't work in older browsers.

var result = await someFunc() returns an object but I expected list of object

I wrote following function for loading indexeddb. (from IndexedDB 備忘メモ)
I think this function should return Array of object. But, sometimes it returns an object. What are the possibilities of bug ?
Chrome developer tool said type of object was Array during in "load" function. But, after received "records" is type of object.
async function load(dbobj, db, index, range) {
return new Promise(async (resolve, reject) => {
const saves = [];
const req = db.transaction(dbobj.storeName).objectStore(dbobj.storeName).index(index).openCursor(range);
const onfinished = () => {
console.log(`${saves.length} saves found.`);
if (saves.length > 0) {
resolve(saves[saves.length - 1]);
}
};
req.onerror = reject;
req.onsuccess = (ev) => {
const cur = ev.target.result;
if (cur) {
saves.push(cur.value);
cur.continue();
} else {
onfinished();
}
};
});
}
// example of receiving data
var records = await load(dbobj, db, index, range);
you are resolving only the value at the last index! resolve(saves) if you need the entire array;
async function load(dbobj, db, index, range) {
return new Promise(async (resolve, reject) => {
const saves = [];
const req = db.transaction(dbobj.storeName).objectStore(dbobj.storeName).index(index).openCursor(range);
const onfinished = () => {
console.log(`${saves.length} saves found.`);
if (saves.length > 0) {
resolve(saves); // you are resolving only the value at the last index! resolve(saves) if you need the entire array;
}
};
req.onerror = reject;
req.onsuccess = (ev) => {
const cur = ev.target.result;
if (cur) {
saves.push(cur.value);
cur.continue();
} else {
onfinished();
}
};
});
}

How to use callback inside loop?

The background is that I allow user drags multiple files into Dropzone. I need to check each file type. If one of them is no allowed, set message and get out early.
Please see code below
Starts at for (let i = 0; i < acceptedFiles.length; i++) {
In side this for loop, I call reader.onloadend, which is a callback.
How do I run callback inside for-loop?
// Keep it internal
const getMimetype = signature => {
switch (signature) {
case '89504E47':
return 'image/png';
case '47494638':
return 'image/gif';
case '25504446':
return 'application/pdf';
case 'FFD8FFDB':
case 'FFD8FFE0':
return 'image/jpeg';
case '504B0304':
return 'application/zip';
default:
return 'Unknown filetype';
}
};
const onDropAccepted = useCallback(acceptedFiles => {
// reset to default state
resetToDefaultState();
//test
console.log('acceptedFiles', acceptedFiles);
// reader
const reader = new FileReader();
let file;
// Multi
if (config.isMultipleFiles === true) {
// Loop all files and check file types
for (let i = 0; i < acceptedFiles.length; i++) {
file = acceptedFiles[i];
// get 1st 4 byptes
const blob = file.slice(0, 4);
reader.readAsArrayBuffer(blob);
reader.onloadend = evt => {
if (evt.target.readyState === FileReader.DONE) {
const uint = new Uint8Array(evt.target.result);
let bytes = [];
uint.forEach(byte => {
bytes.push(byte.toString(16));
});
const hex = bytes.join('').toUpperCase();
const type = getMimetype(hex);
// type is allowed
if (config.fileTypes.includes(type)) {
setFiles([...files, ...acceptedFiles]);
} else {
// type no good
setIsInvaildFileType(true);
}
}
};
}
} else {
// drop 1 file
if (acceptedFiles.length <= 1) {
// bucket no file
if (files.length === 0) {
file = acceptedFiles[0];
// 1st 4 bytes
const blob = file.slice(0, 4);
// read 4 bytes
reader.readAsArrayBuffer(blob);
// later
reader.onloadend = evt => {
if (evt.target.readyState === FileReader.DONE) {
// event res to unit
const uint = new Uint8Array(evt.target.result);
// byte
let bytes = [];
// loop each unit
uint.forEach(byte => {
bytes.push(byte.toString(16));
});
// hex
const hex = bytes.join('').toUpperCase();
const type = getMimetype(hex);
//test
console.log('hex', hex);
console.log('output', type);
// type is allowed
if (config.fileTypes.includes(type)) {
setFiles([...files, ...acceptedFiles]);
} else {
// type no good
setIsInvaildFileType(true);
}
}
};
} else {
// bucket has file already
setIsMaxFileNum(true);
}
} else {
// drop multiple files, no thinking of bucket
setIsMaxFileNum(true);
}
}
});
I've also had to validate per file using react-dropzone in a similar way.
My workaround was to promisify FileReader.
1️⃣ This is the promisified version of "FileReader"
const isValidFile = file => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = evt => {
// other logic removed for brevity...
2️⃣ Your custom logic dictates, if the file is valid or not
if (config.fileTypes.includes(type)) {
resolve(true);
} else {
resolve(false);
}
};
3️⃣ Should there was an error, this file is not good.
reader.onerror = () => resolve(false)
4️⃣ Start the reading process.
const blob = file.slice(0, 4);
reader.readAsArrayBuffer(blob);
});
};
Now you can use that within the for loop you mentioned.
const onDropAccepted = useCallback(acceptedFiles => {
// reset to default state
resetToDefaultState();
1️⃣ As `useCallback` accepts a non-async method,
Create a wrapped async method we can call here.
const processFiles = async () => {
if (config.isMultipleFiles === true) {
for (let i = 0; i < acceptedFiles.length; i++) {
const file = acceptedFiles[i];
2️⃣ Here is where we validate the file using the code above.
const isValid = await isValidFile(file);
if (!isValid) {
setIsInvaildFileType(true);
return;
}
}
3️⃣ At this point, all files are good.
setFiles([...files, ...acceptedFiles]);
} else {
// removed for brevity...
}
};
4️⃣ Let's fire up the process
processFiles();
});

IndexDB cursor .onsucess as a promise

First of all, I can't find a suitable title for this question - please feel free to edit.
I have the following function that reads objects from indexDb,
loadNeededParcels = property => {
const dbResults = [];
var readTransaction = this.db
.transaction("parcelData")
.objectStore("parcelData");
readTransaction.openCursor().onerror = e => {
console.log("open cursor error ", e);
};
readTransaction.openCursor().onsuccess = e => {
const cursor = e.target.result;
if (cursor) {
dbResults.push(cursor.value);
cursor.continue();
} else {
return dbResults;
}
};
};
Now when I call this function with a simple function call, for example:
console.log(loadNeededParcels('hasData'))
The console log is undefined. I am guessing this happens because the function does not wait for the cursor to finish and return the dbResults variable?
So my question is this - how can I re-write this function as a promise, or rather to wait for the readTransaction.openCursor().onsucess to trigger?
So the expected result is for the function to actually return the values read from the database before exiting.
I am using cursors since the .getAll() the method is not supported in IE.
A simple solution that I ended up using:
loadNeededParcels = property => {
return new Promise((resolve, reject) => {
var readTransaction = this.db
.transaction("parcelData")
.objectStore("parcelData");
readTransaction.openCursor().onerror = e => {
reject(e);
};
const dbResults = [];
readTransaction.openCursor().onsuccess = e => {
const cursor = e.target.result;
if (cursor) {
dbResults.push(cursor.value);
cursor.continue();
} else {
resolve(dbResults);
}
};
});
};
Try something like this. Do not call openCursor twice, that creates two requests.
function loadNeededParcels(db, property) {
return new Promise(function(resolve, reject) {
var results = [];
var tx = db.transaction('parcelData');
tx.onerror = function(event) {
reject(tx.error);
};
var store = tx.objectStore('parcelData');
// Open a cursor over all items
var request = store.openCursor();
request.onsuccess = function(event) {
var cursor = request.result;
if(cursor) {
var value = cursor.value;
if(value) {
// Only append defined values to the array
results.push(cursor.value);
}
cursor.continue();
} else {
resolve(results);
}
};
});
}
loadNeededParcels(db, 'hasData').then(function(results) {
console.log('Results', results);
}).catch(function(error) {
console.error(error);
});

Categories