Home > Software engineering >  How to upload multiple audio file in react but sending only 3 POST request
How to upload multiple audio file in react but sending only 3 POST request

Time:01-18

I want to select 100 audio file at a time but want to hit only 3 api call at a time. Once these 3 files uploaded (pass or fail) then only other 3 api request will be sent.

Basically I am providing a input field of file type:

<input type="file" multiple name="file" className="myform"
                    onChange={handleFileChange}
                    accept="audio/wav"
                    />

and I am storing it as array into a state. Below this I am providing an UPLOAD button. When user hit on upload, I want to send 3 POST request using axios. once all 3 done either fail or pass then only next 3 should go.

CodePudding user response:

You can do this by iterating the FileList collection in groups of 3 and sending the requests in parallel using Promise.allSettled().

Simply because I cannot recommend Axios, here's a version using the Fetch API

const BATCH_SIZE = 3;

const [fileList, setFileList] = useState([]);
const [uploading, setUploading] = useState(false);

const handleFileChange = (e) => {
  setFileList(Array.from(e.target.files)); // just a guess
};

const handleUploadClick = async (e) => {
  e.preventDefault();
  setUploading(true);
  const files = [...fileList]; // avoid mutation during long uploading process

  for (let i = 0; i < files.length; i  = BATCH_SIZE) {
    const result = await Promise.allSettled(
      files.slice(i, i   BATCH_SIZE).map(async (file) => {
        const body = new FormData();
        body.append("file", file);
        const res = await fetch(UPLOAD_URL, { method: "POST", body });
        return res.ok ? res : Promise.reject(res);
      })
    );
    const passed = result.filter(({ status }) => status === "fulfilled");
    console.log(
      `Batch ${i   1}: ${
        passed.length
      } of ${BATCH_SIZE} requests uploaded successfully`
    );
  }

  setUploading(false);
};

Promise.allSettled() will let you continue after each set of 3 are uploaded, whether they pass or fail.

This method makes 3 separate requests with 1 file each.


With Axios, it would look like this (just replacing the for loop)

for (let i = 0; i < files.length; i  = BATCH_SIZE) {
  const result = await Promise.allSettled(
    files
      .slice(i, i   BATCH_SIZE)
      .map((file) => axios.postForm(UPLOAD_URL, { file }))
  );
  const passed = result.filter(({ status }) => status === "fulfilled");
  console.log(
    `Batch ${i   1}: ${
      passed.length
    } of ${BATCH_SIZE} requests uploaded successfully`
  );
}

Axios' postForm() method is available from v1.0.0. See https://github.com/axios/axios#files-posting


If you want to send 3 files in a single request, it would look like this for Fetch

for (let i = 0; i < files.length; i  = BATCH_SIZE) {
  const body = new FormData();
  files.slice(i, i   BATCH_SIZE).forEach((file) => {
    body.append("file", file); // use "file[]" for the first arg if required
  });
  try {
    const res = await fetch(UPLOAD_URL, { method: "POST", body });
    if (!res.ok) {
      throw new Error(`${res.status} ${res.statusText}`);
    }
    console.log(`Batch ${i   1} passed`);
  } catch (err) {
    console.warn(`Batch ${i   1} failed`, err);
  }
}

and this for Axios

for (let i = 0; i < files.length; i  = BATCH_SIZE) {
  try {
    await axios.postForm(
      {
        file: files.slice(i, i   BATCH_SIZE),
      },
      {
        formSerializer: {
          indexes: null, // set to false if you need "[]" added
        },
      }
    );
    console.log(`Batch ${i   1} passed`);
  } catch (err) {
    console.warn(`Batch ${i   1} failed`, err.response?.data);
  }
}

CodePudding user response:

You can use a combination of JavaScript's for loop and Promise.all functions to achieve this. First, you will need to divide your files array into chunks of 3. You can do this using a for loop and the slice method. Next, you can use Promise.all to send all the requests in parallel, and only move on to the next set of requests once all the promises in the current set have been resolved. Here's some sample code that demonstrates this approach:

const chunkSize = 3;
for (let i = 0; i < files.length; i  = chunkSize) {
  const fileChunk = files.slice(i, i   chunkSize);
  const promises = fileChunk.map(file => {
    return axios.post('/api/upload', { file });
  });

  await Promise.all(promises);
}

This will send 3 post request at a time and will wait until all the request are completed before sending another 3 api request.

You can also use useState hook with useEffect to set the state of files that are uploaded and use a variable to keep track of number of files uploaded.

const [uploadedFiles, setUploadedFiles] = useState([]);
const [uploadCount, setUploadCount] = useState(0);

useEffect(() => {
  if (uploadCount === files.length) {
    // all files have been uploaded
    return;
  }
  const chunkSize = 3;
  const fileChunk = files.slice(uploadCount, uploadCount   chunkSize);
  const promises = fileChunk.map(file => {
    return axios.post('/api/upload', { file });
  });

  Promise.all(promises).then(responses => {
    setUploadedFiles([...uploadedFiles, ...responses]);
    setUploadCount(uploadCount   chunkSize);
  });
}, [uploadCount]);

This code will work for you.

  • Related