Desired to have a JavaScript to loop trough a specific URL to find existing images using number incrementation ("https://6od.hu/data/hej_Page_%D.png", where %D is incremented).
Trying to Check if image exists without loading it and using the while loop:
The working code
function imageExists(url) {
return new Promise((resolve, reject) => {
const img = new Image(url);
img.onerror = reject;
img.onload = resolve;
const timer = setInterval(() => {
if (img.naturalWidth && img.naturalHeight) {
img.src = ''; /* stop loading */
clearInterval(timer);
resolve();
}
}, 10);
img.src = url;
});
}
function loadImg(i) {
const src_pattern = "https://6od.hu/data/hej_Page_0%D.png"
let src = src_pattern.replace("%D", i);
imageExists(src)
.then(() => console.log("Image exists."))
.catch(() => console.log("Image not exists."));
}
console.log(loadImg(5)); // true|false needed to return here
How this Promise object can return true/false for the loop?
I am just learning these Promise objects, please try to be descriptive. Thank you.
Desired loop
let i = 1;
while (loadImg(i)) {
console.log(i + ": " + loadImg(i)); // log all of the existing img than stop
i++;
}
I added missing return and changed then and catch handlers
Also, removed setInterval
Check one:
function imageExists(url) {
return new Promise((resolve, reject) => {
const img = new Image(url);
img.onerror = reject;
img.onload = resolve;
img.src = url;
});
}
function loadImg(i) {
const src_pattern = "https://6od.hu/data/hej_Page_0%D.png"
let src = src_pattern.replace("%D", i);
return imageExists(src)
.then(() => true)
.catch(() => false);
}
(async() => {
console.log(await loadImg(5));
})()
Loop:
function imageExists(url) {
return new Promise((resolve, reject) => {
const img = new Image(url);
img.onerror = reject;
img.onload = resolve;
img.src = url;
});
}
function loadImg(i) {
const src_pattern = "https://6od.hu/data/hej_Page_0%D.png"
let src = src_pattern.replace("%D", i);
return imageExists(src)
.then(() => true)
.catch(() => false);
}
(async() => {
for (let i = 1; true; i += 1) {
const exists = await loadImg(i)
if (!exists) break
console.log(i, exists);
}
})()
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();
};
})
}
I have got the following script I use to pre-load images:
export default function PreloadImages(images) {
function loadImage(src) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = function() {
resolve(img);
};
img.onerror = img.onabort = function() {
reject(src);
};
img.src = src;
});
}
return Promise.all(images.map(src => loadImage(src)));
}
I then use this within a Vue component like follows:
PreloadImages(this.images).then(() => (this.imagesPreloaded = true));
I want to be able to get progress from this however within my component so I can display for example 2/50 Images Loaded. How would I go about doing this?
Edit
This is what I ended up with:
PreloadImages.js
export default function PreloadImages(images) {
function loadImage(src) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = function() {
resolve(img);
};
img.onerror = img.onabort = function() {
reject(src);
};
img.src = src;
});
}
return images.map(src => loadImage(src));
}
Within my component:
handlePreload() {
PreloadImages(this.images).forEach(p => p.then(() => {
this.imagesPreloaded++;
}));
},
try this:
function PreloadImages(images) {
let promises = [];
images.map(src => loadImage(src));
function loadImage(src) {
promises.push(new Promise((resolve, reject) => {
const img = new Image();
img.onload = function() {
resolve(img);
};
img.onerror = img.onabort = function() {
reject(src);
};
img.src = src;
}));
}
Promise.all(promises);
return promises;
}
let images = [];
let counter = 0;
let promises = PreloadImages(images);
promises.forEach(p => p.then(() => {
counter++;
console.log(counter);
}));
This is how you should approach it, don't introduce any extra looping:
var imageSrcList = [
"https://image.flaticon.com/icons/svg/565/565860.svg",
"https://image.flaticon.com/icons/svg/565/565861.svg",
"https://Error.Done.One.Purpose",
"https://image.flaticon.com/icons/svg/565/565855.svg",
];
var imageContainer = document.getElementById("loadedImages");
var loadedCount = 0;
function displayCurrentLoadStatus(){
console.log("Current load progress: ", loadedCount, " of ", imageSrcList.length);
}
function PreloadImages(images) {
function loadImage(src) {
return new Promise((resolve, reject) => {
const img = new Image();
img.src = src;
img.onload = function()
{
++loadedCount;
imageContainer.appendChild(img);
displayCurrentLoadStatus();
resolve(img);
};
img.onerror = img.onabort = function() {
reject(src);
};
});
}
// Note that I removed Promise.all, let's just return a list of promises for the images passed
return images.map((imgSrc, i)=> loadImage(imgSrc).catch((rejectedSrcError=> rejectedSrcError)));
}
// now let's create all promises for each image in images
Promise.all(PreloadImages(imageSrcList)).then( resolvedSrcList =>
{
// Use your resolved Src's here
console.log("Load complete: ",loadedCount, "/",resolvedSrcList.length, " images were loaded successfully.");
});
img{
width: 60px;
height: 60px;
display: inline-block;
}
<div id="loadedImages"></div>
... tldr ...
There will be 2 different basic ideas and 4 solutions for demonstration purpose.
The first two approaches are valid in case one can not or is not allowed to change the return value (a single promise) of PreloadImages as from the OP's originally provided example code.
The 3rd example presents the preferred approach which is to change the return value of PreloadImages to a list of (image preloading) promises, whereas the 4th example is just No.3 that in addition features a real live demo of progress-bar and statistics rendering.
1) Proxy based
One could use a Proxy object that one, in addition to the image source list, does provides to the preloadImages function.
One would use such an object in its set and validate configuration.
Choosing a Proxy based approach comes with following advantages ...
Introducing eventProxy into preloadImages makes this new implementation agnostic to later user cases.
One just needs to implement functionality for an API that provides all necessary information about image loading states in a/the most generic way.
The user specific, but in the preloadImages' scope unknown use case, that later needs to be handled, will be provided as part of the eventProxy.
/*export default */function preloadImages(imageSourceList, eventProxy) {
// - the introduction of an `eventProxy` makes this implementation
// agnostic to later user cases.
// - one just needs to implement the necessary api that provides
// information about image loading states.
// - the user specific, but in this scope unknown use case, that later
// needs to be handled, will be provided as part of the `eventProxy`.
const maxCount = imageSourceList.length;
let successCount = 0;
let failureCount = 0;
function loadImage(src) {
return new Promise((resolve, reject) => {
const image = new Image();
image.onload = function() {
resolve(image);
++successCount;
eventProxy.event = {
image,
maxCount,
successCount,
failureCount
}
};
image.onerror = image.onabort = function() {
reject(src);
++failureCount;
eventProxy.event = {
image,
maxCount,
successCount,
failureCount
}
};
image.src = src;
});
}
return Promise.all(imageSourceList.map(src => loadImage(src)));
}
function renderImageLoadProgress(evt) {
// the render functionality goes in here ...
console.log('renderImageLoadProgress :: evt :', evt);
}
function isImageLoadType(type) {
return (
type
&& ('image' in type)
&& ('maxCount' in type)
&& ('successCount' in type)
&& ('failureCount' in type)
)
}
const loadEventValidator = {
set: function(obj, key, value) {
let isSuccess = false;
if ((key === 'event') && isImageLoadType(value)) {
obj[key] = value;
isSuccess = true;
// provide render functionality as part of
// the proxy's correct setter environment.
renderImageLoadProgress(value);
}
return isSuccess;
}
}
const loadEventProxy = new Proxy({}, loadEventValidator);
const imageSourceList = [
'https://picsum.photos/id/1011', // loading will fail.
'https://picsum.photos/id/1011/5472/3648',
'https://picsum.photos/id/1012/3973/2639',
'https://picsum.photos/id/1013/4256/2832',
'https://picsum.photos/id/1013', // loading will fail.
'https://picsum.photos/id/1014/6016/4000',
'https://picsum.photos/id/1015/6000/4000',
'https://picsum.photos/id/1016/3844/2563'
];
preloadImages(imageSourceList, loadEventProxy);
.as-console-wrapper { min-height: 100%!important; top: 0; }
2) Event based
In case of not being able making use of proxies, one easily can switch the above approach to EventTarget and CustomEvent, especially since there are polyfills available that also do work in older versions of Internet Explorer. The idea behind this solution and its advantages are the same as with the former Proxy based one ...
/*export default */function preloadImages(imageSourceList, eventTarget) {
// - additionally injecting/providing an `eventTarget` makes this
// implementation agnostic to later user cases.
// - one just needs to dispatch all relevant information via the
// `details` property of a custom event.
// - the user specific, but in this scope unknown use case, will be
// handled from ouside via listening to the custom event's type.
// - there are polyfills for `EventTarget` as well as for `CustomEvent`
// that also do work in older versions of Internet Explorer.
const maxCount = imageSourceList.length;
let successCount = 0;
let failureCount = 0;
function loadImage(src) {
return new Promise((resolve, reject) => {
const image = new Image();
image.onload = function() {
resolve(image);
++successCount;
eventTarget.dispatchEvent(new CustomEvent("loadstatechange", {
detail: {
image,
maxCount,
successCount,
failureCount
}
}));
};
image.onerror = image.onabort = function() {
reject(src);
++failureCount;
eventTarget.dispatchEvent(new CustomEvent("loadstatechange", {
detail: {
image,
maxCount,
successCount,
failureCount
}
}));
};
image.src = src;
});
}
return Promise.all(imageSourceList.map(src => loadImage(src)));
}
function renderImageLoadProgress(evt) {
// the render functionality goes in here ...
console.log('renderImageLoadProgress :: evt.detail :', evt.detail);
}
const loadEventTarget = new EventTarget();
loadEventTarget.addEventListener('loadstatechange', renderImageLoadProgress);
const imageSourceList = [
'https://picsum.photos/id/1011', // loading will fail.
'https://picsum.photos/id/1011/5472/3648',
'https://picsum.photos/id/1012/3973/2639',
'https://picsum.photos/id/1013/4256/2832',
'https://picsum.photos/id/1013', // loading will fail.
'https://picsum.photos/id/1014/6016/4000',
'https://picsum.photos/id/1015/6000/4000',
'https://picsum.photos/id/1016/3844/2563'
];
preloadImages(imageSourceList, loadEventTarget);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Note
Since the 2 above given approaches did not change the return value/signature of the original implementation of PreloadImages they had to be implemented exactly each in their own way. But both together come with a lot of additional code, the proxy based approach being a little fatter than the event based one.
3) Return Array of Promises
Anyhow, if one is/was just willing to change the return value of the former PreloadImages to an array of image loading promises one could achieve a very clean and lean implementation that might look similar to the next one ...
/*export default */function getAsyncLoadingImageList(imageSourceList) {
const totalCount = imageSourceList.length;
let successCount = 0;
let failureCount = 0;
function loadImage(src) {
return new Promise((resolve, reject) => {
const image = new Image();
image.onload = function() {
resolve({
image,
counts: {
total: totalCount,
success: ++successCount,
failure: failureCount
},
success: true,
});
};
image.onerror = image.onabort = function() {
reject({
image,
counts: {
total: totalCount,
success: successCount,
failure: ++failureCount
},
success: false,
});
};
image.src = src;
});
}
// return list of *image loading* promises.
return imageSourceList.map(src => loadImage(src));
}
function renderImageLoadProgress(imageLoadData) {
// the render method.
console.log('imageLoadData : ', imageLoadData);
}
const imageSourceList = [
'https://picsum.photos/id/1011', // loading will fail.
'https://picsum.photos/id/1011/5472/3648',
'https://picsum.photos/id/1012/3973/2639',
'https://picsum.photos/id/1013/4256/2832',
'https://picsum.photos/id/1013', // loading will fail.
'https://picsum.photos/id/1014/6016/4000',
'https://picsum.photos/id/1015/6000/4000',
'https://picsum.photos/id/1016/3844/2563'
];
getAsyncLoadingImageList(imageSourceList).forEach(promise =>
promise
.then(renderImageLoadProgress)
.catch(renderImageLoadProgress)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
4) Bonus
In order to present a real progress bar demo, the 3rd example, that already had it all, was taken as is, but now features a full blown render method instead of just logging the image-loading progress-data ...
/*export default */function getAsyncLoadingImageList(imageSourceList) {
const totalCount = imageSourceList.length;
let successCount = 0;
let failureCount = 0;
function loadImage(src) {
return new Promise((resolve, reject) => {
const image = new Image();
image.onload = function() {
resolve({
image,
counts: {
total: totalCount,
success: ++successCount,
failure: failureCount
},
success: true,
});
};
image.onerror = image.onabort = function() {
reject({
image,
counts: {
total: totalCount,
success: successCount,
failure: ++failureCount
},
success: false,
});
};
image.src = src;
});
}
// return list of *image loading* promises.
return imageSourceList.map(src => loadImage(src));
}
function getImageLoadProgressRenderConfig(progressContainer) {
const elmProgress = progressContainer.querySelector('progress');
const countsContainer = progressContainer.querySelector('.image-counts');
const failuresContainer = progressContainer.querySelector('.load-failures');
const elmImageCount = countsContainer.querySelector('.count');
const elmImageTotal = countsContainer.querySelector('.total');
const elmFailureCount = failuresContainer.querySelector('.failure-count');
const elmCurrentCount = failuresContainer.querySelector('.current-count');
return {
nodes: {
progress: {
display: elmProgress,
count: elmImageCount,
total: elmImageTotal
},
failure: {
display: failuresContainer,
failureCount: elmFailureCount,
currentCount: elmCurrentCount
}
},
classNames: {
failure: 'failures-exist'
}
};
}
function renderImageLoadProgressViaBoundConfig(imageLoadData) {
const imageCounts = imageLoadData.counts;
const imageCountTotal = imageCounts.total;
const imageCountSuccess = imageCounts.success;
const imageCountFailure = imageCounts.failure;
const imageCountCurrent = (imageCountSuccess + imageCountFailure);
const renderConfig = this;
const renderNodes = renderConfig.nodes;
const progressNodes = renderNodes.progress;
const failureNodes = renderNodes.failure;
const classNameFailure = renderConfig.classNames.failure;
const isFailureOnDisplay =
failureNodes.display.classList.contains(classNameFailure);
progressNodes.display.textContent = `${ imageCountCurrent } \/ ${ imageCountTotal }`;
progressNodes.display.value = imageCountCurrent;
progressNodes.count.textContent = imageCountCurrent;
if (!imageLoadData.success) {
if (!isFailureOnDisplay) {
failureNodes.display.classList.add(classNameFailure);
}
failureNodes.failureCount.textContent = imageCountFailure;
} else if (isFailureOnDisplay) {
failureNodes.currentCount.textContent = imageCountCurrent
}
}
function preloadImagesAndRenderLoadProgress(imageSourceList, progressContainer) {
const totalImageCount = imageSourceList.length;
const renderConfig = getImageLoadProgressRenderConfig(progressContainer);
const renderNodes = renderConfig.nodes;
const progressNodes = renderNodes.progress;
const failureNodes = renderNodes.failure;
const renderImageLoadProgress =
renderImageLoadProgressViaBoundConfig.bind(renderConfig);
failureNodes.display.classList.remove(renderConfig.classNames.failure);
failureNodes.failureCount.textContent = 0;
failureNodes.currentCount.textContent = 0;
progressNodes.display.max = totalImageCount;
progressNodes.total.textContent = totalImageCount;
renderImageLoadProgress({
counts: {
success: 0,
failure: 0
},
success: true,
});
getAsyncLoadingImageList(imageSourceList).forEach(promise => promise
.then(renderImageLoadProgress)
.catch(renderImageLoadProgress)
);
}
const imageSourceListWithFailure = [
'https://picsum.photos/id/1011', // loading will fail.
'https://picsum.photos/id/1011/5472/3648',
'https://picsum.photos/id/1012/3973/2639',
'https://picsum.photos/id/1013/4256/2832',
'https://picsum.photos/id/1013', // loading will fail.
'https://picsum.photos/id/1014/6016/4000',
'https://picsum.photos/id/1015/6000/4000',
'https://picsum.photos/id/1016/3844/2563',
'https://picsum.photos/id/1018', // loading will fail.
'https://picsum.photos/id/1018/3914/2935',
'https://picsum.photos/id/1019/5472/3648',
'https://picsum.photos/id/1020/4288/2848',
'https://picsum.photos/id/1021', // loading will fail.
'https://picsum.photos/id/1021/2048/1206',
'https://picsum.photos/id/1022/6000/3376',
'https://picsum.photos/id/1023/3955/2094'
];
const imageSourceListWithoutFailure = [
'https://picsum.photos/id/1039/6945/4635',
'https://picsum.photos/id/1038/3914/5863',
'https://picsum.photos/id/1037/5760/3840',
'https://picsum.photos/id/1036/4608/3072',
'https://picsum.photos/id/1035/5854/3903',
'https://picsum.photos/id/1033/2048/1365',
'https://picsum.photos/id/1032/2880/1800',
'https://picsum.photos/id/1031/5446/3063',
'https://picsum.photos/id/1029/4887/2759',
'https://picsum.photos/id/1028/5184/3456',
'https://picsum.photos/id/1027/2848/4272',
'https://picsum.photos/id/1026/4621/3070'
];
preloadImagesAndRenderLoadProgress(
imageSourceListWithFailure,
document.querySelector('#loading-with-failure')
);
preloadImagesAndRenderLoadProgress(
imageSourceListWithoutFailure,
document.querySelector('#loading-without-failure')
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
.image-loading-progress {
margin: 20px 0;
}
.image-loading-progress,
.image-loading-progress > span,
.image-loading-progress progress {
display: block;
width: 100%;
}
.image-loading-progress > .load-failures {
display: none
}
.image-loading-progress > .load-failures.failures-exist {
display: block
}
<label class="image-loading-progress" id="loading-with-failure">
<progress value="0" max="0">0 / 0</progress>
<span class="image-counts">
...
<span class="count">0</span>
out of
<span class="total">0</span>
images finished loading ...
</span>
<span class="load-failures">
...
<span class="failure-count">0</span>
out of
<span class="current-count">0</span>
images are not available ...
</span>
</label>
<label class="image-loading-progress" id="loading-without-failure">
<progress value="0" max="0">0 / 0</progress>
<span class="image-counts">
...
<span class="count">0</span>
out of
<span class="total">0</span>
images finished loading ...
</span>
<span class="load-failures">
...
<span class="failure-count">0</span>
out of
<span class="current-count">0</span>
images are not available ...
</span>
</label>
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();
}
};
});
}
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();
});