🚀 前端大文件上传终极指南:分片、断点续传与极致优化

1,029 阅读2分钟

🔍 为什么大文件上传是个挑战?

传统文件上传方式(简单 **<input type="file">** + FormData)在遇到大文件时会有这些问题:

  • 内存溢出:浏览器可能崩溃(尤其是 1GB+ 文件)
  • 网络不稳定:上传失败后需要从头开始
  • 无进度反馈:用户不知道要等多久

👉 解决方案 = 分片上传 + 断点续传 + 并行传输!

📦 核心方案:分片上传(Chunk Upload)

1. 前端分片处理(使用 File API)

const CHUNK_SIZE = 5 * 1024 * 1024; // 5MB 每片

async function chunkFile(file) {
  const chunks = [];
  let start = 0;
  
  while (start < file.size) {
    const end = Math.min(start + CHUNK_SIZE, file.size);
    const chunk = file.slice(start, end); // Blob.slice 切割文件
    chunks.push(chunk);
    start = end;
  }
  
  return chunks;
}

2. 上传分片(并发控制)

async function uploadChunks(chunks, fileHash) {
  const MAX_CONCURRENT = 3; // 最大并发数
  const queue = [];
  
  for (let i = 0; i < chunks.length; i++) {
    const formData = new FormData();
    formData.append('chunk', chunks[i]);
    formData.append('hash', `${fileHash}-${i}`);
    formData.append('total', chunks.length);
    
    queue.push(
      fetch('/upload', { method: 'POST', body: formData })
        .then(res => res.json())
    );
    
    // 控制并发
    if (queue.length >= MAX_CONCURRENT) {
      await Promise.race(queue);
    }
  }
  
  await Promise.all(queue); // 等待所有分片完成
}

⚡ 进阶优化:断点续传 + 秒传

1. 文件指纹生成(SparkMD5)

import SparkMD5 from 'spark-md5';

async function calculateFileHash(file) {
  return new Promise((resolve) => {
    const spark = new SparkMD5.ArrayBuffer();
    const reader = new FileReader();
    
    reader.onload = (e) => {
      spark.append(e.target.result);
      resolve(spark.end());
    };
    
    reader.readAsArrayBuffer(file);
  });
}

2. 服务端检查(避免重复上传)

async function checkFileExist(fileHash) {
  const res = await fetch(`/check?hash=${fileHash}`);
  return res.json(); 
  /* 返回示例:
    { exists: true, uploadedChunks: [0,1,2] }
  */
}

🛠️ 技术选型对比

💡 用户体验优化技巧

1. 进度条显示(基于 axios/fetch)

const { data } = await axios.post('/upload', formData, {
  onUploadProgress: (progressEvent) => {
    const percent = Math.round(
      (progressEvent.loaded / progressEvent.total) * 100
    );
    console.log(`上传进度: ${percent}%`);
  }
});

2. 拖拽上传 + 预览(Dropzone.js 示例)

<div id="dropzone">
  <p>拖拽文件到此处上传</p>
  <img id="preview" style="max-width: 200px;">
</div>

<script>
  dropzone.addEventListener('drop', (e) => {
    const file = e.dataTransfer.files[0];
    const preview = document.getElementById('preview');
    preview.src = URL.createObjectURL(file);
  });
</script>

📈 性能对比:优化前后

🚀 扩展阅读

💬 互动话题:
你在项目中如何处理大文件上传? 遇到过哪些坑?欢迎分享! 💡

(如果觉得有用,请点赞/收藏支持!❤️)