上传个大 Excel 报 413,本地好好的一上测试环境就挂

        上周产品跑来说客户投诉了,业务系统里传一份 80 多兆的 Excel,页面直接报错,弹个 413 Request Entity Too Large 出来。

我一开始不慌,本地跑了下,好好的,能传上去。心想估计又是测试环境哪个配置没跟上。结果一排查排了两三个小时,中间还怀疑过是不是 Spring Boot 有什么隐藏限制,是不是 Tomcat 在拦,最后发现 Nginx 和后端都得改,只改一边不够。

写下来记录一下,下次自己再遇到也不用重新想一遍。

报错长啥样

前端 F12 一看,赤裸裸的一行:

413 Request Entity Too Large

然后我去后端应用日志里翻,啥也没有。这一点特别关键 —— 后端连日志都没打,说明请求压根没到应用,被前面的 Nginx 拦下来了。

我们的架构

浏览器 ──> Nginx(反代 + SSL)──> Spring Boot(内嵌 Tomcat)

一个上传请求得穿过 Nginx 和 Spring Boot 两道关。任何一道设了限制,超了就跪。这也是为什么下面要改好几个地方。

第一步:Nginx 放开

Nginx 默认 client_max_body_size 就 1M,你稍微大点的文件都过不去。413 基本就是它拦的。

vim /etc/nginx/nginx.conf

http 块里加一句:

http {
    client_max_body_size 200m;
    # 其他配置...
}

如果只想给某个域名放开,写 server 块或者 location 块里也行:

server {
    listen 443 ssl;
    server_name upload.xxx.com;
    
    client_max_body_size 200m;
    
    location /api/upload {
        client_max_body_size 500m;
        proxy_pass http://127.0.0.1:8080;
    }
}

优先级: location > server > http,就近原则,越具体越管用。

改完记得先 -t 检查语法再 reload,别直接干:

nginx -t
nginx -s reload

第二步:超时时间也要改(很多人栽在这)

我改完大小限制以为搞定了,结果传 80 多 M 的文件又报错,这次变成 504 Gateway Timeout。

一看 Nginx 日志,超时了。Nginx 默认代理超时 60 秒,公司内网还行,客户那边网速一般,60 秒根本传不完。

location 里再加上超时:

location /api/upload {
    client_max_body_size 500m;
    proxy_pass http://127.0.0.1:8080;
    
    proxy_connect_timeout 300s;
    proxy_send_timeout 300s;
    proxy_read_timeout 300s;
    
    client_body_timeout 300s;
    send_timeout 300s;
}

这几个参数我一开始也搞不清区别,简单说下:

  • proxy_connect_timeout:Nginx 连后端的超时

  • proxy_send_timeout:Nginx 往后端发数据的超时

  • proxy_read_timeout:Nginx 读后端响应的超时

  • client_body_timeout:Nginx 读客户端请求体的超时

  • send_timeout:Nginx 往客户端发响应的超时

大文件上传主要卡在 client_body_timeoutproxy_send_timeout 这俩,前三个改了没用别忘了这两个。 我第一次就只改了前三个,白改半天。

第三步:Spring Boot 的限制

Nginx 放开后,如果还报错,那就是应用自己拦的。Spring Boot 默认单文件才 1M,整个请求 10M。

application.yml 加:

spring:
  servlet:
    multipart:
      enabled: true
      max-file-size: 200MB
      max-request-size: 500MB
      file-size-threshold: 2KB

参数意思:

  • max-file-size:单个文件最大多大

  • max-request-size:整个请求最大多大(一次传多文件的时候用这个限总量)

  • file-size-threshold:超过多大就写磁盘临时文件,防止全放内存把服务撑爆

这里有个特别坑的地方: 老版本 Spring Boot 1.x 的配置路径是 spring.http.multipart.maxFileSize(还是驼峰),2.x 之后变成 spring.servlet.multipart.max-file-size(横杠)。网上一堆过时博客混着写的,你抄错了程序照跑不误,就是不生效,还查不出问题,气死人。

如果不确定自己是啥版本,两个都配一遍也没啥毛病。

第四步:Tomcat 层限制

绝大多数情况上面三步就够了。但有时候你会发现前三步都改了还是报错,那就得看 Tomcat 了。

内嵌 Tomcat 一般不用管,但保险起见 yml 里也可以加:

server:
  tomcat:
    max-http-form-post-size: 500MB
    max-swallow-size: 500MB
  • max-http-form-post-size:POST 请求体最大

  • max-swallow-size:Tomcat 拒了请求之后要"吞掉"多少数据,避免连接卡住不释放。设成 -1 就是不限

排查思路记一下

碰到 413 或者大文件上传失败,我一般按这个顺序看,基本能覆盖 99% 的情况:

1. 看错误码

  • 413 → Nginx 或者应用层大小限制

  • 504 → 超时问题

  • 500 加堆栈 → 到应用了,看 Spring 的错

2. 看后端日志有没有这个请求进来

  • 没有 → 前面的 Nginx 拦的

  • 有但报错 → Spring 或 Tomcat 层的问题

3. 逐层放

  • 先 Nginx(大小 + 超时)

  • 再 Spring Boot(multipart)

  • 最后 Tomcat(一般用不到)

附一个完整可用的 Nginx 配置

server {
    listen 443 ssl;
    server_name upload.xxx.com;
    
    ssl_certificate     /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
    
    client_max_body_size 500m;
    client_body_timeout 300s;
    
    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        proxy_connect_timeout 300s;
        proxy_send_timeout 300s;
        proxy_read_timeout 300s;
        send_timeout 300s;
    }
}

说两句

这问题看着小,排查起来还挺绕的。核心就是要搞明白请求经过哪几层,每一层都有自己的限制,任何一层没放开都不行。

另外一个建议:生产环境 client_max_body_size 别开太大,一开几个 G,万一有人恶意刷你磁盘一晚上就满了。我们的做法是全局设个小一点的(比如 20M),单独在需要上传的 location 里放大,比较稳。

有类似问题可以评论区聊。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值