如何在react中上传多个音频文件,但只发送3个POST请求



我想一次选择100个音频文件,但想一次只打3个api调用。一旦这3个文件上传(通过或失败),那么只有其他3个api请求将被发送。

基本上我提供了一个文件类型的输入字段:

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

,我将它作为数组存储到一个状态中。在这下面,我提供了一个上传按钮。当用户点击上传时,我想使用axios发送3个POST请求。一旦所有3个项目都失败或通过,那么只有接下来的3个项目应该继续。

您可以通过以3为一组迭代FileList集合并使用promise . allsettle()并行发送请求来实现这一点。

只是因为我不能推荐Axios,这里有一个使用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()将允许您在每组3上传后继续,无论它们通过还是失败。

此方法生成3个单独的请求,每个请求一个文件。


在Axios中,它看起来像这样(只是替换for循环)

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()方法从v1.0.0开始可用。看到https://github.com/axios/axios files-posting


如果你想在一个请求中发送3个文件,那么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);
}
}

和这个为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);
}
}

你可以使用JavaScript的for循环和Promise的组合。所有功能都实现了这一点。首先,您需要将文件数组分成3个块。您可以使用for循环和slice方法来实现这一点。接下来,您可以使用Promise。并行发送所有请求,并且只有在解决了当前集合中的所有承诺后才移动到下一组请求。下面是演示这种方法的一些示例代码:

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);
}

这将一次发送3个post请求,并将等待直到所有请求完成后再发送另外3个api请求。

你也可以使用useState钩子和useEffect来设置上传文件的状态,并使用一个变量来跟踪上传的文件数量。

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]);

这段代码将为你工作。

最新更新