【问题标题】:@RequestPart with mixed multipart request, Spring MVC 3.2@RequestPart 与混合多部分请求,Spring MVC 3.2
【发布时间】:2013-04-20 06:21:32
【问题描述】:

我正在开发基于 Spring 3.2 的 RESTful 服务。我遇到了一个控制器处理混合多部分 HTTP 请求的问题,第二部分是 XML 或 JSON 格式的数据,第二部分是图像文件。

我正在使用@RequestPart annotation 来接收请求

@RequestMapping(value = "/User/Image", method = RequestMethod.POST,  consumes = {"multipart/mixed"},produces="applcation/json")

public
ResponseEntity<List<Map<String, String>>> createUser(
        @RequestPart("file") MultipartFile file, @RequestPart(required=false) User user) {

    System.out.println("file" + file);

    System.out.println("user " + user);

    System.out.println("received file with original filename: "
            + file.getOriginalFilename());

    // List<MultipartFile> files = uploadForm.getFiles();
    List<Map<String, String>> response = new ArrayList<Map<String, String>>();
    Map<String, String> responseMap = new HashMap<String, String>();

    List<String> fileNames = new ArrayList<String>();

    if (null != file) {
        // for (MultipartFile multipartFile : files) {

        String fileName = file.getOriginalFilename();
        fileNames.add(fileName);

        try {
            file.transferTo(new File("C:/" + file.getOriginalFilename()));
        } catch (IllegalStateException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    responseMap.put("displayText", file.getOriginalFilename());
    responseMap.put("fileSize", "" + file.getSize());
    response.add(responseMap);

    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.add("Accept", "application/json");
    return new ResponseEntity<List<Map<String, String>>>(response,
            httpHeaders, HttpStatus.OK);

}

User.java 会是这样的-

@XmlRootElement(name = "User")


public class User implements Serializable { 
    private static final long serialVersionUID = 1L;

    private int userId;
    private String name;
    private String email;

    private String company;
    private String gender;

    //getter setter of the data members
}

据我了解,使用 @RequestPart 注释我希望 XML 多部分部分根据其 Content-Type 进行评估,并最终取消编组到我的用户类中(我使用的是 Jaxb2,编组器/解组器是正确的在应用程序上下文中配置,并且当我将 XML 数据作为正文传递并使用 @RequestBody 注释时,该过程对于所有其他控制器方法都可以正常工作)。

但实际发生的是,虽然文件被正确找到并解析为 MultipartFile,但“用户”部分从未见过,请求总是失败,与控制器方法签名不匹配。

我用几种客户端类型重现了该问题,并且我确信多部分请求的格式是可以的。

请帮我解决这个问题,也许会有一些解决方法来接收混合/多部分请求。

感谢和问候,

拉格文德拉

【问题讨论】:

  • 你试过consumes = {"multipart/form-data"}吗?
  • @StijnGeukens 我试过了,也没用。

标签: spring rest multipartform-data


【解决方案1】:

你试过了吗

ResponseEntity<List<Map<String, String>>> createUser(
        @RequestPart("file") MultipartFile file, @RequestBody(required=false) User user) {

ResponseEntity<List<Map<String, String>>> createUser(
        @RequestPart("file") MultipartFile file, @RequestParam(required=false) User user) {

如果这不起作用,您可以告诉我们mapping.xml

【讨论】:

    【解决方案2】:

    不确定您是否已经解决了问题,但我也遇到了类似的问题,即当将 @RequestPart 和 MultipartFile 混合在一起时,我的 JSON 对象没有被我的控制器拾取。

    您调用的方法签名看起来正确:

    public ResponseEntity<List<Map<String, String>>> createUser(
            @RequestPart("file") MultipartFile file, @RequestPart(required=false) User user) {
    
    // ... CODE ... 
    }
    

    但请确保您的请求看起来像这样:

    POST /createUser
    Content-Type: multipart/mixed; boundary=B0EC8D07-EBF1-4EA7-966C-E492A9F2C36E
    
    --B0EC8D07-EBF1-4EA7-966C-E492A9F2C36E
    Content-Disposition: form-data; name="user";
    Content-Type: application/xml; charset=UTF-8
    
    <user><!-- your user xml --></user>
    --B0EC8D07-EBF1-4EA7-966C-E492A9F2C36E
    Content-Disposition: form-data; name="file"; filename="A551A700-46D4-470A-86E7-52AD2B445847.dat"
    Content-Type: application/octet-stream
    
    /// FILE DATA
    --B0EC8D07-EBF1-4EA7-966C-E492A9F2C36E--
    

    【讨论】:

    • 我正在使用 Spring RESTful Web 服务来获取多部分数据和对象。你对此有什么想法吗?
    【解决方案3】:

    我已经解决了这个问题

    端点示例:

    @PostMapping("/")
    public Document create(@RequestPart Document document,
                           @RequestPart(required = false) MultipartFile file) {
        log.debug("#create: document({}), file({})", delegation, file);
        //custom logic
        return document;
    }
    

    例外:

    "error_message": "Content type 'application/octet-stream' not supported"
    

    从下一个方法抛出异常:

    org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(HttpInputMessage,MethodParameter,Type)
    

    解决方案:

    我们必须创建自定义转换器@Component,它实现了 HttpMessageConverterHttpMessageConverter 并了解 MediaType.APPLICATION_OCTET_STREAM。对于简单的解决方法,扩展 AbstractJackson2HttpMessageConverter

    就足够了
    @Component
    public class MultipartJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {
    
    /**
     * Converter for support http request with header Content-Type: multipart/form-data
     */
    public MultipartJackson2HttpMessageConverter(ObjectMapper objectMapper) {
        super(objectMapper, MediaType.APPLICATION_OCTET_STREAM);
    }
    
    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return false;
    }
    
    @Override
    public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) {
        return false;
    }
    
    @Override
    protected boolean canWrite(MediaType mediaType) {
        return false;
    }
    }
    

    【讨论】:

    • @Maksim 为什么要覆盖这些方法?
    • @RamanPreetSingh 我们不希望这个转换器用于写入,我们只需要正确读取一个请求。
    【解决方案4】:

    您可以使用 @RequestPart 从 org.springframework.web.bind.annotation.RequestPart; 它用作结合@RequestBody 和文件上传。

    像这样使用@RequestParam @RequestParam("file") MultipartFile 文件 您可以仅上传文件和多个单个数据(键值) 喜欢

        @RequestMapping(value = "/uploadFile", method = RequestMethod.POST,  consumes = { MediaType.MULTIPART_FORM_DATA_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
        public void saveFile(
                             @RequestParam("userid") String userid,
                             @RequestParam("file") MultipartFile file) {
    
        }
    

    您可以使用 @RequestPart 之类的方式发布 JSON 对象数据和文件

        @RequestMapping(value = "/patientp", method = RequestMethod.POST,  consumes = { MediaType.MULTIPART_FORM_DATA_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
    public ResponseEntity<?> insertPatientInfo(
                                                @RequestPart PatientInfoDTO patientInfoDTO,
                                                @RequestPart("file") MultipartFile file) {
    }
    

    您不限于直接使用分段文件上传作为控制器方法参数。您的表单对象可以包含 Part 或 MultipartFile 字段,Spring 自动知道它必须从文件部分获取值并适当地转换这些值。

    上述方法可以响应之前演示的包含单个文件的多部分请求。这是因为 Spring 有一个内置的 HTTP 消息转换器,可以识别文件部分。除了 javax.servlet.http.Part 类型之外,还可以将文件上传转换为 org.springframework.web.multipart.MultipartFile。如果文件字段允许上传多个文件,如第二个多部分请求中所示,只需使用数组或部分集合或 MultipartFiles。

            @RequestMapping(value = "/patientp", method = RequestMethod.POST,  consumes = { MediaType.MULTIPART_FORM_DATA_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
        public ResponseEntity<?> insertPatientInfo(
                                                    @RequestPart PatientInfoDTO patientInfoDTO,
                                                    @RequestPart("files") List<MultipartFile> files) {
        }
    

    乐于助人...

    【讨论】:

      【解决方案5】:

      我已经设法解决了问题:

          @SuppressWarnings("rawtypes")
      @RequestMapping(value = "/DataTransfer", method = RequestMethod.POST, produces = {
              MediaType.APPLICATION_JSON_UTF8_VALUE }, consumes = {  MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_JSON_UTF8_VALUE} )
      @ApiOperation(value = "Sbm Data Transfer Service", response = Iterable.class)
      @ApiResponses(value = { @ApiResponse(code = 200, message = "Successfully find."),
              @ApiResponse(code = 400, message = "There has been an error."),
              @ApiResponse(code = 401, message = "You are not authorized to save the resource"),
              @ApiResponse(code = 403, message = "Accessing the resource you were trying to reach is forbidden"),
              @ApiResponse(code = 404, message = "The resource you were trying to reach is not found") })
      ResponseEntity processDataTransfer(@RequestPart(name="file") MultipartFile  file, @RequestPart(name="param") DataTransferInputDto param);
      

      【讨论】:

      • 也许添加一些关于您的代码的解释,为什么它可以解决问题,也许是一些文档
      猜你喜欢
      • 2015-08-24
      • 2014-09-30
      • 2012-03-09
      • 2016-11-07
      • 2014-02-19
      • 2012-08-12
      • 2014-02-15
      • 1970-01-01
      相关资源
      最近更新 更多