news 2026/3/9 21:43:52

大文件分片上传

作者头像

张小明

前端开发工程师

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/3/8 20:10:43

请求拦截不再难,Symfony 8拦截器实现原理与最佳实践全解析

第一章&#xff1a;请求拦截不再难&#xff0c;Symfony 8拦截器实现原理与最佳实践全解析在现代 Web 应用开发中&#xff0c;对 HTTP 请求进行统一处理是构建高可维护性系统的关键环节。Symfony 8 通过事件监听机制和中间件式设计&#xff0c;提供了灵活而强大的请求拦截能力&a…

作者头像 李华
网站建设 2026/3/8 16:15:30

RAG文本分块策略:优化LLM的知识访问效率

分块并非简单的预处理步骤&#xff0c;而是RAG流水线的核心支柱。优质文本块是有意义、独立完整的知识单元&#xff0c;而劣质文本块只是会误导LLM的孤立碎片。在检索增强生成&#xff08;RAG&#xff09;系统中&#xff0c;若说检索模块是搜索引擎&#xff0c;那么分块&#x…

作者头像 李华
网站建设 2026/3/8 16:19:19

桌面那么点大,性能它偏要狂

AI 基建遍地开花&#xff0c;内存带宽“饭量”暴增&#xff0c;可传统产能却在偷偷“减肥”。于是 DRAM 价格一路高歌——“涨”声响起&#xff0c;根本停不下来&#xff01;科技巨头们囤货如囤年货&#xff0c;闪存和内存条顿时成了超市里的限量薯片&#xff0c;货架越来越空。…

作者头像 李华
网站建设 2026/3/8 11:17:02

基于51单片机的智能水表系统设计

基于51单片机的智能水表系统设计 一、系统设计背景与需求分析 传统机械水表依赖人工抄表&#xff0c;存在效率低下、数据误差大、抄表周期长等问题&#xff0c;尤其在高层住宅、老旧小区中&#xff0c;人工入户抄表不仅耗费人力&#xff0c;还易引发用户隐私纠纷。此外&#xf…

作者头像 李华
网站建设 2026/3/8 4:55:43

基于单片机的交通控制系统

第一章 系统整体架构设计 基于单片机的交通控制系统&#xff0c;核心目标是实现路口交通信号灯的精准控制与车流自适应调节&#xff0c;整体架构分为核心控制模块、信号灯驱动模块、车流检测模块、人机交互模块四大单元。核心控制模块以单片机为核心&#xff0c;负责处理车流数…

作者头像 李华
网站建设 2026/3/8 4:42:47

永磁同步电机PMSM 5 - 7次谐波注入降低转矩脉动实践

永磁同步电机PMSM电机5 -7次谐波注入降低转矩脉动&#xff08;参考文献搭建&#xff09; ①控制思路&#xff1a;以抑制电机电流中较大的 5、7 次谐波分量为目的&#xff0c;实时 提取谐波电流&#xff0c;注入谐波电压来补偿抵消电机运行时电机电流中的谐波&#xff0c;通过抑…

作者头像 李华