Skip to content

大文件分片上传与断点续传实现指南


目录

  1. 核心原理
  2. 前端实现
  3. 后端实现
  4. 时序图
  5. 优化与注意事项

1. 核心原理

1.1 分片上传

  • 将大文件切割为多个小分片(如 5MB/片)
  • 并行上传分片,提升传输效率

1.2 断点续传

  • 通过文件哈希值唯一标识文件
  • 记录已上传分片索引
  • 网络中断后跳过已传分片

1.3 自动合并

  • 服务器检测分片完整性
  • 按序号合并分片生成完整文件

2. 前端实现

2.1 文件分片与哈希计算

javascript
// 文件分片
function splitFile(file, chunkSize = 5 * 1024 * 1024) {
  const chunks = [];
  let start = 0;
  while (start < file.size) {
    chunks.push(file.slice(start, start + chunkSize));
    start += chunkSize;
  }
  return chunks;
}

// 计算文件哈希(使用 SparkMD5)
async function calculateFileHash(file) {
  // ...(同前文实现)
}

2.2 上传控制(带并发限制)

javascript
class Uploader {
  constructor() {
    this.MAX_CONCURRENT = 3;  // 最大并发数
    this.retryLimit = 3;      // 分片重试次数
  }

  async upload(file) {
    const fileHash = await calculateFileHash(file);
    const { uploadedChunks } = await checkProgress(fileHash);
    
    const chunks = splitFile(file);
    const queue = chunks
      .map((chunk, index) => ({ chunk, index }))
      .filter(({ index }) => !uploadedChunks.includes(index));

    // 并发控制
    while (queue.length > 0) {
      const tasks = queue.splice(0, this.MAX_CONCURRENT);
      await Promise.all(tasks.map(task => this.uploadChunk(task)));
    }
  }

  async uploadChunk({ chunk, index }) {
    for (let attempt = 0; attempt < this.retryLimit; attempt++) {
      try {
        const formData = new FormData();
        formData.append('chunk', chunk);
        formData.append('hash', fileHash);
        formData.append('index', index);
        
        await fetch('/api/upload', { method: 'POST', body: formData });
        saveProgress(fileHash, index);
        return;
      } catch (error) {
        if (attempt === this.retryLimit - 1) throw error;
      }
    }
  }
}

3. 后端实现

3.1 接口设计

端点方法功能
/api/uploadPOST上传分片
/api/progressGET查询上传进度
/api/merge-checkGET检查合并状态(可选)

3.2 核心逻辑

javascript
// 分片存储结构
uploads/
├─ {fileHash}/
│  ├─ 0
│  ├─ 1
│  └─ ... (分片文件)

// 自动合并实现
app.post('/api/upload', (req, res) => {
  const { hash, index, total } = req.body;
  
  // 存储分片
  saveChunk(hash, index, req.file);
  
  // 检查合并条件
  if (isUploadComplete(hash, total)) {
    mergeChunks(hash); // 异步执行合并
  }
  
  res.sendStatus(200);
});

// 合并操作(异步)
async function mergeChunks(hash) {
  const chunkDir = path.join(UPLOAD_DIR, hash);
  const chunks = fs.readdirSync(chunkDir).sort((a, b) => a - b);
  
  await new Promise((resolve) => {
    const writeStream = fs.createWriteStream(`${UPLOAD_DIR}/${hash}.file`);
    chunks.forEach(chunk => {
      writeStream.write(fs.readFileSync(path.join(chunkDir, chunk)));
      fs.unlinkSync(path.join(chunkDir, chunk)); // 删除分片
    });
    writeStream.end(() => {
      fs.rmdirSync(chunkDir);
      resolve();
    });
  });
}

4. 时序图


5. 优化与注意事项

5.1 性能优化

  • 分片大小调整:根据网络状况动态调整分片大小(1MB~10MB)
  • 内存管理:使用流式处理避免大文件内存溢出
  • CDN加速:分片上传使用不同CDN节点

5.2 可靠性保障

  • 分片校验:对每个分片进行MD5校验
  • 断点记录:使用IndexedDB存储本地进度
  • 过期清理:服务器定时清理未完成的上传任务

5.3 安全防护

  • 身份验证:上传接口添加JWT校验
  • 大小限制:限制单文件最大尺寸(如10GB)
  • 防重放攻击:分片上传添加时间戳签名

5.4 扩展功能

  • 进度恢复:页面刷新后自动恢复上传
  • 秒传功能:服务器存在相同哈希文件时直接返回
  • 分片压缩:在客户端对分片进行gzip压缩

通过以上方案,可实现支持百万级大文件上传、网络中断自动恢复、服务器负载均衡的高可靠性文件上传系统。实际部署时建议结合对象存储(如AWS S3)实现分片直传,进一步降低服务器压力。