大文件切片上传

秒是不用传,快传是接着传

  • 文件切片
  • 并发控制
  • 状态记录
文件切片

File.slice() 将大文件切分为若干小文件

上传与记录

并发上传这些切片,同时在前端缓存记录下哪些已上传成功

通知合并

所有切片上传完成后,前端发送一个请求通知后端,让后端把这些切片按顺序拼回一个完整的文件

文件切片与状态管理.js 复制代码
// 1、生成文件hash
const fileHash = await calculateFileHash(file); 
// 2、分片大小
const CHUNK_SIZE = 2 * 1024 *1024;

const chunks = [];
let start = 0;
// 3、分片操作
while (start < file.size) {
    const end = Math.min(start + CHUNK_SIZE, file.size);
    chunks.push({
        file: file.slice(start, end),
        index: chunks.length,
        start,
        end,
        hash: fileHash // 用于服务端校验
    });
    start = end;
}
// 4、从本地存储获取已上传的分片记录
const uploadChunks = JSON.parse(localStorage.getItem(fileHash) || '[]');
并发控制与断点续传.js 复制代码
// 5. 找出待上传的分片
const pendingChunks = chunks.filter(chunk => !uploadedChunks.includes(chunk.index));

// 6. 控制并发数(比如3个并发)
const CONCURRENCY = 3;
async function uploadWithConcurrency() {
  for (let i = 0; i < pendingChunks.length; i += CONCURRENCY) {
    const batch = pendingChunks.slice(i, i + CONCURRENCY);
    // 并发上传这一批
    await Promise.all(batch.map((chunk) => uploadChunk(chunk));
  }
}

// 7. 单个分片上传函数
async function uploadChunk(chunk) {
  const formData = new FormData();
  formData.append('file', chunk.file);
  formData.append('index', chunk.index);
  formData.append('hash', chunk.hash);
  formData.append('total', chunks.length); // 总分片数

  try {
    const res = await Request({
      url: 'https://your-api.com/upload/chunk',
      method: 'POST',
      data: formData,
      header: { 'Content-Type': 'multipart/form-data' }
    });

    if (res.data.success) {
      // 上传成功,记录到本地存储
      uploadedChunks.push(chunk.index);
      localStorage.setItem(fileHash, JSON.stringify(uploadedChunks));
      
      // 计算并更新进度
      const progress = (uploadedChunks.length / chunks.length) * 100;
      console.log(`上传进度: ${progress.toFixed(2)}%`);
    }
  } catch (error) {
    console.error(`分片 ${chunk.index} 上传失败:`, error);
    // 这里可以加入重试逻辑,例如重试3次
  }
}

// 8. 开始上传
uploadWithConcurrency().then(() => {
  // 所有分片上传完成,通知服务端合并
  Request({
    url: 'https://your-api.com/upload/merge',
    method: 'POST',
    data: {
      hash: fileHash,
      fileName: file.name,
      totalChunks: chunks.length
    }
  }).then(res => {
    console.log('文件上传成功!URL:', res.data.fileUrl);
    // 上传成功,清除本地记录
    localStorage.removeItem(fileHash);
  });
});

服务端要做的事

前端的工作如上,后端需要配合提供三个接口,这也是面试中常被问到的设计点

  1. /upload/chunk :接收单个分片。通常会以文件hash_分片索引的格式临时存储。
  2. /upload/check(可选):查询已上传的分片。前端启动时调用,快速实现"断点续传"和"秒传"。
  3. /upload/merge:所有分片完成后,后端将临时分片合并,生成最终文件。

💡 高级优化与面试加分项

当你把这套逻辑讲清楚后,能主动提出以下优化点,会让面试官眼前一亮:

  • 秒传 :在初始化上传前,先根据文件hash请求后端。如果文件已存在,直接返回成功,一秒完成
  • Web Worker :将计算文件hash这种CPU密集型任务放到Web Worker里执行,避免阻塞页面渲染
  • 本地存储选型 :小文件、少量记录用localStorage;大文件、复杂记录用IndexedDB
  • 暂停/恢复 :通过AbortController来取消进行中的请求,实现暂停功能。恢复时,只需重新调用上传逻辑,pendingChunks会自动过滤掉已上传的部分
相关推荐
驭渊的小故事7 分钟前
Spring Boot 注解详解01
java·前端·spring boot
pe7er12 分钟前
引子
前端·后端·架构
恋猫de小郭20 分钟前
CPF-Flutter 社区提出折叠场景分栏(平行视界) 方案
android·前端·flutter
aixingpan30 分钟前
aixingpan.cn API开发文档:api_docs_trichart_natal_marx_transit2接口指南
前端·php
csj5031 分钟前
前端基础之《React(13)—表单绑定、列表渲染》
前端·react.js
csj5032 分钟前
前端基础之《React(12)—条件渲染》
前端·react.js
IT_陈寒44 分钟前
Java序列化坑了我三天,原来问题出在这个默认方法
前端·人工智能·后端
雪芽蓝域zzs1 小时前
第四十八节:驾驶舱大屏进阶:数字滚动动画 + 渐变边框美化
前端·javascript·vue.js
ly76891 小时前
Spring Boot 3.2 虚拟线程在 Web 层与 JDBC 池的实践边界
前端·spring boot·后端·tomcat·jdbc·虚拟线程
Bingo_BIG1 小时前
LOV列表的查询
前端·javascript·lov