目录

解决 SpringBoot 上传文件超过 5MB 返回 404 的问题

问题描述

最近遇到了一个比较棘手的问题:测试同学反馈说外地一位用户在使用特定设备发送用户反馈时,自动上传日志附件的功能总是返回 404 错误,而其他设备和其他用户都没有问题。

这个问题初看起来像是路由问题或者特定设备兼容性问题,但经过排查发现,实际上是由于上传的日志文件大小超过了后端 SpringBoot 配置的文件大小限制导致的。

问题排查过程

1. 初步排查

收到问题反馈后,我首先进行了以下排查:

  • 检查客户端代码:确认上传逻辑是否有问题,特别是针对特定设备的处理
  • 本地测试:在我的开发环境下,使用 curl 命令模拟上传请求,测试正常
  • 测试环境验证:测试同学也在他的环境下用 curl 测试,同样没有问题
1
2
3
4
5
6
curl -X POST "https://example.com/api/upload/uploadLog?os=MacBookPro16%2C1-15.5.0&uid=123456&isDev=1" \
-H "Host: example.com" \
-H "Cookie: session=xxx" \
-H "user-agent: AppName/1.0.0 (MacBookPro16,1; Mac OS X 15.5.0)" \
-F "logfiles=@/path/to/test.log;filename=test.log;type=application/octet-stream" \
--compressed

2. 联系后端和运维排查

由于本地测试都正常,我开始怀疑是服务端的问题,于是联系了:

  • 运维同学:检查 Nginx 等反向代理配置,确认没有大小限制
  • 服务端开发:检查接口路由和日志,发现请求根本没有到达后端 Controller

3. 远程排查找到根因

最终通过远程连接到问题用户的电脑,检查实际上传的文件时发现:

  • 问题文件大小:8MB
  • 后端配置限制:单文件最大 5MB,总请求限制 50MB

原来问题出在这里!用户上传的日志文件是 8MB,超过了后端配置的单文件 5MB 限制。当文件超过限制时,SpringBoot 会在请求到达 Controller 之前就拒绝请求,直接返回 404 错误,而不是返回更友好的"文件过大"错误提示。

4. 为什么本地测试没问题?

本地测试时使用的测试文件都比较小(通常小于 1MB),所以没有触发这个限制。而实际用户生成的日志文件可能因为使用时长、日志级别等原因,文件较大,超过了 5MB 的限制。

解决方案

临时解决方案

由于这是后端配置的限制,我采取了以下临时措施:

  1. 客户端文件大小检查:在上传前检查文件大小,如果超过 5MB,提示用户文件过大,建议压缩或分段上传
  2. 日志文件压缩:对于较大的日志文件,在上传前进行压缩处理,减少文件大小
  3. 日志文件分段:如果文件过大,可以考虑将日志文件分段上传

根本解决方案(需要后端配合)

这个问题需要后端同学调整 SpringBoot 的文件上传大小限制配置。以下是几种常见的配置方式,供后端同学参考:

方案一:在 application.yml 中配置(推荐)

application.ymlapplication.properties 中添加以下配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
spring:
  servlet:
    multipart:
      # 单个文件最大大小(根据实际需求调整,比如改为 10MB)
      max-file-size: 10MB
      # 整个请求的最大大小(包含所有文件和表单数据)
      max-request-size: 50MB
      # 文件写入磁盘的阈值,超过此大小将写入临时文件
      file-size-threshold: 2KB
      # 临时文件存储位置
      location: /tmp

如果使用 application.properties

1
2
3
4
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=50MB
spring.servlet.multipart.file-size-threshold=2KB
spring.servlet.multipart.location=/tmp
方案二:在代码中配置 MultipartConfigElement

如果需要在代码中动态配置,可以创建一个配置类:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@Configuration
public class MultipartConfig {
    
    @Bean
    public MultipartConfigElement multipartConfigElement() {
        MultipartConfigFactory factory = new MultipartConfigFactory();
        // 单个文件最大大小
        factory.setMaxFileSize(DataSize.ofMegabytes(10));
        // 整个请求的最大大小
        factory.setMaxRequestSize(DataSize.ofMegabytes(50));
        // 文件写入磁盘的阈值
        factory.setFileSizeThreshold(DataSize.ofKilobytes(2));
        return factory.createMultipartConfig();
    }
}

问题总结

这次排查让我学到了几个重要的经验:

  1. 404 不一定是路由问题:当文件超过 SpringBoot 配置的大小限制时,请求会在到达 Controller 之前就被拒绝,返回 404 错误,而不是更友好的错误提示。

  2. 本地测试的局限性:本地测试环境可能无法完全复现生产环境的问题,特别是文件大小、网络环境等因素。需要尽可能模拟真实场景,或者直接到问题现场排查。

  3. 跨团队协作的重要性:这类问题需要客户端、服务端、运维等多个团队协作排查,及时沟通可以更快定位问题。

  4. 客户端防御性编程:应该在上传前进行文件大小检查,给用户友好的提示,而不是让用户看到莫名其妙的 404 错误。

延伸思考:SpringBoot 文件上传配置

对于后端开发同学,如果需要调整文件上传大小限制,可以参考以下配置方式:

配置说明

SpringBoot 对文件上传有以下关键配置:

  • spring.servlet.multipart.max-file-size: 单个文件最大大小,默认值为 1MB
  • spring.servlet.multipart.max-request-size: 整个请求的最大大小,默认值为 10MB

当上传的文件超过这些限制时,SpringBoot 会在请求到达 Controller 之前就拒绝请求,并返回 404 错误(在某些版本中可能返回 413 错误)。

注意事项

  1. 单位大小写:配置中的单位(MB、KB)不区分大小写,但建议使用大写以保持一致性。

  2. max-request-size 必须大于等于 max-file-sizemax-request-size 应该设置为大于或等于 max-file-size,因为一个请求可能包含多个文件和其他表单数据。

  3. 临时文件清理:如果设置了 location,需要确保临时目录有足够的空间,并且定期清理临时文件。

  4. Nginx 等反向代理配置:如果使用了 Nginx 等反向代理,还需要在 Nginx 配置中增加 client_max_body_size 参数:

1
2
3
4
5
6
7
http {
    client_max_body_size 50M;
    # 或者针对特定 location
    location /api/ {
        client_max_body_size 50M;
    }
}
  1. Tomcat 配置:如果使用内嵌的 Tomcat,可能还需要配置 maxSwallowSize
1
2
3
server:
  tomcat:
    max-swallow-size: 50MB

验证配置

配置完成后,可以通过以下方式验证:

  1. 使用 Postman 或 curl 测试上传不同大小的文件
  2. 查看应用日志,确认请求是否正常到达 Controller
  3. 检查返回的状态码,应该是 200(成功)或 400(业务错误),而不是 404

参考链接