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
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.
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