news 2026/10/7 15:29:06

前端大文件切片上传怎么做?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
前端大文件切片上传怎么做?

一、大文件切片上传怎么设计?

大文件上传我一般会拆成:切片、并发上传、断点续传、秒传、失败重试和最终校验这几块,核心是让上传可以暂停、失败后继续,而且不用从头再传。


面试官继续追问后的展开

1. 为什么要切片?

如果一个 1.5GB 视频直接上传:

1.5GB File ↓ 一个 HTTP 请求 ↓ 上传失败 ↓ 基本只能重新上传

切片之后:

1.5GB File ↓ ┌────┬────┬────┬────┬────┬────┐ │ 0 │ 1 │ 2 │ 3 │ ...│ N │ └────┴────┴────┴────┴────┴────┘ ↓ 分别上传 ↓ 服务端保存各个 chunk ↓ 全部完成 ↓ 按顺序合并

前端核心 API 就是:

constchunk=file.slice(start,end);

但slice()本身根本不是这道题的难点。

真正的难点是:

切完以后,怎么可靠地把这些 chunk 上传、恢复、校验并最终合并。


2. 切片之后为什么不能直接Promise.all()?

这是第一个高频追问。

比如:

awaitPromise.all(chunks.map(chunk=>upload(chunk)));

代码虽然简单,但实际上等于:

有多少切片就同时发多少请求。但不应该一次性把所有 chunk 都启动上传。

如果一个 1GB 文件按 1MB 切,就是大约 1024 个请求。

这会带来:

  • 浏览器连接和请求调度压力
  • 服务端瞬时并发压力
  • 带宽竞争
  • 大量 Promise 和请求对象
  • 某个网络环境下整体吞吐反而下降

所以实际应该做:固定并发数 + 任务队列。

例如:

chunk queue │ ┌───────┼───────┐ ↓ ↓ ↓ worker1 worker2 worker3 ↓ ↓ ↓ chunk0 chunk1 chunk2 ↓ 完成后继续取下一个

例如限制:

constCONCURRENCY=4;

4 个请求完成一个,再从队列取下一个。

这里还有一个容易被问的点

并发数不是固定的“4~6 个最佳”。

它取决于:

  • HTTP 版本
  • 浏览器
  • 网络质量
  • chunk 大小
  • 服务端处理能力
  • 带宽
  • 是否走 CDN / 对象存储

所以高级回答不要说:

HTTP/1.1 最多只能 6 个,所以设置 6。

更准确的说法是:

HTTP/1.1 同源连接数受浏览器限制,而 HTTP/2 可以在单连接上复用多个请求;实际并发数还是应该根据网络和服务端吞吐做控制,而不是死记一个数字。


3. 怎么实现断点续传?

这是这道题真正开始拉开差距的地方。

错误思路

浏览器 localStorage ↓ 记录 chunk 0、1、2、3 已上传

这个方案只能解决:

同一台设备、同一个浏览器、缓存没有被清掉的情况下恢复。

但它解决不了:

清缓存 换浏览器 换电脑 换手机 重新登录

所以断点续传真正应该以服务端状态为准。


正确流程

用户选择文件:

File ↓ 计算 fileHash ↓ POST /upload/check │ ├── 文件已经存在 → 秒传 │ └── 文件不存在 ↓ 返回已上传 chunks ↓ 前端过滤已上传 chunk ↓ 只上传缺失 chunk

例如服务端返回:

{"uploadedChunks":[0,1,2,5,6]}

那么:

0 ✓ 1 ✓ 2 ✓ 3 → 上传 4 → 上传 5 ✓ 6 ✓ 7 → 上传

这样网络断在 99%,重新上传的时候:不是从 0 开始,而是继续上传缺失部分。


4. 服务端怎么知道这是哪个文件?

这里就需要一个非常关键的概念:

文件唯一标识

通常可以基于:

fileHash

再结合:

chunkIndex

来定位 chunk。

例如:

fileHash = abc123 chunk: abc123_0 abc123_1 abc123_2 ...

或者服务端使用:

uploadId + chunkIndex

这种设计通常更稳妥。

因为:

文件身份和一次上传任务的身份,不一定应该完全绑定在一起。

例如同一个文件:

fileHash = abc123

可能同时出现:

uploadId=A uploadId=B

这就涉及后面的并发上传同一个文件问题。


5. 秒传到底是什么?

秒传本质上不是“上传很快”。而是:

服务端发现自己已经有这个文件,所以直接复用已有文件,不再上传文件内容。

流程:

用户选择文件 ↓ 计算 fileHash ↓ POST /upload/check ↓ 服务端查询 fileHash ↓ 存在 ↓ 直接返回成功

所以:

1.5GB 文件 ↓ 只需要上传/计算文件指纹 ↓ 服务端发现已有 ↓ 直接完成

这才叫秒传。


6. 那么 fileHash 怎么算?

这里又是一个很容易被忽略的性能问题。如果你直接:

读取整个 1.5GB 文件 ↓ 计算 MD5

就可能造成:

  • CPU 消耗
  • 主线程卡顿
  • 内存压力
  • 用户界面卡顿

所以实际可以:

File ↓ 分块读取 ↓ Hash Worker ↓ 计算 fileHash

也可以使用:

Web Worker

把计算放到 Worker 中,避免阻塞主线程。


7. 文件 Hash 和 Chunk Hash 是一回事吗?

不是。可以分别存在:

fileHash

和:

chunkHash

例如:

fileHash = SHA256(file) chunk0Hash = SHA256(chunk0) chunk1Hash = SHA256(chunk1) chunk2Hash = SHA256(chunk2)

这样服务端可以进一步校验:

上传 chunk ↓ 服务端计算/校验 chunkHash ↓ 一致 → 接收 不一致 → 重新上传这个 chunk

这样比等到整个文件合并之后才发现错误更加高效。


8. 如果某一个切片上传失败怎么办?

不能因为一个 chunk 失败:

chunk 0 ✓ chunk 1 ✓ chunk 2 ✗ chunk 3 ✓ chunk 4 ✓

然后:

全部重新上传

应该:只重试失败的 chunk。

例如:

chunk2 ↓ 失败 ↓ retry 1 ↓ 失败 ↓ retry 2 ↓ 失败 ↓ retry 3 ↓ 仍然失败 ↓ 任务失败

重试一般配合:

指数退避

避免网络异常的时候大量请求同时再次打到服务端。


9. 网络断了,怎么恢复?

这里其实有两种情况。

情况一:页面没刷新

前端可以保留当前上传任务状态:

uploadId fileHash uploadedChunks

网络恢复之后:

重新查询服务端 ↓ 获取最新 uploadedChunks ↓ 继续上传缺失 chunk

情况二:页面刷新甚至换设备

这时候前端自己的内存状态全部没了。所以仍然依赖:

fileHash ↓ 服务端查询 ↓ 返回已经存在的 chunks ↓ 继续上传

这也是为什么:

localStorage 最多只能做客户端辅助缓存,不能作为断点续传的最终依据。


10. 两个用户同时上传同一个文件怎么办?

这是非常好的追问。

例如:

User A ── upload ──┐ ├── fileHash = abc123 User B ── upload ──┘

服务端不能简单认为:

A 创建文件 B 又创建一个完全相同的文件

应该围绕:

fileHash

做去重。例如:

fileHash = abc123 ↓ 服务端发现已经存在 ↓ 直接复用

但这里又有一个更深的问题:

文件可能正在上传,还没有最终完成。

例如:

User A chunk 0 ✓ chunk 1 ✓ chunk 2 ✓ ... User B ↓ 发现 abc123 正在上传 ↓ 复用已有上传进度

这就涉及:

  • upload session
  • 状态机
  • 并发控制
  • 幂等
  • 数据库唯一约束
  • 分布式锁 / CAS

11. Chunk 上传接口为什么必须幂等?

例如:

POST /upload/chunk

网络情况可能是:

客户端发送 chunk ↓ 服务端已经成功保存 ↓ 响应丢失 ↓ 客户端以为失败 ↓ 再次上传同一个 chunk

如果服务端没有幂等设计,就可能:

重复保存

所以应该让:

uploadId + chunkIndex

或者:

fileHash + chunkIndex

成为唯一定位。再次上传相同 chunk 时:

已经存在 → 直接返回成功 不存在 → 保存

这就是高级工程设计里非常重要的:幂等性。


12. 所有 Chunk 都上传完之后呢?

这时候才进入:

Merge

流程:

chunk0 ✓ chunk1 ✓ chunk2 ✓ ... chunkN ✓ ↓ POST /upload/complete ↓ 服务端确认所有 chunk 都存在 ↓ 按 chunkIndex 顺序合并 ↓ 生成最终文件

注意:前端不能简单告诉服务端“我上传完了”,服务端应该自己再次确认。


13. 合并的时候怎么保证顺序?

例如:

chunk0 chunk1 chunk2 chunk3

必须按照:

0 → 1 → 2 → 3

合并。不能按照上传完成顺序:

2 → 0 → 3 → 1

因为:

上传顺序和文件顺序是两回事。

这也是为什么每个 chunk 必须带:

chunkIndex

14. 合并完成后怎么校验完整性?

这是非常关键的一问。

最直接的方案是:

原文件 fileHash ↓ 上传 + 合并 ↓ 服务端对最终文件计算 hash ↓ 比较 ↓ 一致 → 成功 不一致 → 异常

例如:

Client: SHA-256(originalFile) ↓ abc123 Server: SHA-256(mergedFile) ↓ abc123 ↓ 一致 ↓ 上传成功

15. 如果最终 Hash 不一致,是全部重传还是部分重传?

不能简单地说“部分重传”。
因为:

fileHash 不一致

只能说明:

最终文件和原文件不一致。

它本身不能直接告诉你:

“到底是 chunk 37 错了。”

如果每个 chunk 都有:

chunkHash

服务端就可以进一步定位:

chunk0 ✓ chunk1 ✓ chunk2 ✓ chunk3 ✗ ...

那么:

只重新上传 chunk3。

所以更完整的设计是:

fileHash │ ↓ 整体完整性校验 │ 不一致? │ ↓ chunkHash 对比 │ ┌────────┴────────┐ ↓ ↓ 找到异常 chunk 无法定位 ↓ ↓ 部分重传 整体重新处理

16. 合并是不是一定要同步完成?

不一定。如果是:

10MB 文件

同步合并可能完全没问题。

但如果是:

10GB 50GB 100GB

合并可能持续很长时间。这时候更合理的是:

POST /upload/complete ↓ 创建 merge task ↓ 立即返回 ↓ 后台异步合并 ↓ 状态:merging ↓ 完成 ↓ 状态:completed

前端可以:

轮询

或者:

WebSocket SSE

接收状态变化。

注意:WebSocket/SSE 不是大文件上传本身的必要条件,只是用于把异步合并状态通知给前端。


17. 整个链路怎么串起来?

这是这道题最值得背下来的一张图:

用户选择文件 │ ↓ 计算 fileHash │ ↓ /upload/check │ ┌──────────┴──────────┐ ↓ ↓ 文件已经存在 文件不存在 ↓ ↓ 秒传 返回 uploadId │ ↓ 查询已上传 chunks │ ↓ 过滤已上传 chunk │ ↓ 并发队列上传 │ ┌─────────────────┴──────────────┐ ↓ ↓ 成功 失败 │ │ │ 自动重试/指数退避 │ │ └──────────────┬─────────────────┘ ↓ 全部 chunk 完成 │ ↓ /upload/complete │ ↓ 服务端合并 │ ↓ 最终文件完整性校验 │ ┌─────────┴─────────┐ ↓ ↓ 一致 不一致 ↓ ↓ 成功 定位异常 chunk ↓ 重传

18. 这道题真正考什么?

可以把面试官的追问链压缩成:

会切片 ↓ 会并发控制吗? ↓ 会断点续传吗? ↓ 换电脑还能续传吗? ↓ 能秒传吗? ↓ 两个用户同时上传呢? ↓ 请求重试会不会重复? ↓ 合并怎么保证顺序? ↓ 怎么校验完整性? ↓ 校验失败怎么定位? ↓ 大文件合并会不会阻塞? ↓ 异常状态怎么恢复?

所以它考的其实不是:

Blob.slice()会不会。

而是:

你能不能把一个“文件上传 API”设计成一个可靠的端到端系统。


19. 面试官最容易继续追问的几个点

追问 1:localStorage能不能实现断点续传?

不能作为最终方案。

它只能保存客户端状态,清缓存、换浏览器、换设备都会丢。

真正的上传进度应该以服务端已保存的 chunk 状态为准。


追问 2:为什么不用Promise.all()?

因为切片数量可能非常大,不能让所有请求同时发出去,要用并发队列控制同时上传的数量。


追问 3:为什么需要chunkIndex?

因为上传完成顺序是不确定的,服务端必须靠chunkIndex恢复文件原来的顺序。


追问 4:为什么需要fileHash?

主要解决两个问题:

1. 判断是不是同一个文件 2. 实现秒传和断点续传

追问 5:为什么还需要chunkHash?

因为fileHash只能告诉你最终文件对不对,chunkHash可以进一步定位到底哪个切片有问题。


追问 6:上传接口为什么要幂等?

因为:

请求成功 ↓ 响应丢失 ↓ 客户端重试

可能导致同一个 chunk 被重复上传。所以服务端要能够安全处理重复请求。


追问 7:HTTP/2 之后还需要控制并发吗?

需要。HTTP/2 解决的是多个请求共享连接的问题,但不代表服务端、浏览器和网络可以无限并发。并发控制依然是为了控制:

带宽 CPU 内存 服务端压力 请求队列

20. 最后给你一版真正适合面试开口的答案

大文件上传我一般不会只考虑“切片”,而是把它做成一套完整的上传链路:切片、并发控制、断点续传、秒传、失败重试、合并和完整性校验。

前端先用Blob.slice()把文件切成多个 chunk,每个 chunk 带上uploadId、chunkIndex等信息,然后通过并发队列控制同时上传的请求数量,不能直接Promise.all()把所有切片一起发出去。

断点续传不能只依赖localStorage,真正应该以服务端状态为准。用户重新上传时,先根据fileHash查询服务端已经有哪些 chunk,只上传缺失的部分,所以即使刷新页面、清缓存甚至换一台电脑,也能继续。

秒传也是基于fileHash:服务端如果已经存在这个文件,就直接复用已有文件,不需要再次上传内容。

上传过程中,单个 chunk 失败只重试这个 chunk,并且接口要保证幂等,避免网络重试造成重复数据。

所有 chunk 上传完成后,再通知服务端按chunkIndex顺序合并。合并完成后校验最终文件的fileHash;如果不一致,再通过chunkHash定位异常 chunk,优先做部分重传。

所以这道题真正考的不是会不会slice(),而是能不能把大文件上传做成一个可恢复、可重试、可校验、能处理并发和异常的完整方案。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/7 15:26:07

Agent Runtime 是什么?为什么说它是 AI 下半场的「操作系统」

如果说模型是智能体的「大脑」,那 Agent Runtime 就是让它持续、安全、可观测地「活着」的那层基础设施。2026 年,AI 圈的热词已经从「大模型」转向了「Agent」。但真正上手做过 Agent 项目的人会发现一个尴尬的事实:模型很好调,D…

作者头像 李华
网站建设 2026/10/7 15:25:23

Claude Code 营销技能包实战:SEO 审计与 FAQPage 结构化数据生成

1. 从"marketingskills"这个仓库名说起:它到底想解决什么问题第一次看到marketingskills这个名字,我的直觉是:这大概率不是一个普通的营销工具库,而是一套面向 AI Agent 的"技能包"。事实也确实如此。它本质上…

作者头像 李华
网站建设 2026/10/7 15:23:41

Sopracciglio RP2040开源徽章:真实场景下的嵌入式系统工程实践

1. 这块“眉毛”徽章控制器,到底在解决什么真实问题?Sopracciglio RP2040——光看名字就带着一股意大利语的俏皮感,“Sopracciglio”直译是“眉毛”,但在这里它不是指人体部位,而是项目作者为这块开源硬件起的代号&…

作者头像 李华
网站建设 2026/10/7 15:22:59

TEN-framework 内嵌 libwebsockets 的 SMD 系统消息分发机制详解

人工智能AI Agent多模态语音AI 应用 【免费下载链接】ten-framework Open-source framework for conversational voice AI agents 项目地址: https://gitcode.com/TEN-framework/ten-framework 点击查看 免费下载 SMD(System Message Distribution&…

作者头像 李华
网站建设 2026/10/7 15:21:44

pytest 入门与实践:从第一个测试到可维护的测试体系

pytest 入门与实践:从第一个测试到可维护的测试体系 面向 Python 开发者与测试工程师的实战指南,涵盖安装、发现规则、断言、fixture、参数化、标记、配置、常用命令和团队实践。 阅读对象:刚开始写 Python 自动化测试的开发者、测试工程师&a…

作者头像 李华