说个可能让很多不接触工业软件的人意外的场景:我在和一家半导体级制造企业的设备数据团队对接时,工艺工程师需要把一批几 GB 的晶圆缺陷检测图片、设备运行日志和工艺参数文件传到 MES 边侧服务器上做后续分析。界面是 SpringBoot 搭建的后台管理系统,上传进度条转了半天,最后网关直接报 504,浏览器显示请求超时,文件却没有真正写进存储。那时候我就意识到,“大文件上传”在制造系统里从来不是一个“SpringBoot 接口怎么写”的小问题,而是一整套关于传输稳定性、服务端资源控制、断点续传、文件校验和任务状态管理的问题链。
为了说清楚这条链路,我会用 SpringBoot 为核心实现框架,从“为什么不能直接用普通文件上传”,到“分片上传到底怎么设计”,再到“后端如何安全合并文件”“部署时网关和容器参数怎么调整”,最后把我真实踩过的几个深坑总结出来。涉及芯片制造、半导体设备数据采集和一般企业系统建设的内容都适用。无论你是主要写业务 CRUD 的 Java 开发,还是正在搭建内部文件交换平台的架构师,都建议按顺序看完,尤其是后面那几个坑,它们往往不会出现在官方文档里。
1. 当“大文件上传”遇到制造系统:先想清楚问题,再写代码
很多人一听大文件上传就觉得“无非是把 Spring 的 multipart 配置调大一点”,这种思路在处理几十 MB 的附件时没问题,但到了半导体制造场景中的几个 GB、几十 GB 文件时,问题会迅速演化成三个层面。
1.1 半导体等制造场景里,为什么天然绕不开大文件
芯片制造流程复杂,包含光刻、刻蚀、薄膜沉积、检测等环节。每一道工序的设备都会产生大量过程数据:晶圆检测设备输出的高分辨率缺陷图像、SECS/GEM 通讯日志、设备配方文件、SPC 量测数据、Trace 日志。很多系统为了实现质量追溯,会把不同批次、不同设备、不同时间段的数据打包成一个大文件传给上层的分析平台或 MES。
以一套典型的晶圆缺陷检测系统为例,一片晶圆可能有几十甚至上百个 Die,每个 Die 的缺陷图如果保存为未压缩的 TIF 格式,单张可能就是几十 MB,一批产品检测完成后打包数据轻松超过 1GB。如果再做灰度图、3D 形貌图和原始像素数据归档,单个文件达到 10GB 级别完全正常。传统 OA 系统里那种“上传个 Excel、传个 Word”的思维在这里根本不适用。
还有一个容易被忽略的点:制造车间的网络环境通常是隔离的。工程师办公区到产线服务器之间可能经过多层防火墙、网闸、文件摆渡系统,带宽不一定高,链路还容易抖动。如果大文件传输不支持重试和续传,一旦传输到 90% 时网络闪断,整个流程就得从头再来。这种损耗带来的不仅是时间成本,还可能延误工艺分析窗口,甚至影响生产决策。
1.2 大文件上传的三个核心挑战:传输、存储、状态可见
在做技术方案前,我喜欢把问题拆成三个维度:
- 传输维度:文件太大时,单次 HTTP 请求的耗时很长,期间任何一个网络抖动、服务重启或浏览器标签页误关闭都会导致前功尽弃。传输本身要支持中断恢复,而不是失败后全部重来。
- 存储维度:服务端同时接收多个大文件时,内存、临时磁盘、合并时的 IO 都可能成为瓶颈。SpringBoot 默认的 multipart 解析会先把文件写到临时目录,如果临时目录不可控或磁盘空间不足,处理过程会非常脆弱。
- 状态维度:用户需要一个明确的“当前传到哪里了”的感知。传统上传没有进度反馈,用户只能看到浏览器转圈。大文件上传必须提供任务级别的进度信息,否则使用者无法判断是继续等待还是选择重传。
这三个维度决定了我们不能只写一个 MultipartFile 接口就收工,而是要把它当成一个有状态、可恢复、可监控的小型任务系统来做。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型:为什么我从不建议直接拿 MultipartFile 硬扛
先说明,SpringBoot 自带的 MultipartFile 并不是不能用,它非常适合处理小文件。但用它做大文件上传,有几个天然硬伤。
2.1 MultipartFile 单次请求模型的三个硬伤
第一个硬伤是请求体过大时,网关和服务端压力集中释放。SpringBoot 处理上传时,如果使用默认的 StandardServletMultipartResolver,会将整个请求体解析并暂存到临时文件。文件越大,解析时间越长,占用临时磁盘空间越大。一旦并发上来,临时目录瞬间被写满,整个应用都可能变慢。
第二个硬伤是网络中断后没有恢复能力。一次 HTTP 请求只要建立后中断,服务端已经接收到的数据无法复用。客户端只能重新选择文件、重新计算、重新上传。这对 GB 级文件来说不可接受。
第三个硬伤是用户体验不可控。浏览器不会给你一个准确的“服务端已接收多少字节”的进度,前端拿到的上传进度往往只是“请求已发送”,无法了解服务端正在写临时文件还是已完成落盘。实际项目里,用户看到进度条 100% 但接口迟迟没有返回,这种状态最让人焦虑。
2.2 从“传文件”到“传分片”的方案演进
常见的大文件传输方案可以归为三类:
| 方案 | 实现思路 | 优势 | 局限 |
|---|---|---|---|
| 对象存储直传 | 前端生成签名,直传 OSS/MinIO/S3 | 客户端直连存储,服务端压力小 | 内网部署对象存储需要额外运维,与业务系统的整合成本不低 |
| 开源分片组件 | 比如 WebUploader、Resumable.js 等 | 分片和续传逻辑封装较好 | 很多组件已停止维护,且上传参数和服务端校验规则需要自行实现 |
| 自研分片上传接口 | 前端切片、后端合并,SpringBoot 提供接口 | 完全贴合业务,便于和已有权限系统、任务系统集成 | 需要自己处理并发、合并、状态记录等细节 |
芯片制造企业的系统通常跑在隔离内网,不一定允许直接引入外部对象存储服务;即便内部部署了 MinIO,也需要一个业务侧的上传入口做权限校验、病毒扫描、格式识别和元数据保存。所以我在多数项目里倾向于“自研 SpringBoot 分片上传 + 内部文件服务/MinIO 落盘”的组合。前端把大文件切成若干分片,逐个上传;后端收到分片后先落到本地临时目录,等全部分片到达后触发合并任务,大文件落地后再异步同步到对象存储或归档系统。
这样设计的好处是:不需要在浏览器与对象存储之间暴露存储桶凭证;每个分片都很小,可以独立重试;服务端每收到一个分片都能立刻返回结果,前端可以实时更新进度。
注意:分片上传不是银弹。如果单个文件本身只有几十 MB,分片反而会增加请求次数和失败概率。想清楚场景边界很重要。
3. 核心链路一:前端分片、指纹计算与断点续传的信息设计
很多人一聊分片上传就开始写 MultipartFile 接口,但真正做到生产级,前端的信息设计比后端代码更关键。因为后端能正确合并文件,完全依赖于前端在每个分片请求中传递的元数据是否完整、准确。
3.1 文件的“身份标识”:MD5 与采样哈希怎么选
大文件上传首先要解决识别问题:这个文件是什么?所有分片属于谁?服务端要根据什么判断文件是全新文件还是已经上传过一部分的续传文件?
业界常见做法是给文件计算 MD5,作为全局唯一标识。前端先读完整文件,计算一次 MD5,再开始分片;服务端以 MD5 作为目录名和任务记录主键。但这里有个很现实的坑
