在本篇博客中,我们将介绍如何使用 React 实现大文件分块上传功能。我们将使用 axios 库来发送 HTTP 请求,以及使用 HTML5 中的 File API 来处理文件操作。
上一篇文章大文件分片上传,我们已经使用python实现了后台功能。现在我们使用React实现前段示例。
背景
在 Web 开发中,当需要上传大型文件时,直接将整个文件一次性上传可能会导致性能问题,特别是在网络连接较慢或不稳定的情况下。为了解决这个问题,我们可以将大文件切分成多个小块(分片),分别上传每个小块,然后在服务器端将这些小块合并成完整的文件。
原理
- 用户选择要上传的文件。
- 前端根据分片大小将文件切分成多个小块。
- 逐个上传每个小块到服务器端。
- 服务器端接收并保存每个小块。
- 在服务器端,根据上传的小块将它们合并成完整的文件。
实现
首先,我们需要安装 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
,它具有以下功能:
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 请求,通过切分文件为多个分片,并逐个上传分片的方式来实现分块上传的逻辑。