【问题标题】:Spring Boot: post and process multipart/form-data request with multiple body partsSpring Boot:使用多个正文部分发布和处理多部分/表单数据请求
【发布时间】:2020-03-14 00:42:24
【问题描述】:

我有以下处理多部分/表单数据请求的端点:

    @RequestMapping(value = "/test", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseEntity<Response> handleRequest(
            @RequestPart(value = "image") MultipartFile image,
            @RequestPart(value = "data") Request request, HttpServletRequest request) throws IOException {
    }

当我使用 IDEA 休息客户端对其进行测试时,端点正在按预期到达:

POST http://myUrl/test
Accept: */*
Cache-Control: no-cache
Content-Type: multipart/form-data; boundary=xxxx
Authorization: Basic username pass

--xxxx
Content-Disposition: form-data; name="image"; filename="image.jpg"
Content-Type: image/jpg

< C:\pathToImage\image.jpg
--xxxx
Content-Disposition: form-data; name="data"
Content-Type: application/json

{
"firstName":"John",
"lastName":"Doe"
}
--xxxx

但是当我尝试通过 Spring RestTemplate 到达此端点时,我观察到 400 响应和以下错误: Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'image' is not present].

我用于发布请求的代码如下:

public Response test(final byte[] image, MediaType imageMimeType, final Request request) {
        String url = "http://myUrl/test";
        HttpHeaders headers = HeaderUtils.headerBasicAuth("username", "pass");
        headers.set(HttpHeaders.ACCEPT,MediaType.APPLICATION_JSON_VALUE);
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
        map.add("image", new InputStreamResource(new ByteArrayInputStream(image)));
        map.add("data", request.toJson());
        HttpEntity<?> requestEntity = new HttpEntity<>(map, headers);
        ResponseEntity<Response> result = template.exchange(url, HttpMethod.POST, requestEntity, Response.class);
        return result.getBody();
    }

Request类的大致代码如下:

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.joda.JodaModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.io.IOException;
import java.io.Serializable;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.json.JSONObject;

@AllArgsConstructor(access = AccessLevel.PUBLIC)
@Getter
@JsonInclude(Include.NON_NULL)
public final class VerifyRequest implements Cloneable, Serializable {
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    private final String firstName;
    private final String lastName;

    public JSONObject toJson() {
        String json;
        try {
            json = OBJECT_MAPPER.writeValueAsString(this);
        } catch (JsonProcessingException var3) {
            throw new IllegalStateException(var3);
        }

        return new JSONObject(json);
    }
    ....
}

有什么建议可以发布这样的请求吗?

【问题讨论】:

  • request.toJson() 是做什么的?您可以发布您的请求吗?
  • 问题不在于Request对象,而在于识别图像内容。不过,Request 类的大概代码在上面。

标签: spring spring-boot post multipartform-data resttemplate


【解决方案1】:

您收到该错误是因为您没有在 MultiValueMap 中为图像字段使用有效对象。通常你会使用 FileSystemResource 从磁盘加载图像,这样的例子很多。

但是,如果您仍想从字节数组中获取它,您可以使用 ByteArrayResource 覆盖控制器端所需的 getFilename()。所以而不是:

map.add("image", new InputStreamResource(new ByteArrayInputStream(image)));

简单地说:

ByteArrayResource byteArrayResource = new ByteArrayResource(image) {
    @Override
    public String getFilename() {
        return "image.jpg";
    }
};
map.add("image", byteArrayResource);

【讨论】:

  • Resource 的类型无关紧要,只要是InputStreamSource。所以任何一个都可以。
  • 我刚刚尝试过自己,但无法像问题中那样使用 InputStreamResource。我不断收到MissingServletRequestPartException: Required request part 'image' is not present。但是,如果我按照我的建议覆盖 getFIlename ,它会起作用。请使用 InputStreamResource 显示任何替代方法
  • 覆盖 getFileName() 方法会有所帮助。谢谢@pepevalbe。我还尝试从其余客户端的请求中删除文件名,也得到了 400。
  • 文件名确实是必需的,否则它不是文件的有效多部分参数。但是底层的 httpmessageconverter 与每个InputStreamSource 都可以很好地编写内容,但是确实需要文件名。
猜你喜欢
  • 2016-11-11
  • 2013-04-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-08
  • 1970-01-01
  • 2020-01-18
  • 1970-01-01
相关资源
最近更新 更多