news 2026/2/17 2:05:28

大文件分片上传

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
大文件分片上传

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 一、大文件上传的核心问题
  • 二、解决方案:分片上传
    • 2.1 核心定义
    • 2.2 核心优势
  • 三、分片上传核心原理
    • 3.1 整体流程
    • 3.2 关键:文件读取与分片
      • 3.2.1 读取本地文件
      • 3.2.2 文件分片实现
      • 3.2.3 分片特点
  • 四、核心:文件唯一标识(Hash计算)
    • 4.1 为什么需要Hash值?
    • 4.2 Hash计算方案
      • 4.2.1 安装依赖
      • 4.2.2 实现Hash计算
      • 4.2.3上传分片
    • 4.3 秒传功能原理
  • 五、断点续传核心逻辑
  • 六、总结

一、大文件上传的核心问题

当上传视频等大文件时,直接单次上传会面临诸多问题,这也是B站等平台采用分片上传的核心原因:

  1. 上传耗时极长:大文件数据量庞大,单次请求传输链路长,等待时间久;

  2. 失败代价高:网络中断、服务器异常等问题会导致上传失败,需重新完整上传;

  3. 服务端限制:多数服务器会对单次上传文件大小设限,直接上传易触发限制;

  4. 用户体验差:无进度反馈、失败后重复操作,严重影响使用感受。

二、解决方案:分片上传

2.1 核心定义

将大文件切割为多个大小均等的数据小块(分片),逐个或并行上传至服务器,全部分片上传完成后,由后端按顺序组装还原为完整文件的技术方案。

2.2 核心优势

  1. 降低失败风险:仅需重新上传出错的分片,无需重复传输完整文件;

  2. 提升上传效率:支持多分片并行上传,突破单文件传输瓶颈;

  3. 适配服务端限制:单个分片大小可控,避免触发服务端文件大小限制;

  4. 支持断点续传:结合状态记录,可在网络恢复后继续上传未完成部分。

三、分片上传核心原理

3.1 整体流程

  1. 前端读取本地文件,获取File对象;

  2. 按固定大小切割File对象,生成多个分片(Blob类型);

  3. 计算文件唯一标识(hash值),用于服务端识别文件;

  4. 逐个/并行上传分片,携带文件标识、分片序号等信息;

  5. 服务端接收分片并存储,记录分片顺序与位置;

  6. 所有分片上传完成后,前端通知后端触发文件组装;

  7. 后端按分片顺序拼接,还原为完整文件。

3.2 关键:文件读取与分片

3.2.1 读取本地文件

通过监听input标签的change事件,在回调中获取用户选择的File对象(FileList伪数组形式,需通过下标获取单个文件)。

<template><!--文件选择输入框--><input @change="handleUpload"type="file"/></div></template><script setup lang="ts">/** * 处理文件选择事件 * @param e 事件对象 */consthandleUpload=(e:Event)=>{// 类型断言:将事件目标转为HTMLInputElementconstinput=e.targetasHTMLInputElement;// 获取文件列表(伪数组,支持下标访问)constfiles=input.files;if(!files||files.length===0)return;// 获取第一个选中的文件(File对象,继承自Blob)constfile=files[0];console.log("选中文件信息:",{name:file.name,// 文件名size:file.size,// 文件大小(字节)type:file.type// 文件MIME类型});};</script>

3.2.2 文件分片实现

核心依赖Blob.slice()方法:File对象继承自Blob,可直接调用该方法切割文件。通过循环控制切割的起始与结束位置,生成多个固定大小的分片。

关键参数CHUNK_SIZE(分片大小,通常设为1-5MB,此处以1MB为例)

// 分片大小:1MB(1024*1024字节)constCHUNK_SIZE=1024*1024;/** * 切割文件为多个分片 * @param file 待分片的File对象 * @returns 分片数组(每一项为Blob类型) */constcreateChunks=(file:File):Blob[]=>{constchunks:Blob[]=[];letcurrentPosition=0;// 当前切割位置// 循环切割,直到覆盖整个文件while(currentPosition<file.size){/** * slice(start, end):切割Blob * start:起始字节位置 * end:结束字节位置(不包含) */constchunk=file.slice(currentPosition,currentPosition+CHUNK_SIZE);chunks.push(chunk);// 更新下一次切割的起始位置currentPosition+=CHUNK_SIZE;}console.log(`文件分片完成,共${chunks.length}个分片`);returnchunks;};// 调用示例(在handleUpload中添加)// const file = files[0];// const chunks = createChunks(file);

3.2.3 分片特点

分片过程几乎瞬间完成,原因是:Blob/File对象仅存储文件元信息(名称、大小等),不存储实际文件数据,slice()方法仅生成新的元信息引用,未复制数据。

四、核心:文件唯一标识(Hash计算)

4.1 为什么需要Hash值?

服务端需通过唯一标识区分不同文件,避免以下问题:

  • 文件名重复:不同文件可能重名,相同文件可能改名;

  • 实现秒传:相同内容的文件仅需上传一次;

  • 断点续传:通过Hash值匹配已上传的分片。

4.2 Hash计算方案

采用spark-md5库(轻量、高效,支持浏览器端),基于文件内容生成唯一MD5 Hash值。为优化大文件计算效率,采用「抽样计算」策略:

  1. 第一个和最后一个分片:完整参与计算;

  2. 中间分片:仅抽取头部2字节、中间2字节、尾部2字节参与计算;

  3. 兼顾唯一性与性能,避免全量计算耗时过长。

4.2.1 安装依赖

# npm安装npminstallspark-md5 --save# yarn安装yarnaddspark-md5# pnpm安装pnpmaddspark-md5

4.2.2 实现Hash计算

importSparkMD5from"spark-md5";/** * 计算文件的唯一Hash值(基于内容) * @param chunks 文件分片数组 * @returns 文件Hash值(Promise) */constcalculateFileHash=(chunks:Blob[]):Promise<string>=>{returnnewPromise((resolve,reject)=>{constspark=newSparkMD5.ArrayBuffer();// 基于ArrayBuffer计算constfileReader=newFileReader();// 用于读取分片数据constsampleChunks:Blob[]=[];// 用于抽样的分片数据// 1. 抽样策略:选取部分数据参与计算chunks.forEach((chunk,index)=>{constisFirstChunk=index===0;constisLastChunk=index===chunks.length-1;if(isFirstChunk||isLastChunk){// 首/尾分片:完整加入抽样sampleChunks.push(chunk);}else{// 中间分片:抽取头部2B、中间2B、尾部2BsampleChunks.push(chunk.slice(0,2));sampleChunks.push(chunk.slice(CHUNK_SIZE/2,CHUNK_SIZE/2+2));sampleChunks.push(chunk.slice(CHUNK_SIZE-2,CHUNK_SIZE));}});// 2. 读取抽样数据fileReader.readAsArrayBuffer(newBlob(sampleChunks));// 3. 读取完成后计算HashfileReader.onload=(e)=>{try{constarrayBuffer=e.target?.resultasArrayBuffer;spark.append(arrayBuffer);// 追加数据constfileHash=spark.end();// 生成最终Hash值console.log("文件唯一Hash值:",fileHash);resolve(fileHash);}catch(error){reject("Hash计算失败:"+error);}};// 4. 读取失败处理fileReader.onerror=(error)=>{reject("文件读取失败:"+error);};});};// 调用示例(在handleUpload中添加)// const chunks = createChunks(file);// const fileHash = await calculateFileHash(chunks);

4.2.3上传分片

constuploadChunks=async(chunks:Blob[])=>{constdata=chunks.map((chunk,index)=>{return{fileHash:fileHash.value,chunkHash:fileHash.value+"-"+index,chunk,};});constformDatas=data.map((item)=>{constformData=newFormData();formData.append("fileHash",item.fileHash);formData.append("chunkHash",item.chunkHash);formData.append("chunk",item.chunk);returnformData;});// console.log(formDatas)constmax=6;letindex=0;consttaskPool:any=[];//请求池while(index<formDatas.length){consttask=fetch("/upload",{method:"POST",body:formDatas[index],});taskPool.splice(taskPool.findIndex((item:any)=>item===task))taskPool.push(task)if(taskPool.length===max){awaitPromise.race(taskPool);//taskPool里如果有一个已完成,那么这个Promise状态就会标志城已完成}index++;}awaitPromise.all(taskPool);//方志友请求未完成};

4.3 秒传功能原理

  1. 用户上传文件前,前端先计算文件Hash值并传给服务端;

  2. 服务端查询该Hash值是否已存在(对应已上传的完整文件);

  3. 若存在:直接返回“上传成功”,无需传输文件(秒传效果);

  4. 若不存在:正常执行分片上传流程。

五、断点续传核心逻辑

基于文件Hash值实现,核心是“上传前校验已传分片”:

  1. 前端计算文件Hash后,向服务端发起“查询已传分片”请求;

  2. 服务端返回该文件已上传的分片序号列表(如:[0,1,2,5]);

  3. 前端过滤掉已上传的分片,仅上传未完成的分片;

  4. 所有分片上传完成后,请求服务端组装文件。

六、总结

前端核心职责:文件读取 → 分片切割 → Hash计算 → 分片上传 → 触发组装

后端核心职责:接收分片 → 存储分片 → 校验已传分片 → 组装完整文件

关键技术:Blob.slice()分片、spark-md5 Hash计算、FormData传输分片

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/7 21:52:54

扩展加载失败频发?,PHP 8.6依赖治理全链路解决方案来了

第一章&#xff1a;PHP 8.6扩展依赖管理的挑战与演进随着 PHP 生态系统的持续扩张&#xff0c;扩展模块之间的依赖关系日益复杂。PHP 8.6 虽未引入全新的依赖管理器&#xff0c;但在扩展加载机制和版本兼容性校验方面进行了关键优化&#xff0c;以应对多扩展协同工作时的冲突与…

作者头像 李华
网站建设 2026/2/13 4:01:24

【PHP扩展进阶必看】:5大关键API让你秒懂8.6扩展架构

第一章&#xff1a;PHP 8.6 扩展架构概览 PHP 8.6 的扩展架构在保持向后兼容的同时&#xff0c;进一步优化了模块化设计与性能表现。核心引擎 Zend 引擎的增强使得扩展开发更加高效&#xff0c;同时引入了更严格的类型检查和内存管理机制&#xff0c;提升了运行时稳定性。 扩展…

作者头像 李华
网站建设 2026/2/10 3:09:52

医院数字产科管理平台,数字产科源码,覆盖了孕妇从怀孕开始到生产结束以及产后42天以内的一系列医院保健服务信息

数字产科管理平台是为医院产科量身定制的信息管理系统&#xff0c;旨在提高医院产科的服务质量和管理效率。该平台全面覆盖了孕妇从怀孕开始到生产结束以及产后42天以内的一系列医院保健服务信息。以下是数字产科管理平台的详细介绍&#xff1a;一、系统组成数字产科管理平台主…

作者头像 李华
网站建设 2026/2/13 21:59:20

如何用Symfony 8实现零宕机微服务通信?一线架构师的5点建议

第一章&#xff1a;Symfony 8 微服务通信的零宕机挑战在构建高可用的微服务架构时&#xff0c;Symfony 8 提供了强大的组件支持&#xff0c;如 Messenger 组件和 HTTP Client&#xff0c;使得服务间通信更加灵活。然而&#xff0c;在实际部署中&#xff0c;如何实现服务更新期间…

作者头像 李华