在本篇博客中,我们将介绍如何使用 React 实现大文件分块上传功能。我们将使用 axios 库来发送 HTTP 请求,以及使用 HTML5 中的 File API 来处理文件操作。

上一篇文章大文件分片上传,我们已经使用python实现了后台功能。现在我们使用React实现前段示例。

背景

在 Web 开发中,当需要上传大型文件时,直接将整个文件一次性上传可能会导致性能问题,特别是在网络连接较慢或不稳定的情况下。为了解决这个问题,我们可以将大文件切分成多个小块(分片),分别上传每个小块,然后在服务器端将这些小块合并成完整的文件。

原理

  1. 用户选择要上传的文件。
  2. 前端根据分片大小将文件切分成多个小块。
  3. 逐个上传每个小块到服务器端。
  4. 服务器端接收并保存每个小块。
  5. 在服务器端,根据上传的小块将它们合并成完整的文件。

实现

首先,我们需要安装 axios 库,它将用于发送 HTTP 请求。在项目的根目录下执行以下命令:

1
2
Copy code
npm install axios

接下来,我们创建一个名为 FileUploader.js 的组件,其中包含了分块上传的逻辑。

下面是完整示例

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import React, {
useState
} from 'react';
import axios from 'axios';
import "./app.css"

const CHUNK_SIZE = 1024 * 1024 * 20; // 分片大小
const API_HOST = 'http://localhost:5000/api/upload' // 后端接口地址

function FileUploader() {
const [file, setFile] = useState(null);
const [progress, setProgress] = useState(0);

const handleFileChange = (event) => {
setFile(event.target.files[0]);
};

const handleUploadClick = async () => {
const totalChunks = Math.ceil(file.size / CHUNK_SIZE); // 计算分片总数
let uploadedChunks = 0; // 已上传的分片数

// 获取后端返回文件名
const filename_new = await startUpload(file.name, file.size);

for (let i = 0; i < totalChunks; i++) {
const start = i * CHUNK_SIZE;
const end = (i + 1) * CHUNK_SIZE;

const chunk = file.slice(start, end);
await uploadChunk(filename_new, i, chunk); // 上传分片
uploadedChunks++;

setProgress((uploadedChunks / totalChunks) * 100); // 更新上传进度
}

await finishUpload(filename_new, totalChunks); // 完成上传
};

const startUpload = async (filename, filesize) => {
let data = {
'name': filename,
'size': filesize,
}

const response = await axios.post(API_HOST + '/init', data, {
headers: {
'Content-Type': 'application/json'
}
});
return response.data.filename;
};

const uploadChunk = async (filename, chunkIndex, chunk) => {
const formData = new FormData();
formData.append('filename', filename);
formData.append('index', chunkIndex);
formData.append('chunk', chunk);

let resp = await axios.post(API_HOST, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
});
console.log(resp)
};

const finishUpload = async (filename, nums) => {
const formData = new FormData();
formData.append('filename', filename);
formData.append('nums', nums.toString());

fetch(API_HOST + '/complete', {
method: 'POST',
body: formData,
})
.then(response => {
if (response.ok) {
alert("success")
} else {
console.log(response)
}
})
.catch(error => console.error(error));
};


return (
<div>
<div
style={{
"width": "500px",
"height": "200px",
"border": "1px solid green"
}}
className='center'
>
<input
type="file"
onChange={handleFileChange}
/>
<button onClick={handleUploadClick}>Upload</button>
<div>{progress}% uploaded</div>
<progress max="100" value={progress}></progress>
</div>
</div>


);
}

export default FileUploader;

这段 React 代码实现了一个文件上传组件 FileUploader,它具有以下功能:

  1. handleFileChange 函数用于处理用户选择要上传的文件。它通过 event.target.files[0] 获取文件对象,并将其保存到组件的状态中。
  2. handleUploadClick 函数在用户点击上传按钮时触发。它首先计算文件的分片总数,然后迭代每个分片进行上传。
  3. startUpload 函数用于向后端发送初始上传请求,并获取后端返回的文件名。它通过 axios 发送 POST 请求到 ${API_HOST}/init,并传递文件名和文件大小作为 JSON 数据。
  4. uploadChunk 函数用于上传单个分片到后端。它创建一个 FormData 对象,并将文件名、分片索引和分片文件添加到 FormData 中。然后,通过 axios 发送 POST 请求到 ${API_HOST},以 multipart/form-data 格式发送 FormData 数据。
  5. finishUpload 函数用于告知后端上传已完成。它创建一个 FormData 对象,并将文件名和分片总数添加到 FormData 中。然后,通过 fetch 发送 POST 请求到 ${API_HOST}/complete,以 multipart/form-data 格式发送 FormData 数据。
  6. 组件渲染了一个包含文件选择、上传按钮、上传进度显示和进度条的界面。

总体而言,这段代码实现了将大文件分块上传到后端的功能,并提供了上传进度的显示。它使用了 axios 库发送 HTTP 请求,通过切分文件为多个分片,并逐个上传分片的方式来实现分块上传的逻辑。