在本篇博客中,我们将介绍如何使用 React 实现大文件分块上传功能。我们将使用 axios 库来发送 HTTP 请求,以及使用 HTML5 中的 File API 来处理文件操作。
上一篇文章大文件分片上传,我们已经使用python实现了后台功能。现在我们使用React实现前段示例。
背景
在 Web 开发中,当需要上传大型文件时,直接将整个文件一次性上传可能会导致性能问题,特别是在网络连接较慢或不稳定的情况下。为了解决这个问题,我们可以将大文件切分成多个小块(分片),分别上传每个小块,然后在服务器端将这些小块合并成完整的文件。
原理
- 用户选择要上传的文件。
- 前端根据分片大小将文件切分成多个小块。
- 逐个上传每个小块到服务器端。
- 服务器端接收并保存每个小块。
- 在服务器端,根据上传的小块将它们合并成完整的文件。
实现
首先,我们需要安装 axios 库,它将用于发送 HTTP 请求。在项目的根目录下执行以下命令:
| 12
 
 | Copy codenpm install axios
 
 | 
接下来,我们创建一个名为 FileUploader.js 的组件,其中包含了分块上传的逻辑。
下面是完整示例
| 12
 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,它具有以下功能:
- handleFileChange函数用于处理用户选择要上传的文件。它通过- event.target.files[0]获取文件对象,并将其保存到组件的状态中。
- handleUploadClick函数在用户点击上传按钮时触发。它首先计算文件的分片总数,然后迭代每个分片进行上传。
- startUpload函数用于向后端发送初始上传请求,并获取后端返回的文件名。它通过 axios 发送 POST 请求到- ${API_HOST}/init,并传递文件名和文件大小作为 JSON 数据。
- uploadChunk函数用于上传单个分片到后端。它创建一个 FormData 对象,并将文件名、分片索引和分片文件添加到 FormData 中。然后,通过 axios 发送 POST 请求到- ${API_HOST},以- multipart/form-data格式发送 FormData 数据。
- finishUpload函数用于告知后端上传已完成。它创建一个 FormData 对象,并将文件名和分片总数添加到 FormData 中。然后,通过 fetch 发送 POST 请求到- ${API_HOST}/complete,以- multipart/form-data格式发送 FormData 数据。
- 组件渲染了一个包含文件选择、上传按钮、上传进度显示和进度条的界面。
总体而言,这段代码实现了将大文件分块上传到后端的功能,并提供了上传进度的显示。它使用了 axios 库发送 HTTP 请求,通过切分文件为多个分片,并逐个上传分片的方式来实现分块上传的逻辑。