使用RestTemplate POST大字节数组到API 超4MB报500错误如何修复
问题修复方案
你遇到的超4MB文件上传报500错误,核心触发点有三个:
- Spring Boot内嵌Tomcat默认限制了multipart上传的文件大小,未做自定义配置时单文件默认上限为1MB,你当前环境可传4MB属于手动调整过阈值但未覆盖大文件场景;未配置异常处理时,文件超限抛出的
MaxUploadSizeExceededException会直接返回500状态码,而非标准的413错误。 - 服务端Controller直接用
byte[]接收文件参数,会将整个文件全量加载到JVM堆内存,大文件场景下容易触发内存溢出或参数解析失败。 - 客户端RestTemplate构造multipart请求时未指定正确的请求头,直接传字节数组的方式无法被服务端识别为文件参数,且未调整大文件场景下的超时配置。
服务端(Controller侧)修复
1. 放开上传大小限制
在服务端的application.yml中添加以下配置,具体阈值按业务需求调整:
spring: servlet: multipart: enabled: true max-file-size: 100MB # 单文件最大支持100MB max-request-size: 110MB # 整个请求最大大小,需预留其他表单参数的空间 file-size-threshold: 2MB # 超过2MB的文件直接写入临时磁盘,不驻留内存 server: tomcat: max-http-form-post-size: 110MB # 放开Tomcat本身的POST表单大小限制,和上面的请求大小保持一致
如果是properties格式配置,对应参数为:
spring.servlet.multipart.enabled=true spring.servlet.multipart.max-file-size=100MB spring.servlet.multipart.max-request-size=110MB spring.servlet.multipart.file-size-threshold=2MB server.tomcat.max-http-form-post-size=110MB
2. 调整接口接收方式
不要直接用byte[]接收文件,改用MultipartFile对象,直接获取流做S3上传,避免全量文件加载到内存:
@PostMapping("/upload") public void uploadToS3(@RequestParam String bucket, @RequestParam String key, @RequestParam MultipartFile file) throws IOException { // 直接传入输入流到S3上传逻辑,不需要将整个文件转为字节数组 dataJockeyService.uploadFile(bucket, key, file.getInputStream()); }
对应Service层的uploadFile方法调整为接收InputStream类型参数,对接S3 SDK的流式上传接口即可。
客户端(RestTemplate侧)修复
调整RestTemplate的请求构造逻辑,正确设置multipart请求头,用资源对象传文件,同时调大超时时间适配大文件传输:
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); requestFactory.setBufferRequestBody(false); // 关闭请求体缓冲,大文件直接流式传输 requestFactory.setConnectTimeout(10000); // 连接超时10秒 requestFactory.setReadTimeout(120000); // 读超时调整为2分钟,适配大文件上传耗时 RestTemplate restTemplate = new RestTemplate(); restTemplate.setRequestFactory(requestFactory); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); // 必须指定multipart表单类型 MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>(); paramMap.add("bucket", datajockeys3bucket.replace(sing_env,env)); String fileName = sampleFile.substring(sampleFile.lastIndexOf("/") + 1); paramMap.add("key", "clone/".concat(fileName)); // 构造字节数组资源,不要直接传入原生byte数组 paramMap.add("file", new ByteArrayResource(sampleFileArray) { @Override public String getFilename() { // 必须返回文件名,否则服务端会将该参数识别为普通表单参数,而非文件 return fileName; } }); HttpEntity<MultiValueMap> request = new HttpEntity<>(paramMap, headers); restTemplate.exchange( dataJockeyUrl.replace(sing_env,env).concat("/upload"), HttpMethod.POST, request, Void.class );
如果需要传输100MB以上的文件,建议将SimpleClientHttpRequestFactory替换为Apache HttpComponents实现的HttpComponentsClientHttpRequestFactory,对大文件流式传输的稳定性更好。
GB级大文件优化方案
如果需要传输几百MB到GB级的文件,上述方案会因为文件经过应用服务器中转导致带宽占用高、易超时,建议改用两种方案:
- 分片上传:客户端将文件切为5MB~10MB的固定分片,逐个调用接口上传分片,所有分片上传完成后服务端调用S3的分片合并接口完成上传,支持断点续传,失败仅需重传出错分片。
- 客户端直传S3:服务端仅负责向客户端签发S3的STS临时上传凭证,客户端拿到凭证后直接将文件上传到S3,完全不经过应用服务器,性能最优,也没有应用侧的文件大小限制。
内容的提问来源于stack exchange,提问作者Arjun C E
相关产品推荐
相关产品推荐

