大文件上传 与 批量文件上传的优化方案
大文件上传与批量文件上传的优化方案:使用 Promise.allSettled
在前端开发中,文件上传是一个常见的需求。尤其在处理大文件(如10GB)和多个文件的批量上传时,我们需要考虑如何进行分片上传、并发控制、错误处理等,以确保上传过程的高效与稳定。本文将介绍如何通过 Promise.allSettled 来优化上传过程,以确保部分文件上传失败时不会影响其他文件的正常上传。
一、背景
前端上传文件至服务器时,通常面临以下问题: 1.文件大小限制:前端和后端对单次上传的大小有限制,因此需要进行分片处理。 2.网络波动:网络波动可能导致上传失败,因此需要失败重试机制。 3.传输效率:对于大文件或多个文件,单次上传会消耗较多带宽,影响上传速度和用户体验。 4.错误处理:多个文件上传过程中,单个文件失败不应影响整体上传过程,因此需要避免上传失败时中断其他文件的上传。
二、需求分析
1
2
3
1.大文件分片上传:将10GB文件切分为100MB的分片上传,每个分片独立上传,完成后通知服务器合并。
2.批量文件上传:针对多个文件,设置单次上传大小不超过200MB,并确保部分上传失败不会影响其他文件。
3.失败处理:当某个文件上传失败时,使用 Promise.allSettled 确保其他文件的上传不受影响,并单独收集失败的文件用于重试或后续处理。
三、实现方案与代码示例
在实现过程中,我们将通过 Promise.allSettled 来并发上传文件,确保在文件上传失败的情况下,不影响其他文件的上传。
1. 大文件分片上传
大文件分片上传的核心是将文件分割为固定大小的分片(如100MB),并逐个上传。Promise.allSettled 可以让我们收集每个分片的上传结果,确保即使某个分片上传失败,也不会影响其他分片的上传。上传完所有分片后再进行合并操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function sliceFile(file, chunkSize = 100 * 1024 * 1024) {
const chunks = [];
let start = 0;
while (start < file.size) {
const end = Math.min(start + chunkSize, file.size);
chunks.push(file.slice(start, end));
start = end;
}
return chunks;
}
async function uploadChunkedFile(file) {
const chunks = sliceFile(file);
const uploadPromises = chunks.map((chunk, index) => uploadChunk(chunk, index));
const results = await Promise.allSettled(uploadPromises);
const failedChunks = results
.map((result, index) => (result.status === "rejected" ? index : null))
.filter(index => index !== null);
if (failedChunks.length > 0) {
console.error("Some chunks failed to upload:", failedChunks);
// 可在此处进行失败重试或记录失败分片
} else {
await completeUpload(file.name); // 通知服务器合并分片
console.log("File upload complete");
}
}
async function uploadChunk(chunk, index) {
const formData = new FormData();
formData.append("chunk", chunk);
formData.append("index", index);
await fetch("/upload-chunk", {
method: "POST",
body: formData,
});
}
说明: •sliceFile 函数用于将大文件分片。 •uploadChunkedFile 函数调用 Promise.allSettled 并发上传所有分片,并在上传完成后检查失败的分片进行重试。 •uploadChunk 函数处理单个分片的上传,前端发送分片数据及索引至服务器。
2. 批量文件上传控制大小
在批量上传场景中,我们将文件分批上传,且每次上传的文件总大小不超过200MB,并通过 Promise.allSettled 避免单个文件的失败影响其他文件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
function batchFiles(files, maxSize = 200 * 1024 * 1024) {
let batch = [], currentSize = 0;
const batches = [];
files.forEach(file => {
if (currentSize + file.size > maxSize) {
batches.push(batch);
batch = [];
currentSize = 0;
}
batch.push(file);
currentSize += file.size;
});
if (batch.length) batches.push(batch);
return batches;
}
async function uploadFilesBatch(files) {
const batches = batchFiles(files);
for (const batch of batches) {
const uploadPromises = batch.map(file => uploadFileWithRetry(file));
const results = await Promise.allSettled(uploadPromises);
results.forEach((result, index) => {
if (result.status === "rejected") {
console.error(`File ${batch[index].name} failed:`, result.reason);
// 可以进一步收集失败文件并重试
}
});
}
}
async function uploadFileWithRetry(file, retries = 3) {
while (retries > 0) {
try {
await uploadFile(file);
console.log(`File ${file.name} uploaded successfully`);
break;
} catch (error) {
retries--;
console.warn(`Retry for file ${file.name}, retries left: ${retries}`);
if (retries === 0) throw error;
}
}
}
async function uploadFile(file) {
const formData = new FormData();
formData.append("file", file);
await fetch("/upload", {
method: "POST",
body: formData,
});
}
说明: •batchFiles 函数按总大小限制将文件分成多个批次。 •uploadFilesBatch 使用 Promise.allSettled 来保证即使批次中的某个文件上传失败,也不会影响其他文件的上传。 •uploadFileWithRetry 提供文件上传的重试机制,确保最大限度提升成功率。
四、优缺点分析
优点:
1
2
3
•分片上传:有效控制每次请求的大小,避免一次性上传大文件带来的带宽和性能瓶颈。
•批量控制:通过限制单批上传文件总大小,减少并发请求数,降低带宽消耗。
•错误隔离:使用 Promise.allSettled 隔离失败文件,确保单个文件失败不影响其他文件的上传。
缺点:
1
2
•请求数量增加:分片上传和批次上传增加了请求数量,增加了服务器负担。
•复杂性提高:实现过程中需要协调分片和批次上传的逻辑,且实现重试机制会增加代码复杂度。
五、性能开销
1
2
3
•内存和带宽:分片上传对客户端和服务器带宽、内存有较大消耗,需要合理控制分片大小和批次上传数。
•服务器负载:并发上传文件过多会导致服务器负载增加,可以通过合理的分片和批次控制来缓解服务器压力。
•响应时间:由于 Promise.allSettled 等待所有请求完成,可能延长响应时间,但对文件上传的可靠性提升明显。
总结
通过使用 Promise.allSettled,我们可以在文件上传场景中,隔离失败文件并单独处理,保证即使在部分文件上传失败的情况下,其他文件也能正常完成上传。结合分片上传和批量控制等技术手段,我们能够有效提升文件上传的效率与稳定性。这种方案适用于大文件上传、批量文件上传等复杂的上传场景。