事情是从产品团队反馈开始的:“用户上传 80MB 的视频,页面提示‘上传成功’,但后台查不到文件。”
我们立刻检查前端代码:
const res = await fetch('/api/upload', {
method: 'POST',
body: formData
});
if (res.ok) {
showSuccess("上传成功!");
} 逻辑没问题。 再看浏览器 Network 面板:
请求状态码:200 OK; Response Body:{"code":0,"msg":"success"}。 看起来真成功了。
但查后端日志——根本没有 /api/upload 的访问记录!
更诡异的是:
上传 10MB 文件,一切正常; 上传 50MB 以上,就“假成功”。 而且不是所有环境都这样:
本地开发(直接连 Spring Boot):80MB 能传; 测试环境(走 Nginx Ingress):80MB 假成功。 怀疑是不是 Nginx 拦截了? 查 Nginx Ingress Controller 日志,发现一行警告:
2024/12/10 14:23:01 [warn] 42#42: *1234 a client request body is buffered to a temporary file ... 接着是:
2024/12/10 14:23:05 [error] 42#42: *1234 client intended to send too large body: 83886080 bytes client intended to send too large body!
原来,Nginx 默认限制客户端请求体大小为 1MB(client_max_body_size 默认值)。
当请求超过这个值,Nginx 不会转发给后端,而是自己返回一个错误页面。
但为什么前端收到的是 200?
因为我们的 Nginx Ingress 配了自定义 error page:
nginx.ingress.kubernetes.io/custom-http-errors: "413"
nginx.ingress.kubernetes.io/default-backend: my-error-page-service 而那个 my-error-page-service 是个静态服务,对所有错误都返回 200 + JSON 格式的友好提示,比如:
{"code":0,"msg":"success"} ……只是为了“不让用户看到丑陋的 413”。
结果,前端把错误当成了成功。
为什么本地能传? 因为本地开发时,我们直接访问 http://localhost:8080, 绕过了 Nginx,只受 Spring Boot 限制。
而 Spring Boot 默认的 spring.servlet.multipart.max-file-size 是 1MB, 但我们在 application.yml 里显式改成了:
spring:
servlet:
multipart:
max-file-size: 100MB
max-request-size: 100MB 所以本地能传 80MB。
但 Nginx 层没配,依然卡在 1MB。
验证 用 curl 直接打 Nginx:
curl -F "file=@large-video.mp4" https://test-env/api/upload 返回:
{"code":0,"msg":"success"} 但抓包发现,这是 Nginx 返回的,不是后端。
再看响应头:
Server: nginx 确认了。
解决办法 在 Ingress 上加注解,调大 body 限制:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: upload-ingress
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
spec:
rules:
- host: test.myapp.com
http:
paths:
- path: /api/upload
pathType: Prefix
backend:
service:
name: app-service
port:
number: 8080 注意:要用 proxy-body-size,它会设置 client_max_body_size。
同时,确保后端 Spring Boot 的 multipart 限制 ≥ Nginx 的值。
上线后,80MB 文件上传真实成功。
其实这个问题最讽刺的地方在于: 我们为了用户体验,把错误包装成成功,结果反而让用户以为功能正常,耽误了问题上报。
后来 我们做了两件事彻底避免这类问题:
统一错误响应格式,绝不让 4xx/5xx 返回 200 修改 my-error-page-service,对 413 返回:
{"code":413,"msg":"File too large"} 并让前端根据 code 判断,而不是只看 HTTP 状态码。
在 CI/CD 中加入配置一致性检查 写脚本自动比对:
Nginx proxy-body-size Spring Boot multipart.max-file-size 前端上传组件的 maxSize 三者必须一致,否则构建失败。