Spring 3.0 FileUpload with POST only?

I am trying to upload a file with one parameter using spring 3.

This is my controller method that should enable this service:

@RequestMapping(value="/{id}", method = RequestMethod.PUT, headers="content-type=multipart/form-data") public ResponseEntity<String> uploadImageWithJsonParamater(@PathVariable("id") Long id, @RequestParam String json, @RequestParam MultipartFile customerSignFile) { //... } 

The problem is that the server cannot send this method: MissingServletRequestParameterException: Required string parameter 'json' is missing

If I changed RequestMethod from PUT to POST, everything will be fine. So does anyone know the problem?

It seems that submitting form data via PUT is prohibited.

I debugged a bit, and the following method returns false in the case of PUT, but true in the case of POST:

 public boolean isMultipart(HttpServletRequest request) { return (request != null && ServletFileUpload.isMultipartContent(request)); } 

I would be grateful for any help!

Thanks in advance!

+6
source share
4 answers

You cannot submit form data via PUT in accordance with the HTML standard. You can send files only through PUT, and in this case they are sent more efficiently, then using POST (because you no longer have all the multi-page service data), but so that you can connect the third-party PUT server component to actually receive the file through PUT, you must make sure that you really send it the PUT command (for example, through javascript). Here is an example using jQuery:

 $('#file_upload').fileUpload({ namespace: 'file_upload', url: '/path', method: 'PUT' }); 
+2
source

You can accomplish this using spring HiddenHttpMethodFilter , but you will need to make sure you put spring MultipartFilter in front of HiddenHttpMethodFilter in the web.xml filter chain.

For example: in your web.xml

 <filter> <filter-name>MultipartFilter</filter-name> <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class> <init-param> <param-name>multipartResolverBeanName</param-name> <param-value>filterMultipartResolver</param-value> </init-param> </filter> <filter-mapping> <filter-name>MultipartFilter</filter-name> <servlet-name>/*</servlet-name> </filter-mapping> <filter> <filter-name>HiddenHttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter-mapping> <filter-name>HiddenHttpMethodFilter</filter-name> <servlet-name>/*</servlet-name> </filter-mapping> 

Then in your spring -config.xml add a link to CommonsMultipartResolver:

 <bean id="filterMultipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/> 

Note that if you do not add the entry spring -config.xml, then MultipartFilter will use MultipartResolver by default, which uses the implementation of the 3.0 servlet specification, and will throw an error, for example: NoSuchMethodError HttpServletRequest.getParts () re not using 3.0.

+5
source

I am facing the same problem. My solution was to implement ExtendedMultipartResolver that accept multiparts sent using the http PUT method. Just copy the CommonsMultipartResolver code, rename the class, and change the implementation of the isMultipart() function to suit your needs:

 private boolean isMultipartContent(HttpServletRequest request) { String httpMethod = request.getMethod().toLowerCase(); // test for allowed methods here... String contentType = request.getContentType(); return (contentType != null && contentType.toLowerCase().startsWith("multipart")); } public boolean isMultipart(HttpServletRequest request) { return (request != null && isMultipartContent(request)); } 

Here you can check POST, PUT or other HTTP methods. In my case, all methods will be accepted, since my controller annotations will be filtered to the allowed methods.

Remember to configure the bean in the spring web context:

 <bean id="multipartResolver" class="sample.package.ExtendedMultipartResolver"/> 

hope this helps.

amuses Chris

+4
source

I implemented a slightly different solution based on http://rugal.ga/development/2015/10/03/uploading-file-other-than-post/ and is close to the previous post:

 public class ExtendedMultipartResolver extends CommonsMultipartResolver { @Override public boolean isMultipart(HttpServletRequest request) { return (request != null && isMultipartContent(request)); } /** * Extends ServletFileUpload.isMultipartContent() behavior to allow PUT requests as multipart. * * @param request * The servlet request to be evaluated. Must be non-null. * * @return <code>true</code> if the request is multipart; <code>false</code> otherwise. * @see org.apache.commons.fileupload.servlet.ServletFileUpload#isMultipartContent */ public static final boolean isMultipartContent(HttpServletRequest request) { HttpMethod httpMethod = HttpMethod.valueOf(request.getMethod()); if (HttpMethod.POST != httpMethod && HttpMethod.PUT != httpMethod) { return false; } return FileUploadBase.isMultipartContent(new ServletRequestContext(request)); } 

}

Nothing new here, except that my code relies on org.apache.commons.fileupload.FileUploadBase.isMultipartContent (RequestContext) instead of duplicating its contents.

Now, if you ever needed to do unit tests of file upload services, you could use org.springframework.test.web.servlet.request.MockMvcRequestBuilders.fileUpload () as follows:

 @Test public void testUploadFilePost() throws Exception { MockMultipartFile multipartFile = new MockMultipartFile(...); String url = ...; mockMvc.perform(fileUpload(url).file(multipartFile)).andExpect(status().isOk()); } 

The above code can only issue POST requests due to the way org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder is implemented. To unit test download a PUT file, you might be interested in the following code:

 private static final RequestPostProcessor PUT_REQUEST_POST_PROCESSOR = new RequestPostProcessor() { @Override public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) { request.setMethod(HttpMethod.PUT.name()); return request; } }; private static MockHttpServletRequestBuilder putFileUpload(String url, MockMultipartFile multipartFile, Object... urlVariables) { return fileUpload(url, urlVariables).file(multipartFile).with(PUT_REQUEST_POST_PROCESSOR); } 

Now the test can be adapted as follows:

 @Test public void testUploadFilePut() throws Exception { MockMultipartFile multipartFile = new MockMultipartFile(...); String url = ...; mockMvc.perform(putFileUpload(url, multipartFile)).andExpect(status().isOk()); } 
+1
source

Source: https://habr.com/ru/post/900616/


All Articles