当前位置: 首页 > news >正文

分片上传-

  •  分片上传原理:客户端将选择的文件进行切分,每一个分片都单独发送请求到服务端;
  • 断点续传 & 秒传原理:客户端
    发送请求询问服务端某文件的上传状态
    ,服务端响应该文件已上传分片,客户端再将未上传分片上传即可;
    • 如果没有需要上传的分片就是秒传;
    • 如果有需要上传的分片就是断点续传;
  • 每个文件要有自己唯一的标识,这个标识就是将整个文件进行MD5加密,这是一个Hash算法,将加密后的Hash值作为文件的唯一标识;
    • 使用spark-md5第三方工具库,spark-md5是指一个用于计算MD5哈希值的前端JavaScript库
  • 文件的合并时机:当服务端确认所有分片都发送完成后,此时会发送请求通知服务端对文件进行合并操作;

如下图所示是前端分片上传的整体流程:

  • 第一步:将文件进行分片,并计算其Hash值(文件的唯一标识)
  • 第二步:发送请求,询问服务端文件的上传状态
  • 第三步:根据文件上传状态进行后续上传
    • 文件已经上传过了
      • 结束 --- 秒传功能
    • 文件存在,但分片不完整
      • 将未上传的分片进行上传 --- 断点续传功能
    • 文件不存在
      • 将所有分片上传
  • 第四步:文件分片全部上传后,发送请求通知服务端合并文件分片

案例实现

  • 前端使用 Element Plus UI

  • 实现文件选择 → 计算 Hash → 分片上传 → 进度显示

  • 假设后端提供接口

    1. POST /upload/check → 接收 fileHash,返回已上传分片列表

    2. POST /upload/chunk → 上传单个分片

    3. POST /upload/merge → 所有分片上传完成后通知合并

<template><el-upload:file-list="fileList":before-upload="beforeUpload":show-file-list="false"><el-button type="primary">选择文件上传</el-button></el-upload><el-progressv-if="uploading":percentage="uploadProgress":text-inside="true"></el-progress>
</template><script setup>
import { ref } from 'vue';
import SparkMD5 from 'spark-md5';
import axios from 'axios';const fileList = ref([]);
const uploadProgress = ref(0);
const uploading = ref(false);
const chunkSize = 2 * 1024 * 1024; // 2MB// 计算文件Hash
function calculateFileHash(file) {return new Promise((resolve, reject) => {const spark = new SparkMD5.ArrayBuffer();const fileReader = new FileReader();const chunks = Math.ceil(file.size / chunkSize);let currentChunk = 0;fileReader.onload = e => {spark.append(e.target.result);currentChunk++;if (currentChunk < chunks) {loadNext();} else {resolve(spark.end());}};fileReader.onerror = () => reject('文件读取错误');function loadNext() {const start = currentChunk * chunkSize;const end = Math.min(file.size, start + chunkSize);fileReader.readAsArrayBuffer(file.slice(start, end));}loadNext();});
}// 分片上传
async function uploadFileChunks(file, fileHash) {const chunks = Math.ceil(file.size / chunkSize);// 先询问服务端已上传分片const { data } = await axios.post('/upload/check', { fileHash });const uploadedChunks = data.uploaded || [];let uploadedCount = 0;for (let i = 0; i < chunks; i++) {if (uploadedChunks.includes(i)) {uploadedCount++;uploadProgress.value = Math.floor((uploadedCount / chunks) * 100);continue; // 已上传,跳过}const start = i * chunkSize;const end = Math.min(file.size, start + chunkSize);const chunkData = file.slice(start, end);const formData = new FormData();formData.append('file', chunkData);formData.append('fileHash', fileHash);formData.append('index', i);await axios.post('/upload/chunk', formData, {onUploadProgress: e => {// 分片进度可加权到整体进度const chunkProgress = e.loaded / e.total;uploadProgress.value = Math.floor(((uploadedCount + chunkProgress) / chunks) * 100);},});uploadedCount++;uploadProgress.value = Math.floor((uploadedCount / chunks) * 100);}// 分片上传完成,通知合并await axios.post('/upload/merge', { fileHash, totalChunks: chunks });
}// 选择文件上传
async function beforeUpload(file) {uploading.value = true;uploadProgress.value = 0;fileList.value = [file];// 计算Hashconst fileHash = await calculateFileHash(file);// 分片上传await uploadFileChunks(file, fileHash);uploading.value = false;ElMessage.success('文件上传完成!');return false; // 阻止默认上传
}
</script>

1.文件 Hash 的作用是什么?为什么要计算 Hash?

Hash 用作文件的唯一标识,可以判断文件是否已经上传过(秒传),也可以实现断点续传。

同样,合并分片后可以通过 Hash 校验文件完整性。

2.Hash 是怎么计算的?为什么要用增量计算?

使用 FileReader 将文件分片读取,逐块用 SparkMD5 增量计算 Hash。

对大文件一次性计算 Hash 内存占用大且阻塞界面,增量计算避免一次性加载整个文件。

fileReader.readAsArrayBuffer异步读取分片,触发fileReader.onload回调添加到spark中

3.大文件上传可能出现性能瓶颈,你如何优化?

并发上传多分片,充分利用带宽,提高上传速度。

分片大小调节,避免请求次数过多或分片过大导致单次失败。

Hash 计算优化,例如只读取前 N MB + 文件大小组合做快速 Hash。

4.前端上传大量分片时,浏览器内存会不会撑爆?如何避免?

通过分片逐块读取,每次只在内存中处理当前分片,读取完成后释放内存。

5.单个分片上传失败怎么处理?

前端可设置自动重试次数(如 3 次)。

若多次失败,提示用户网络异常或重试。

6.分片上传完成后如何合并?

按分片索引顺序读取所有分片,顺序写入最终文件,生成完整文件。

合并完成后再次计算文件 Hash 或 MD5,与客户端 Hash 比对,如果一致,说明文件完整。

http://www.xdnf.cn/news/1460953.html

相关文章:

  • Boost搜索引擎 网络库与前端(4)
  • 力扣hot100:搜索二维矩阵 II(常见误区与高效解法详解)(240)
  • OpenBMC之编译加速篇
  • 三、神经网络
  • VisionPro联合编程相机拍照 九点标定实战
  • pinia状态管理的作用和意义
  • SSD固态硬盘加速优化-明显提高固态硬盘的效率并保持峰值性能-供大家学习研究参考
  • Ubuntu 22.04 网络服务安装配置
  • 硬件开发1-51单片机3-串口
  • 三阶Bezier曲线曲率极值及对应的u的计算方法
  • LeetCode 994 腐烂的橘子
  • 【C语言】深入理解指针(4)
  • 【LeetCode热题100道笔记】旋转图像
  • pycharm解释器使用anaconda建立的虚拟环境里面的python,无需系统里面安装python。
  • MySQL复制技术的发展历程
  • Spring启示录
  • 从传统CNN到残差网络:用PyTorch实现更强大的图像分类模型
  • BenTsao本草-中文医学大模型
  • 【算法--链表】61.旋转链表--通俗讲解
  • 【Day 44】Shell-Git版本控制器
  • 【Python】数据可视化之分类图
  • Day2p2 夏暮客的Python之路
  • 数学建模25c
  • [数据结构] 链表
  • 深度学习之第七课卷积神经网络 (CNN)调整学习率
  • MySQL子查询的分类讲解与实战
  • 从基础到实践:Web核心概念与Nginx入门全解析
  • 前端url参数拼接和提取
  • 嵌入式基础 -- I²C 信号与位层规则
  • Swift 解法详解:LeetCode 371《两整数之和》