Spring Boot中RestTemplate消费multipart/form-data时的文件参数问题
在Spring中正确传递multipart/form-data的文件ContentType、文件名及内容
我来帮你搞定这个RestTemplate调用multipart接口的问题!你之前的两种写法都踩了坑,导致返回400 Bad Request,咱们一步步来修正。
先说说你之前写法的问题
- 代码示例1:直接把字节数组丢进
MultiValueMap,Spring没法识别这是一个文件part,自然没法帮你设置正确的Content-Type和文件名,接口接收方解析失败就返回400了。 - 代码示例2:手动拼接
Content-Disposition和Content-Type到key里,还手动指定了boundary,这完全打乱了Spring自动处理multipart请求的逻辑——Spring本来会自动生成合法的boundary、正确组装每个part的请求头,你手动干预后请求格式就乱了,接口自然不认。
正确的实现方式
Spring的RestTemplate处理multipart请求时,每个文件part需要单独封装成HttpEntity,通过它来设置该文件的Content-Type、文件名等信息,再用ByteArrayResource(或FileSystemResource)包装文件内容。
针对字节数组的场景(你的案例)
import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; import org.springframework.core.io.ByteArrayResource; import org.springframework.http.ContentDisposition; // 1. 构建全局请求头 HttpHeaders globalHeaders = new HttpHeaders(); globalHeaders.add("Token", "_45378ffb-e366-45ec-9ac4-eb968c57aee3"); globalHeaders.setContentType(MediaType.MULTIPART_FORM_DATA); // 2. 构建文件part的专属请求头 String filename = "你的文件名.txt"; // 替换成实际文件名 HttpHeaders filePartHeaders = new HttpHeaders(); filePartHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM); // 设置文件的Content-Type // 设置Content-Disposition:指定表单字段名(name要和接口要求的一致)、文件名 ContentDisposition fileDisposition = ContentDisposition.builder("form-data") .name("file") // 注意!这里是接口要求的表单字段名,比如接口期望文件在"file"这个key下 .filename(filename) .build(); filePartHeaders.setContentDisposition(fileDisposition); // 3. 用ByteArrayResource包装文件字节内容,重写getFilename确保Spring识别文件名 ByteArrayResource fileResource = new ByteArrayResource(msg.getBytes()) { @Override public String getFilename() { return filename; } }; // 把文件内容和专属头包装成HttpEntity HttpEntity<ByteArrayResource> fileEntity = new HttpEntity<>(fileResource, filePartHeaders); // 4. 构建其他表单参数(比如OrderRequest) HttpEntity<String> orderRequestEntity = new HttpEntity<>(message); // 5. 组装所有请求part到MultiValueMap MultiValueMap<String, Object> requestBody = new LinkedMultiValueMap<>(); requestBody.add("file", fileEntity); // 这里的key要和上面fileDisposition里的name一致 requestBody.add("OrderRequest", orderRequestEntity); // 6. 发送请求 RestTemplate restTemplate = new RestTemplate(); HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(requestBody, globalHeaders); ResponseEntity<String> response = restTemplate.postForEntity(url, requestEntity, String.class);
针对本地文件的场景(如果是读取本地文件上传)
如果你的文件是本地磁盘文件,可以用FileSystemResource替代ByteArrayResource,更简便:
import org.springframework.core.io.FileSystemResource; import java.io.File; File localFile = new File("/path/to/your/local/file"); FileSystemResource fileResource = new FileSystemResource(localFile); // 后续的请求头构建、组装逻辑和上面一致 HttpHeaders filePartHeaders = new HttpHeaders(); filePartHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM); ContentDisposition fileDisposition = ContentDisposition.builder("form-data") .name("file") .filename(localFile.getName()) .build(); filePartHeaders.setContentDisposition(fileDisposition); HttpEntity<FileSystemResource> fileEntity = new HttpEntity<>(fileResource, filePartHeaders);
关键注意点
- 表单字段名要匹配:
ContentDisposition.builder里的name参数,必须和接口文档要求的文件字段名一致(比如接口可能要求用uploadFile而不是file),这是很多人踩坑的点。 - 不要手动设置boundary:全局请求头里只需要设置
MediaType.MULTIPART_FORM_DATA,Spring会自动生成合法的boundary,手动设置反而会导致请求格式错误。 - 文件资源必须返回文件名:不管用
ByteArrayResource还是FileSystemResource,都要确保getFilename()能返回正确的文件名,这样Spring才能在请求头里正确带上文件名信息。
这样处理后,你的请求就会符合multipart/form-data的规范,接口就能正确解析文件的ContentType、文件名和内容了,不会再返回400错误啦。
内容的提问来源于stack exchange,提问作者Chaithra Shetty
相关产品推荐
相关产品推荐

