首页 > 编程语言 >大文件传输与断点续传实现(极简Demo: React+Node.js)

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

时间:2024-12-19 18:41:43浏览次数:9  
标签:Node 断点续传 const fileHash 文件传输 file 分片 chunkIndex 上传

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

简述

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

文章内容

前端实现(React)

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

npm i axios spark-md5

以下是实现大文件上传的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; // 从查询参数获取 fileHash
    const 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");
});

总结

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

标签:Node,断点续传,const,fileHash,文件传输,file,分片,chunkIndex,上传
From: https://www.cnblogs.com/chatGPT-Last/p/18617776

相关文章

  • node.js毕设德云社票务系统程序+论文
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容一、选题背景关于票务系统的研究,现有研究主要以大型综合票务平台为主,专门针对德云社这种特定文化娱乐场景下的票务系统研究较少。因此本选题将以德云社票务为研究情......
  • node.js毕设地铁舆情管理系统程序+论文
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容一、选题背景关于地铁舆情管理系统的研究,现有研究多集中在舆情管理的通用理论和大型交通系统的舆情监控上,专门针对地铁这一特定交通场景的舆情管理系统的研究较少。......
  • egg.js 使用 wechatpay-node-v3 微信支付
    安装$npmiwechatpay-node-v3--save创建controller/wx.js文件asyncwxPay(){ //把配置常量放在了/config/config.js中const{config,ctx}=thisconst{openId}=ctx.queryconstpay=newWxPay({appid:config.wx.appid,mchid:con......
  • 基于node的在线五子棋游戏算法bug怎么解决
    //检查各个方向是否符合获胜条件functioncheckDirection(i,j,p,q){//p=0,q=1水平方向;p=1,q=0竖直方向//p=1,q=-1左下到右上//p=-1,q=1左到右上letm=1letn=1letisBlack=obj.me?1:2for(;m<5;m++){//console.lo......
  • node.js的简单示例
    Node.js是一个基于ChromeV8引擎的JavaScript运行时环境,用于方便地构建快速、可扩展的网络应用。下面是一个简单的Node.js示例,它创建了一个简单的HTTP服务器,当访问服务器时,它会响应“HelloWorld”//引入Node.js的HTTP模块consthttp=require('http');//创建HTTP服务......
  • nodejs安装及环境配置详细教程
    ‌Node.js的安装和环境配置可以通过以下步骤完成‌:‌下载和安装Node.js‌:首先,访问[Node.js官网](https:odejs.org/)下载适合您操作系统的安装包。对于Windows系统,可以选择.exe文件进行安装。安装时,可以选择自定义安装路径,通常建议将其安装在非系统盘以避免占用C盘空间。‌......
  • node.js毕设基于android的课堂考勤管理系统 论文+程序
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容一、选题背景关于课堂考勤管理系统的研究,现有研究主要集中在传统的考勤方式或者基于PC端的管理系统,专门针对基于Android平台的课堂考勤管理系统的研究较少。在国内外......
  • node.js毕设机器人编程平台的设计与实现 论文+程序
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容一、选题背景关于毕设机器人编程平台的设计与实现这一课题,在当前的国内外研究中,现有的研究主要集中在通用编程平台的开发与优化方面,对于专门针对毕业设计场景下的机......
  • 不同操作系统中使用nvm(Node Version Manager)管理Node多版本的详细步骤,包括安装、卸载
    一、在Linux系统中(以Ubuntu为例)1.安装nvm打开终端,通过以下命令下载nvm安装脚本:curl-o-https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh|bash上述命令从nvm的官方GitHub仓库获取安装脚本并执行它。安装过程中可能会提示你输入密码,因为它需要修改一......
  • 探索Node.js的奇妙世界:从零开始的入门之旅
    引言:在现代Web开发领域,Node.js已经成为了一颗耀眼的新星。它不仅改变了服务器端编程的游戏规则,还为开发者们提供了一种全新的方式来构建高效、可扩展的网络应用。无论你是前端开发者还是后端开发者,了解和掌握Node.js都将为你的开发生涯增添一份强大的技能。本文将带你踏上......