2

I'm uploading multiples images and I wanted to display the correct percentage "50%". However I'm calling the API one by one. Meaning I upload the image one by one.

The problem is the percentage keeps getting back and forth like 0 - 50%, then going back again to 0 and so on... The reason for this is because I'm dispatching the setProgress multiples times.

What will be the correct calculation to achieve the correct percentage?

Reducer

case SET_UPLOAD_PROGRESS: return { ...state, uploadProgress: action.payload, } 

Actions

export const uploadImages = ({ images }) => (dispatch) => { images.forEach(async (image) => { const formData = new FormData(); formData.append(image.name, image.imageFile); try { dispatch({ type: UPLOAD_IMAGES_REQUEST }); const response = await axios.post("http://test.com", formData, { onUploadProgress(progressEvent) { const { loaded, total } = progressEvent; const percentage = Math.floor((loaded / total) * 100); dispatch(setUploadProgress(percentage)); <- GETTING PERCENTAGE. IT IS WORKING ALREADY. EXAMPLE OUTPUT: 50 }, }); dispatch({ type: UPLOAD_IMAGES_SUCCESS, payload: [...(images || [])], }); } catch (error) { dispatch({ type: UPLOAD_IMAGES_FAILURE }); } }); }; 

Component

 const uploadProgress = useSelector(state => state.images.uploadProgress); <LinearProgress variant="determinate" value={uploadProgress} /> 
0

1 Answer 1

1

FormData can handle multiple files, even under the same field name. If the API supports it, build up your request payload and send it once

export const uploadImages = ({ images }) => async (dispatch) => { // async here const formData = new FormData(); images.forEach(image => { formData.append(image.name, image.imageFile); }) // as per usual from here... }; 

Otherwise, you'll need to chunk the progress by the number of images, treating the average as the total progress.

export const uploadImages = ({ images }) => async (dispatch) => { const imageCount = images.length; const chunks = Array.from({ length: imageCount }, () => 0); await Promise.all(images.map(async (image, index) => { // snip... await axios.post(url, formData, { onUploadProgress({ loaded, total }) { // for debugging console.log( "chunk:", index, "loaded:", loaded, "total:", total, "ratio:", loaded / total ); // store this chunk's progress ratio chunks[index] = loaded / total; // the average of chunks represents the total progress const avg = chunks.reduce((sum, p) => sum + p, 0) / chunks.length; dispatch(setUploadProgress(Math.floor(avg * 100))); } }); // snip... }); // snip... }; 

Note that the upload progress can reach 100% before the request actually completes. To handle that, you may want something in your store that detects when the uploadProgress is set to 100 and set another state (finalisingUpload = true for example) that you can clear after the request complete.

Here's a quick demo using mocks showing how the logic works ~ https://jsfiddle.net/rtLvxkb2/

Sign up to request clarification or add additional context in comments.

14 Comments

Hi Phil. I'm getting more than 100 with your second answer
Haha thanks Phil. My api doesn't support your first answer. Just use your second answer
Hi Phil. It currently outputs 100 instantly without having to wait for the API response to finish
@Joseph that seems odd. I've tested this now and it works for me as expected
Did you upload a lot of images?
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.