You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用RestTemplate接收multipart/form-data类型MultiValueMap遇异常求助

问题:GET接口返回multipart/form-data时RestTemplate无法解析响应

服务端Mock接口代码

@GetMapping(value = "/getMsgFileExt",
            produces = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<MultiValueMap<String, Object>> getMsgFileExt() throws IOException {
    MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
    Path path = Paths.get("C:\Users\User\IdeaProjects\ccgw_sources\TKmock\src\main\resources\test.txt.zip.total.zip");
    formData.add("xml", respDataMsgInfo);
    formData.add("zip", Base64.getEncoder().encodeToString(Files.readAllBytes(path)));
    return new ResponseEntity<>(formData, HttpStatus.OK);
}

客户端接收代码

String uri = String.format("%s/getMsgFileExt", customsUrl);

UriComponents builder = UriComponentsBuilder.fromHttpUrl(uri)
  .queryParam("id", id)
  .build();

 ResponseEntity<MultiValueMap<String, Object>> response = customsCardRestTemplate.exchange(builder.toUriString(), HttpMethod.GET,
                                                                                                  null,
                                                                                                  new ParameterizedTypeReference<>() {});
MultiValueMap<String, Object> map = response.getBody();

抛出异常

Could not extract response: no suitable HttpMessageConverter found for response type [org.springframework.util.MultiValueMap<java.lang.String, java.lang.Object>] and content type [multipart/form-data]

解决方案

1. 改用POST请求(推荐,符合HTTP规范)

HTTP规范中GET请求通常不用于返回multipart/form-data格式的响应,改用POST更合理,同时Spring对POST场景的multipart支持更完善。

修改后服务端代码

@PostMapping(value = "/getMsgFileExt",
            produces = MediaType.MULTIPART_FORM_DATA_VALUE,
            consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ResponseEntity<MultiValueMap<String, Object>> getMsgFileExt(@RequestParam String id) throws IOException {
    MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
    // 修正路径转义符
    Path path = Paths.get("C:\\Users\\User\\IdeaProjects\\ccgw_sources\\TKmock\\src\\main\\resources\\test.txt.zip.total.zip");
    formData.add("xml", respDataMsgInfo);
    // 直接返回字节资源,避免Base64编码解码损耗
    formData.add("zip", new ByteArrayResource(Files.readAllBytes(path)) {
        @Override
        public String getFilename() {
            return "attachment.zip";
        }
    });
    return new ResponseEntity<>(formData, HttpStatus.OK);
}

修改后客户端代码

String uri = String.format("%s/getMsgFileExt", customsUrl);

// 构造请求参数
MultiValueMap<String, String> requestParams = new LinkedMultiValueMap<>();
requestParams.add("id", id);

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(requestParams, headers);

// 调用接口并解析响应
ResponseEntity<MultiValueMap<String, Object>> response = customsCardRestTemplate.exchange(
        uri, HttpMethod.POST, requestEntity, new ParameterizedTypeReference<>() {});
MultiValueMap<String, Object> responseMap = response.getBody();

// 解析xml和zip
String xmlContent = (String) responseMap.getFirst("xml");
ByteArrayResource zipResource = (ByteArrayResource) responseMap.getFirst("zip");
byte[] zipBytes = zipResource.getByteArray();

2. 为RestTemplate配置支持multipart响应的转换器

如果必须保留GET请求,需要扩展RestTemplate的消息转换器,让它能处理multipart/form-data类型的响应:

@Bean
public RestTemplate customsCardRestTemplate() {
    RestTemplate restTemplate = new RestTemplate();
    List<HttpMessageConverter<?>> converters = restTemplate.getMessageConverters();

    // 让Jackson转换器支持multipart/form-data类型
    for (HttpMessageConverter<?> converter : converters) {
        if (converter instanceof MappingJackson2HttpMessageConverter) {
            MappingJackson2HttpMessageConverter jacksonConverter = (MappingJackson2HttpMessageConverter) converter;
            jacksonConverter.getSupportedMediaTypes().add(MediaType.MULTIPART_FORM_DATA);
        }
    }

    // 添加MultipartFormHttpMessageConverter(处理multipart格式)
    restTemplate.getMessageConverters().add(new MultipartFormHttpMessageConverter());

    return restTemplate;
}

注意:此方案下服务端返回的Base64编码zip字符串,客户端需要手动解码:

String zipBase64 = (String) responseMap.getFirst("zip");
byte[] zipBytes = Base64.getDecoder().decode(zipBase64);

3. 手动解析原始响应流

如果以上方案都不适用,可以直接获取响应的输入流,用第三方库手动解析multipart内容(需引入Apache Commons FileUpload依赖):

<!-- Maven依赖 -->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.5</version>
</dependency>

客户端代码:

ResponseEntity<Resource> response = customsCardRestTemplate.exchange(
        builder.toUriString(), HttpMethod.GET, null, Resource.class);

// 解析multipart响应
String contentType = response.getHeaders().getContentType().toString();
InputStream inputStream = response.getBody().getInputStream();
MultipartParser parser = new MultipartParser(inputStream, contentType);

Part part;
while ((part = parser.readNextPart()) != null) {
    if (part.isParam()) {
        ParamPart paramPart = (ParamPart) part;
        if ("xml".equals(paramPart.getName())) {
            String xmlContent = paramPart.getStringValue();
            // 处理xml内容
        }
    } else if (part.isFile()) {
        FilePart filePart = (FilePart) part;
        if ("zip".equals(filePart.getName())) {
            byte[] zipBytes = filePart.getInputStream().readAllBytes();
            // 处理zip文件
        }
    }
    part.close();
}

内容的提问来源于stack exchange,提问作者FPS Bard

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 16:25:03