大文件传输与断点续传实现(极简Demo: React+Node.js)

news/2024/12/19 18:38:36/文章来源:https://www.cnblogs.com/chatGPT-Last/p/18617776

大文件传输与断点续传实现(极简Demo:React+Node.js)

简述

使用React前端和Node.js后端实现大文件传输和断点续传的功能。通过分片上传技术,可以有效地解决网络不稳定带来的传输中断问题。

文章内容

前端实现(React)

首先,您需要在前端项目中安装axios库以处理HTTP请求。可以使用以下命令:

npm i axios

以下是实现大文件上传的React组件代码:

import axios from 'axios';
import { useRef, useState } from 'react';// 定义文件分片大小(例如 5MB)
const CHUNK_SIZE = 5 * 1024 * 1024;/** 解析当前页面功能 */
export default function FileUploader() {const [file, setFile] = useState(null); // 用于存储用户选择的文件const [uploadProgress, setUploadProgress] = useState(0); // 上传进度const uploading = useRef(false); // 用于防止重复触发上传逻辑// 当用户选择文件时触发const handleFileChange = (e) => {setFile(e.target.files[0]); // 将选择的文件存入状态setUploadProgress(0); // 重置上传进度};// 计算文件的唯一标识 (哈希)const calculateFileHash = async (file) => {return new Promise((resolve) => {const reader = new FileReader();reader.onload = (e) => {const sparkMD5 = require('spark-md5');const hash = sparkMD5.ArrayBuffer.hash(e.target.result);resolve(hash);};reader.readAsArrayBuffer(file);});};// 开始文件上传const handleUpload = async () => {if (!file || uploading.current) return; // 如果未选择文件或正在上传,则直接返回uploading.current = true; // 标记为正在上传const fileHash = await calculateFileHash(file); // 获取文件的唯一标识(哈希值)console.log('fileHash', fileHash);const totalChunks = Math.ceil(file.size / CHUNK_SIZE); // 计算文件分片总数// 检查哪些分片已经上传const { data: uploadedChunks } = await axios.post('http://localhost:5000/check',{fileName: file.name,fileHash,},);// 上传未完成的分片for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {if (uploadedChunks?.includes(chunkIndex)) {console.log('跳过chunkIndx', chunkIndex);setUploadProgress(((chunkIndex + 1) / totalChunks) * 100); // 更新进度continue; // 跳过已经上传的分片}console.log('上传chunkIndx', chunkIndex);// 创建当前分片const start = chunkIndex * CHUNK_SIZE; // 分片起始字节const end = Math.min(file.size, start + CHUNK_SIZE); // 分片结束字节const chunk = file.slice(start, end); // 获取分片// 上传分片const formData = new FormData();formData.append('chunk', chunk); // 当前分片formData.append('fileName', file.name); // 文件名formData.append('fileHash', fileHash); // 文件唯一标识formData.append('chunkIndex', chunkIndex); // 分片索引await axios.post(`http://localhost:5000/upload?fileHash=${fileHash}&chunkIndex=${chunkIndex}&fileName=${file.name}`,formData,{onUploadProgress: (progressEvent) => {const progress =((chunkIndex + progressEvent.loaded / progressEvent.total) /totalChunks) *100;setUploadProgress(progress); // 实时更新上传进度},},);}// 通知服务端合并分片await axios.post('http://localhost:5000/merge', {fileName: file.name,fileHash,totalChunks,});alert('上传成功!');uploading.current = false; // 标记上传完成};return (<div style={{ padding: '20px' }}><p>大文件上传(支持断点续传)</p><input type="file" onChange={handleFileChange} /><button onClick={handleUpload}>提交上传文件</button><div style={{ marginTop: '20px' }}><progress value={uploadProgress} max="100" /><div>上传进度:{uploadProgress.toFixed(2)}%</div></div></div>);
}

后端实现(Node.js)

在后端,您需要安装以下依赖:multerfs-extraexpresscorsbody-parser。可以使用以下命令:

npm i multer fs-extra express cors body-parser

注意: 这些包应用于生产环境和开发环境。

以下是Node.js服务器的实现代码:

// 文件:server.js
const express = require("express");
const multer = require("multer");
const fs = require("fs");
const bodyParser = require("body-parser");
const path = require("path");
const cors = require("cors");
const app = express();
const uploadDir = path.join(__dirname, "uploads"); // 上传目录// 确保上传目录存在
if (!fs.existsSync(uploadDir)) {fs.mkdirSync(uploadDir);
}app.use(cors()); // 允许跨域请求
app.use(bodyParser.json());
app.use(express.json()); // 解析 JSON 请求体// 检查已上传的分片
app.post("/check", (req, res) => {const { fileHash } = req.body;console.log("fileHash check",fileHash)const fileChunkDir = path.join(uploadDir, fileHash); // 分片存储目录if (!fs.existsSync(fileChunkDir)) {return res.json([]); // 如果目录不存在,返回空数组}// 返回已上传的分片索引const uploadedChunks = fs.readdirSync(fileChunkDir).map((chunk) => {return parseInt(chunk.split("-")[1]); // 提取分片索引});res.json(uploadedChunks);
});// 设置 multer 中间件,用于处理文件上传
const storage = multer.diskStorage({destination: (req, file, cb) => {const fileHash = req.query.fileHash; // 从查询参数获取 fileHashconst chunkDir = path.join(uploadDir, fileHash);// 确保切片目录存在if (!fs.existsSync(chunkDir)) {fs.mkdirSync(chunkDir, { recursive: true });}cb(null, chunkDir);},filename: (req, file, cb) => {const { chunkIndex } = req.query;cb(null, `chunk-${chunkIndex}`);},
});const upload = multer({ storage:storage });// 上传文件分片
app.post("/upload", upload.single("chunk"), (req, res) => {const { fileHash } = req.body;res.status(200).send("分片上传成功");
});// 合并分片
app.post("/merge", (req, res) => {const { fileName, fileHash, totalChunks } = req.body;console.log("fileName",req.body)const fileChunkDir = path.join(uploadDir, fileHash);const filePath = path.join(uploadDir, fileName);// 创建可写流用于最终合并文件const writeStream = fs.createWriteStream(filePath);for (let i = 0; i < totalChunks; i++) {const chunkPath = path.join(fileChunkDir, `chunk-${i}`);const data = fs.readFileSync(chunkPath); // 读取分片writeStream.write(data); // 写入最终文件// fs.unlinkSync(chunkPath); // 删除分片文件--留下来,可以看上传记录}writeStream.end(); // 关闭流
//   fs.rmdirSync(fileChunkDir); // 删除分片目录--留下来,可以看上传记录res.send("文件合并完成");
});app.listen(5000, () => {console.log("服务器已启动:http://localhost:5000");
});

总结

通过上述代码,您可以实现大文件的分片上传和断点续传功能。这种方法不仅提高了上传的可靠性,还能有效应对网络的不稳定性。希望这篇文章对您有所帮助!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/855512.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

浏览器怎么渲染数据的

突然发现自己对于css的样式规则一直都没有完全明白,今天写文好好整明白 浏览器渲染原理 1. 浏览器向服务器请求html文件 2. html文件返回浏览器 3. 浏览器解读html文件<!DOCTYPE html>//告诉浏览器,这是html5文件// html树<html lang="en">//语言<h…

在宝塔里添加反向代理

安装OnlyOffice时,需要配置反向代理; 完成以上操作之后,访问时有可能出现504报错,也可能就可以正常访问了。如果报错可以修改上图反向代理中的目标URL的内容将域名改成127.0.0.1。我当时改成http://127.0.0.1:9633就可以了本文来自博客园,作者:飞龙在生,转载请注明原文链…

2024 Clion安装使用教程(附激活以及常见问题处理)

第一步:下载Clion安装包 访问Clion官网,下载Clion第二步: 安装 Clion下载完成后,进行安装,next,安装完成点击xx 关掉程序! 第三步: 下载补丁 Clion补丁文件 点击获取补丁下载成功后,打开标注的文件文件夹 , 进入到文件夹 /jetbra 注意: 这个文件夹单独copy一份,所属文…

语言沟通中的设计实现

系统设计的目的是更好的支持需求 我们常说,只要业务能将你的需求描述清楚,能自圆其说,我们就有办法实现。 这其实是系统设计的最理想的状态, 如果业务没想清楚,那么在系统实现中,一定会把问题暴露出来。很多时候,问题的暴露源于没有考虑周全亦或都没有考虑这种场景! 比…

Python 解密 Navicat导出的数据库连接,Navicat数据库连接导入DBeaver。

最近公司收到Navicat律师告知书,让停止使用Navicat,用了那么久的数据库连接工具,不得不换其他的。 最终选择了开源的DBeaver。 安装完DBeaver后,把Navicat导出的connections.ncx文件直接导入DBeaver。直接访问提示连接失败,因为connections.ncx文件里的密码都是加密的。 如…

营销系统缺失投放概念

营销系统现阶段分为营销权益工具、玩法、招商提报三大块。其中权益工具主要负责创建券、促销、换购类优惠权益。玩法负责用户与平台之间的连接,通过任务式交互获得权益。招商提报主要是面向商家参与活动的连接。比如要做一个活动,需要从招商发布活动信息,提报统一创建营销权…

一款使用NET+MQTT+Arduino开发的智能浇花工具

最近闲来无事,对硬件控制产生了兴趣。看到家里的盆栽,我突然萌生了制作一个自动浇水工具的想法。通过在淘宝搜索并查找相关资料,我了解了需要的硬件和通信协议。接下来,我们先看看需要做哪些准备工作(如安装 Arduino、.NET、EMQX 工具等,请自行搜索并完成安装)。 准备工…

营销平台过去与展望

营销是什么 营销是商家给用户权益,让用户少花钱买到商品,以此达到商家的营销目的。 以此逻辑为基础,展开几个关键角色用户 平台 商家他们各自在营销侧的诉求是用户需要有获得优惠的途径 门店需要有发放优惠的能力 商家需要有优惠活动的功能营销工具权益建设 以这些诉求为切入…

圈选概念

电商系统中,最核心的几个点就是如何圈选出商品、消费者、门店 在整个逻辑中,圈选有两个点选出目标集合 给出圈选的规则选出目标集合 以商品为例,在商品选品中,选出目标集合,有几种方式通过人工方式将已有的商品列表导入到系统中 通过外部系统收集的商品列表导入到系统中,…

python 悬臂梁的有限元分析

依赖包 fenics是一种用于有限元计算的动态面向对象库,它提供了一种专用的数学语言UFL来表述变分形式,并自动生成底层C++代码。 fenics 名称释义:fe:finite element的简写 cs:computational software的简写 ni:有了fe和cs后,由于最初fenics软件是在芝加哥大学(简称为phoe…

枚举思想——算法学习(一)

枚举思想——算法学习(一)前言 在算法学习的道路上,枚举思想是一种简单却强大的思想。作为一种暴力求解方法,枚举算法通过穷尽所有可能的解,从中找到满足条件的最优解或所有解。虽然它看似“低效”,但在解决许多实际问题时却显得直观且有效,尤其是在问题规模可控的情况下…

20222307 2024-2025-1 《网络与系统攻防技术》实验八实验报告

1.实验内容 (1)Web前端HTML 能正常安装、启停Apache。理解HTML,理解表单,理解GET与POST方法,编写一个含有表单的HTML。 (2)Web前端javascipt 理解JavaScript的基本功能,理解DOM。 在(1)的基础上,编写JavaScript验证用户名、密码的规则。在用户点击登陆按钮后回显“欢迎+…