Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@RequestPart with mixed multipart request, Spring MVC 3.2

Tags:

I'm developing a RESTful service based on Spring 3.2. I'm facing a problem with a controller handling mixed multipart HTTP request, with a Second part with XMLor JSON formatted data and a second part with a Image file .

I am using @RequestPart annotation for receiving the request

@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 will be like this-

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

To my understanding, using the @RequestPart annotation I would expect the XML multipart section to be evaluated depending on its Content-Type and finally un-marshalled into my User class (I'm using Jaxb2, the marshaller/unmarhaller is properly configured in the application context and the procedure is working fine for all the other controller methods when I pass the XML data as body and use the @RequestBody annotation).

But what is actually happening is that, although the file is correctly found and parsed as MultipartFile, the "user" part is never seen and the request is always failing, not matching the controller method signature.

I reproduced the problem with several clients type and I am confident the format of the multipart request is ok.

Please help me to solve this issue, Maybe some workaround will be there to receive mixed/multipart request.

Thanks and Regards,

Raghvendra

like image 275
Raghvendra Avatar asked Apr 26 '13 06:04

Raghvendra


2 Answers

I have managed to solve the problem

Endpoint example:

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

Exception:

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

Exception is thrown from the next method:

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

Solution:

We have to create custom converter @Component, which implements HttpMessageConverter or HttpMessageConverter and knows about MediaType.APPLICATION_OCTET_STREAM. For simple workaround it's enough to extend 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; } } 
like image 175
Max Ploter Avatar answered Sep 27 '22 22:09

Max Ploter


Not sure if you had fixed your problem, but I also had a similar problem where my JSON object was not getting picked up by my controller when mixing @RequestPart and MultipartFile together.

The method signature for your call looks correct:

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

However make sure your request looks something like this:

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-- 
like image 40
Tobin Schwaiger-Hastanan Avatar answered Sep 27 '22 20:09

Tobin Schwaiger-Hastanan