5

Does anyone have any idea of any products or libraries like Apache Commons FileUpload that will deal with PUT file uploads?

Any friendly advice or direction would be very much appreciated!

Full Story:

We are starting to implement a file upload rest(like) service for our java webapp, but there doesn't seem to be any 'easy' solutions for dealing with file uploads via the HTTP PUT method.

We are hoping to find a library like the Apache Commons FileUpload project, but something that doesn't only deal with "Form-based File Upload in HTML" and/or "multipart/form-data".

We really like FileUpload's ability to store files temporarily, move those files when asked, and then clean up the temporary files after they are no longer used. We also like the fact that Spring will automajically bind the MultipartFile List to our command object and its just available for us to use when it gets into our other html form based file upload controllers.

Full Stack Background:

  • Spring MVC (3.2.3.RELEASE)
  • Tomcat 7
  • We are trying to follow a layered architecture (UI, services/business logic, persistence)

Thank you for your time!


The following url is an example that shows the ability to upload a file from the request's InputStream. The code gets the work done but it isn't quite production quality.

https://boplicity.nl/confluence/display/spring/Using+HTTP+PUT+and+Spring+MVC+to+upload+files


We are using the following curl command to test our webservice:

curl -v -k -X PUT --data-binary @"c:/java/files/tempfilename.txt" https://localhost:8443/api/file/tempfilename.txt 

xwoker then gave the following nice curl example:

curl -v -X PUT -T "myfile" http://localhost:8080/mytargetfilename 
3
  • Found the original code: boplicity.nl/confluence/display/spring/… Commented Sep 10, 2013 at 20:55
  • Shouldn't you use the path variable fileName somewhere? Commented Sep 16, 2013 at 20:31
  • @xwoker, yes, we should be using the fileName somewhere, but it isn't quite the point of my post. I will try and update my question with a better code example explaining the simplicity we're attempting to achieve. Commented Sep 16, 2013 at 23:40

2 Answers 2

10

It is fairly painless to get spring to respond correctly to a File Upload request for an HTTP PUT Method.

All it takes is overriding the isMultipart() method in a customized MultipartResolver class.

import org.apache.commons.fileupload.FileUploadBase; import org.apache.commons.fileupload.servlet.ServletRequestContext; import javax.servlet.http.HttpServletRequest; public class PostAndPutCommonsMultipartResolver extends CommonsMultipartResolver { private static final String POST_METHOD = "POST"; private static final String PUT_METHOD = "PUT"; @Override public boolean isMultipart(HttpServletRequest request) { boolean isMultipartRequest = false; if (request != null) { if (POST_METHOD.equalsIgnoreCase(request.getMethod()) || PUT_METHOD.equalsIgnoreCase(request.getMethod())) { isMultipartRequest = FileUploadBase.isMultipartContent(new ServletRequestContext(request)); } } return isMultipartRequest; } } 

What is really important, is that the default MultipartResolver is extended so that the isMultipart() method will return a true for either a POST or PUT request.

In general there are two default MultipartResolver implementations: CommonsMultipartResolver (used with Apache Commons FileUpload) and StandardServletMultipartResolver (used with Servlet 3.0+ Part API).

Since we are using Apache Commons FileUpload we extended the CommonsMultipartResolver class.

There is documentation on the MultipartResolver's Javadoc page that explains how to properly define a customized MultipartResolver for your application (emphasis added):

There is no default resolver implementation used for Spring DispatcherServlets, as an application might choose to parse its multipart requests itself. To define an implementation, create a bean with the id "multipartResolver" in a DispatcherServlet's application context. Such a resolver gets applied to all requests handled by that DispatcherServlet.

For an xml configured application it will look close to the following:

<bean id="multipartResolver" class="<package>.<name>.PostAndPutCommonsMultipartResolver"/> 

For an annotation configured application it will look close to the following:

@Bean(name = "multipartResolver") public CommonsMultipartResolver createMultipartResolver() { return new PostAndPutCommonsMultipartResolver(); } 

More information on Annotation configuration of MultipartResolver.

Sign up to request clarification or add additional context in comments.

2 Comments

I´m facing the same problem, I´ve tried your code, but I got Bad Request. I can see that when I do the PUT with postman, the isMultipart method is exectued and returns true. But any way, the request never arrives to the controller, returning http 400. Any ideas?
Sorry you're having difficulties with this solution. I would recommend creating a new stack overflow question and linking it in a new comment. Make sure to include all relevant code and stack traces.
1

I'm not aware of any libraries that fulfill your requirements. But if you don't mind to do some coding, I think nice way to create something similar would be to write your own

public class FileMessageConverter extends AbstractHttpMessageConverter<File> 

that converts the request body to File in the tmp directory:

@Override protected File readInternal(Class<? extends File> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { InputStream inputStream = inputMessage.getBody(); File tmpFile = File.createTempFile("upload","tmp"); if (inputStream != null) { FileOutputStream outputStream = new FileOutputStream(tmpFile); byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, bytesRead); } outputStream.flush(); outputStream.close(); } return tmpFile; } 

In the controller you would define your method with:

@RequestMapping(value="/{fileName}", method = RequestMethod.PUT) public ResponseEntity uploadFile(@PathVariable(value="fileName") String fileName, @RequestBody File tmpFile) throws IOException { // .. process tmpFile, e.g. tmpFile.renameTo(new File(fileName); return new ResponseEntity<String>(HttpStatus.CREATED); } 

Don't forget to register your FileMessageConverter, e.g.

@Configuration @EnableWebMvc @ComponentScan(basePackages = {"my.controller.package"}) public class WebConfig extends WebMvcConfigurerAdapter { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(new FileMessageConverter()); } } 

The curl command to invoke the upload:

curl -v -X PUT -T "myfile" http://localhost:8080/mytargetfilename 

2 Comments

Nice. I Haven't thought of using a MessageConverter yet, great idea! I knew actually having a library out in the wild for this work was most likely NOT going to happen and we've resolved that we're going to have to write some code. Question though, how would you clean up the temporary file in this case?
I don't think you can guarantee an early cleanup. But you might want to have a look how Apache Axis implemented their TempFileManager. Or even use it instead of File.createTempFile svn.apache.org/repos/asf/axis/axis2/java/core/trunk/modules/…

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.