分片上传需前后端协同实现:前端按2–5mb切片并传fileid、chunkindex、totalchunks及哈希值;后端绕过iformfile,流式读取+幂等写入+分片校验+状态持久化+及时清理。

分片上传必须自己实现前后端协同
ASP.NET Core 默认的 IFormFile 无法处理 GB 级文件,服务端会直接拒绝或超时。分片不是加个配置就能开的开关,它要求前端按固定大小切片、携带序号和唯一标识(如 fileId),后端按序接收、暂存、校验,最后合并。跳过任一环节都会导致文件损坏或丢失。
- 前端切片常用
File.slice()(浏览器)或FileStream.Read()+ 缓冲区(桌面端),单片建议 2–5 MB,太小增加 http 开销,太大降低失败重传效率 - 每次请求必须带至少三个关键字段:
fileId(整个文件唯一 ID)、chunkIndex(从 0 开始)、totalChunks - 后端不能依赖请求顺序——HTTP 不保证顺序到达,需用
fileId+chunkIndex做幂等写入,重复上传同一片应忽略而非报错
后端接收分片要绕过模型绑定和默认限制
直接用 [FromForm] 或 IFormFile 会触发 ASP.NET Core 的完整文件读取和内存缓冲,大文件直接 OOM。必须改用 Request.Body 流式读取,并手动解析 multipart boundary。
- 禁用默认限制:在
Startup.cs或Program.cs中调用ConfigureKestrel设置MaxRequestBodySize = long.MaxValue,同时在控制器上加[RequestSizeLimit(long.MaxValue)] - 禁用模型绑定:方法签名不要含
IFormFile,改用HttpRequest.Body+MultipartReader解析,参考microsoft.AspNetCore.WebUtilities.MultipartReader - 每片写入临时目录时,文件名建议为
{fileId}_{chunkIndex},避免并发写入冲突;不要用MemoryStream缓存整片内容
合并前必须做 MD5 或 SHA256 分片校验
网络传输中单片出错很常见,但用户往往只看到“上传完成”,实际文件已损坏。仅靠文件大小一致无法判断内容正确性。
- 前端应在切片前计算整个文件的
fileHash,并随首片一起发送;每片上传时额外提交该片的chunkHash - 后端保存每片时同步计算其哈希值,与前端传来的
chunkHash比对,不一致则返回400 Bad Request并记录日志 - 合并操作仅在所有片校验通过、且
chunkIndex连续覆盖0..totalChunks-1后才执行,否则返回409 Conflict
断点续传依赖服务端状态持久化
如果只把分片存在本地磁盘,iis 重启或 Kestrel 进程崩溃后,已上传的分片就丢了,用户只能重头来。真正的断点续传需要可恢复的状态记录。
- 用轻量数据库(如 sqlite 或 redis)存元数据:
fileId、totalChunks、receivedChunks(位图或集合)、status(uploading / merged / failed) - 前端发起上传前先发
HEAD /api/upload?fileId=xxx查询已传片数,服务端返回206 Partial Content和已接收的chunkIndex列表 - 合并完成后立即删临时分片文件,并将
status改为merged;失败时保留状态供重试,但需加 TTL 防止磁盘被占满
最易被忽略的是分片命名和清理时机——用时间戳或随机数作临时文件名,会导致无法关联同一文件的分片;合并成功后没删分片,几天下来磁盘就满了。这些细节不写进逻辑,系统迟早出问题。