news 2026/1/9 2:16:51

大文件分片上传

作者头像

张小明

前端开发工程师

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/1/5 20:43:53

基于SpringBoot的电影购票系统设计与实现-计算机毕设 附源码 38761

基于SpringBoot的电影购票系统设计与实现 摘要 本研究旨在设计与实现一个基于 SpringBoot 框架的电影购票系统&#xff0c;以满足用户便捷购票和管理员高效管理的需求。系统采用 Java 语言进行开发&#xff0c;结合 SpringBoot 框架的高效性和灵活性&#xff0c;构建了一个稳定…

作者头像 李华
网站建设 2026/1/6 19:36:00

LoRA微调在垂直行业大模型训练中的应用指南

大模型训练正从通用化向垂直行业深度渗透&#xff0c;金融、医疗、能源、通信和政务已成为当前模型训练的五大热门行业方向 。这些行业的大模型应用呈现明显的差异化特点&#xff0c;需要根据各自的数据特性设计合适的微调策略。LoRA(低秩自适应)作为一种参数高效微调方法&…

作者头像 李华
网站建设 2025/12/15 20:11:33

【低代码PHP开发新纪元】:属性绑定核心技术揭秘与实战应用

第一章&#xff1a;低代码PHP开发新纪元的背景与趋势随着企业数字化转型进程加速&#xff0c;传统编码模式在快速迭代需求面前逐渐显露出开发周期长、人力成本高等问题。低代码平台应运而生&#xff0c;成为连接业务需求与技术实现的桥梁。PHP作为长期活跃于Web开发领域的语言&…

作者头像 李华
网站建设 2025/12/15 20:07:52

面试复习题--Android和iOS设备上的绘制原理

深入理解Flutter 在 Android 和 iOS 设备上的绘制底层原理,核心是搞清楚 Flutter 如何跨这两个系统实现统一的 UI 渲染,以及它在两端分别与系统底层渲染框架的交互逻辑 ——Flutter 的核心特点是「自绘引擎(Skia)接管全渲染流程」,避开了 Android/iOS 的原生 UI 组件系统,…

作者头像 李华
网站建设 2025/12/31 17:14:11

Kafka日志迁移与查询机制解析

Kafka Broker 端用于管理日志目录&#xff08;Log Directory&#xff09;迁移和查询 的核心逻辑&#xff0c;分别对应两个关键 API&#xff1a; alterReplicaLogDirs(...)&#xff1a;实现 KIP-113 中的 ALTER_REPLICA_LOG_DIRS 请求&#xff0c;用于将某个分区的日志从一个磁盘…

作者头像 李华