I am trying to write a spring mvc method that can receive either a multipart/form or transfer-encoding chunked file upload. I can write a separate method to handle each type but I'd like to do it with the same method so i can use the same REST POST uri such as:
http://host:8084/attachments/testupload
Here is my best attempt so far:
@RequestMapping(value = { "/testupload" }, method = RequestMethod.POST, produces = "application/json") public @ResponseBody ResponseEntity<MessageResponseModel> testUpload( @RequestParam(value = "filedata", required = false) MultipartFile filedata, final HttpServletRequest request) throws IOException { InputStream is = null; if (filedata == null) { is = request.getInputStream(); } else { is = filedata.getInputStream(); } byte[] bytes = IOUtils.toByteArray(is); System.out.println("read " + bytes.length + " bytes."); return new ResponseEntity<MessageResponseModel>(null, null, HttpStatus.OK); }
Using the above method I can upload a multipart file, but if i upload a chunked file I get an exception from spring that says:
org.springframework.web.multipart.MultipartException: \ The current request is not a multipart request
If I remove the MultipartFile request param it works great for transfer encoding chunked. If I leave it in it works great for MultipartFile uploads. How can I do it with the same method to handle both upload types?
This works fine for chunked:
@RequestMapping(value = { "/testupload" }, method = RequestMethod.POST, produces = "application/json") public @ResponseBody ResponseEntity<MessageResponseModel> testUpload( final HttpServletRequest request) throws IOException { InputStream is = null; is = request.getInputStream(); byte[] bytes = IOUtils.toByteArray(is); System.out.println("read " + bytes.length + " bytes."); return new ResponseEntity<MessageResponseModel>(null, null, HttpStatus.OK); }
and this works great for MultipartFile:
@RequestMapping(value = { "/testupload" }, method = RequestMethod.POST, produces = "application/json") public @ResponseBody ResponseEntity<MessageResponseModel> testUpload( @RequestParam MultipartFile filedata) throws IOException { InputStream is = null; is = filedata.getInputStream(); byte[] bytes = IOUtils.toByteArray(is); System.out.println("read " + bytes.length + " bytes."); return new ResponseEntity<MessageResponseModel>(null, null, HttpStatus.OK); }
It should be possible, does anybody know how to do this?
Thank you, Steve
A new attribute "maxSwallowSize" is the key to deal this situation. It should happen when you upload a file which is larger than 2M. Because the 2M is the default value of this new attribute .
The dependency needed for MVC is the spring-webmvc package. The javax. validation and the hibernate-validator packages will be also used here for validation. The commons-io and commons-fileupload packages are used for uploading the file.
Multipart upload allows you to upload a single object as a set of parts. Each part is a contiguous portion of the object's data. You can upload these object parts independently and in any order. If transmission of any part fails, you can retransmit that part without affecting other parts.
Excerpt from my code (Spring 3.2, blueimp file upload with AngularJS):
/** * Handles chunked file upload, when file exceeds defined chunked size. * * This method is also called by modern browsers and IE >= 10 */ @RequestMapping(value = "/content-files/upload/", method = RequestMethod.POST, headers = "content-type!=multipart/form-data") @ResponseBody public UploadedFile uploadChunked( final HttpServletRequest request, final HttpServletResponse response) { request.getHeader("content-range");//Content-Range:bytes 737280-819199/845769 request.getHeader("content-length"); //845769 request.getHeader("content-disposition"); // Content-Disposition:attachment; filename="Screenshot%20from%202012-12-19%2017:28:01.png" request.getInputStream(); //actual content. //Regex for content range: Pattern.compile("bytes ([0-9]+)-([0-9]+)/([0-9]+)"); //Regex for filename: Pattern.compile("(?<=filename=\").*?(?=\")"); //return whatever you want to json return new UploadedFile(); } /** * Default Multipart file upload. This method should be invoked only by those that do not * support chunked upload. * * If browser supports chunked upload, and file is smaller than chunk, it will invoke * uploadChunked() method instead. * * This is instead a fallback method for IE <=9 */ @RequestMapping(value = "/content-files/upload/", method = RequestMethod.POST, headers = "content-type=multipart/form-data") @ResponseBody public HttpEntity<UploadedFile> uploadMultipart( final HttpServletRequest request, final HttpServletResponse response, @RequestParam("file") final MultipartFile multiPart) { //handle regular MultipartFile // IE <=9 offers to save file, if it is returned as json, so set content type to plain. HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.TEXT_PLAIN); return new HttpEntity<>(new UploadedFile(), headers); }
This should get you started. Minimal testing done on IE8, IE9, IE10, Chrome, FF. Of course there might be issues, and probably there is an easier way of extracting content ranges, but .. works for me.
Here is the controller for that
package com.faisalbhagat.web.controller; @Controller @RequestMapping(value = { "" }) public class UploadController { @RequestMapping(value = "/uploadMyFile", method = RequestMethod.POST) @ResponseBody public String handleFileUpload(MultipartHttpServletRequest request) throws Exception { Iterator<String> itrator = request.getFileNames(); MultipartFile multiFile = request.getFile(itrator.next()); try { // just to show that we have actually received the file System.out.println("File Length:" + multiFile.getBytes().length); System.out.println("File Type:" + multiFile.getContentType()); String fileName=multiFile.getOriginalFilename(); System.out.println("File Name:" +fileName); String path=request.getServletContext().getRealPath("/"); //making directories for our required path. byte[] bytes = multiFile.getBytes(); File directory= new File(path+ "/uploads"); directory.mkdirs(); // saving the file File file=new File(directory.getAbsolutePath()+System.getProperty("file.separator")+picture.getName()); BufferedOutputStream stream = new BufferedOutputStream( new FileOutputStream(file)); stream.write(bytes); stream.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); throw new Exception("Error while loading the file"); } return toJson("File Uploaded successfully.") } public String toJson(Object data) { ObjectMapper mapper=new ObjectMapper(); StringBuilder builder=new StringBuilder(); try { builder.append(mapper.writeValueAsString(data)); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return builder.toString(); } }
you can find comple solution at with client side code at http://faisalbhagat.blogspot.com/2014/09/springmvc-fileupload-with-ajax-and.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With