文件分片上传与断点续传

选择文件后,前端按固定大小切片上传。刷新页面或中断后再次选择同一个文件,会先查询服务端已有分片,只补传缺失部分,最后由 Python 服务端合并并校验 SHA-256。

上传演示

分片大小:1MB,并发数:3,支持暂停、继续、重试和断点续传。

--文件名
--文件大小
--分片数量
未选择状态

运行日志

实现了哪些功能

  • 前端使用 Blob.slice 将大文件切成多个 1MB 分片。
  • 上传前计算文件 SHA-256,生成稳定的 uploadId
  • 通过 /api/status 查询服务端已有分片,实现断点续传。
  • 通过 /api/upload-chunk 并发上传缺失分片。
  • 通过 /api/merge 通知服务端按顺序合并文件并校验 hash。
  • 页面支持暂停、继续、失败重试、进度展示和运行日志。

前端主要代码段

核心是切片、查询已有分片、只上传缺失分片。

const chunks = [];
for (let start = 0; start < file.size; start += CHUNK_SIZE) {
  chunks.push(file.slice(start, start + CHUNK_SIZE));
}

const status = await fetch(`/api/status?uploadId=${uploadId}`);
const uploaded = new Set((await status.json()).uploadedChunks);
const pending = chunks
  .map((chunk, index) => ({ chunk, index }))
  .filter((item) => !uploaded.has(item.index));

Python 主要代码段

服务端把每个分片保存到 uploads/chunks/{uploadId}/{index},合并时按 index 顺序写入最终文件。

with chunk_path.open('wb') as file:
    remaining = content_length
    while remaining > 0:
        data = self.rfile.read(min(1024 * 1024, remaining))
        file.write(data)
        remaining -= len(data)

with target_path.open('wb') as target:
    for index in range(total_chunks):
        with (upload_dir / str(index)).open('rb') as chunk:
            shutil.copyfileobj(chunk, target)

整体逻辑

  • 选择文件后,浏览器计算文件 hash,并根据 hash、文件名、大小生成上传 ID。
  • 页面向服务端查询这个上传 ID 已经上传过哪些分片。
  • 前端跳过已存在分片,只上传缺失分片;如果网络中断,下次会继续补传。
  • 所有分片上传成功后,请求服务端合并。
  • 服务端检查分片是否完整,按序合并,再计算最终文件 hash 做完整性校验。

注意事项

  • 生产环境要限制文件大小、文件类型、用户权限和上传目录访问范围。
  • 分片大小不是越小越好,过小会增加请求数量,过大会影响失败重试成本。
  • 需要定期清理长期未合并的临时分片,避免磁盘被占满。
  • 真实项目建议增加鉴权、秒传、限速、病毒扫描、对象存储直传和数据库记录。
  • 本示例使用标准库实现教学逻辑,没有处理复杂 multipart 表单和分布式锁。