@RequestPart在混合多部分请求中的应用,Spring MVC 3.2

25

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

我使用@RequestPart注释来接收请求。

@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进行评估,并最终取消编组为我的User类(我正在使用Jaxb2,配置了适当的marshaller / unmarhaller在应用程序上下文中,并且当我将XML数据作为正文传递并使用@RequestBody注释时,该过程对于所有其他控制器方法都有效)。
但实际发生的是,尽管文件被正确地找到并解析为MultipartFile,但“user”部分从未被看到,并且请求始终失败,不匹配控制器方法签名。
我使用几种客户端类型重现了这个问题,并且我有信心multipart请求格式没问题。
请帮助我解决这个问题,也许有一些解决方法可接收混合/多部分请求。
谢谢和问候,
Raghvendra

你有没有尝试过使用 consumes = {"multipart/form-data"} - Stijn Geukens
@StijnGeukens 我尝试了那个,也没有起作用。 - Hola Soy Edu Feliz Navidad
5个回答

33

我已经成功解决了这个问题

端点示例:

@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;
}
}

1
@Maksim 为什么你覆盖了这些方法? - Raman Preet Singh
1
@RamanPreetSingh 我们不希望这个转换器被用于写入操作,我们只需要正确地读取请求。 - Max Ploter

9

不确定您是否已经解决了问题,但我也遇到了类似的问题,当混合使用@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--

1
我正在使用Spring RESTful Web服务获取多部分数据和对象。你有什么想法吗? - Praveen Shendge

1
你可以从org.springframework.web.bind.annotation.RequestPart导入@RequestPart使用,它用于结合@RequestBody和文件上传。
使用@RequestParam的方式如下 @RequestParam("file") MultipartFile file 你可以仅上传文件和多个单个数据(键值对)像这样。
    @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。如果文件字段允许多个文件上传,如第二个多部分请求所示,只需使用Part或MultipartFile的数组或集合即可。

        @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) {
    }

乐于助人...


-1

我已经成功解决了问题:

    @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);

1
也许可以在你的代码中添加一些解释,说明它为什么能够解决这个问题,或者一些文档。 - Reznik

-4

你试过了吗?

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文件?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接