Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SpringBoot: Large Streaming File Upload Using Apache Commons FileUpload

Am trying to upload a large file using the 'streaming' Apache Commons File Upload API.

The reason I am using the Apache Commons File Uploader and not the default Spring Multipart uploader is that it fails when we upload very large file sizes (~2GB). I working on a GIS application where such file uploads are pretty common.

The full code for my file upload controller is as follows:

@Controller public class FileUploadController {      @RequestMapping(value="/upload", method=RequestMethod.POST)     public void upload(HttpServletRequest request) {         boolean isMultipart = ServletFileUpload.isMultipartContent(request);         if (!isMultipart) {             // Inform user about invalid request             return;         }          //String filename = request.getParameter("name");          // Create a new file upload handler         ServletFileUpload upload = new ServletFileUpload();          // Parse the request         try {             FileItemIterator iter = upload.getItemIterator(request);             while (iter.hasNext()) {                 FileItemStream item = iter.next();                 String name = item.getFieldName();                 InputStream stream = item.openStream();                 if (item.isFormField()) {                     System.out.println("Form field " + name + " with value " + Streams.asString(stream) + " detected.");                 } else {                     System.out.println("File field " + name + " with file name " + item.getName() + " detected.");                     // Process the input stream                     OutputStream out = new FileOutputStream("incoming.gz");                     IOUtils.copy(stream, out);                     stream.close();                     out.close();                  }             }         }catch (FileUploadException e){             e.printStackTrace();         }catch (IOException e){             e.printStackTrace();         }     }      @RequestMapping(value = "/uploader", method = RequestMethod.GET)     public ModelAndView uploaderPage() {         ModelAndView model = new ModelAndView();         model.setViewName("uploader");         return model;     }  } 

The trouble is that the getItemIterator(request) always returns an iterator that does not have any items (i.e. iter.hasNext() ) always returns false.

My application.properties file is as follows:

spring.datasource.driverClassName=org.postgresql.Driver spring.datasource.url=jdbc:postgresql://localhost:19095/authdb spring.datasource.username=georbis spring.datasource.password=asdf123  logging.level.org.springframework.web=DEBUG  spring.jpa.hibernate.ddl-auto=update  multipart.maxFileSize: 128000MB multipart.maxRequestSize: 128000MB  server.port=19091 

The JSP view for the /uploader is as follows:

<html> <body> <form method="POST" enctype="multipart/form-data" action="/upload">     File to upload: <input type="file" name="file"><br />     Name: <input type="text" name="name"><br /> <br />     Press here to upload the file!<input type="submit" value="Upload">     <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" /> </form> </body> </html> 

What might I be doing wrong?

like image 798
balajeerc Avatar asked Sep 25 '15 12:09

balajeerc


People also ask

Why am I using the Apache Commons file uploader instead of multipart?

The reason I am using the Apache Commons File Uploader and not the default Spring Multipart uploader is that it fails when we upload very large file sizes (~2GB). I working on a GIS application where such file uploads are pretty common. The full code for my file upload controller is as follows:

What is the maximum file size for file upload in Spring Boot?

Now it will read the maximum file size from the spring-boot-file-upload.yml file in Consul, which is set to 10MB. Now let us create the Controller class, FileUploadRestApi.java, where we have defined the REST API to upload the file.

How do I upload multipart files in Spring Boot?

Closed 11 months ago. Spring boot's default MultiPartResolver interface handles the uploading of multipart files by storing them on the local file system. Before the controller method is entered, the entire multipart file must finish uploading to the server.

How to upload a file using post API in Spring Boot?

To try the POST API for uploading a file, click on it. Once it is expanded, click on Try Now and then choose a file to upload and Execute. The project can be downloaded from GitHub: spring-boot-file-upload.


1 Answers

Thanks to some very helpful comments by M.Deinum, I managed to solve the problem. I have cleaned up some of my original post and am posting this as a complete answer for future reference.

The first mistake I was making was not disabling the default MultipartResolver that Spring provides. This ended up in the resolver processing the HttpServeletRequest and thus consuming it before my controller could act on it.

The way to disable it, thanks to M. Deinum was as follows:

multipart.enabled=false 

However, there was still another hidden pitfall waiting for me after this. As soon as I disabled default multipart resolver, I started getting the following error when trying to make an upload:

Fri Sep 25 20:23:47 IST 2015 There was an unexpected error (type=Method Not Allowed, status=405). Request method 'POST' not supported 

In my security configuration, I had enabled CSRF protection. That necessitated that I send my POST request in the following manner:

<html> <body> <form method="POST" enctype="multipart/form-data" action="/upload?${_csrf.parameterName}=${_csrf.token}">     <input type="file" name="file"><br>     <input type="submit" value="Upload"> </form> </body> </html> 

I also modified my controller a bit:

@Controller public class FileUploadController {     @RequestMapping(value="/upload", method=RequestMethod.POST)     public @ResponseBody Response<String> upload(HttpServletRequest request) {         try {             boolean isMultipart = ServletFileUpload.isMultipartContent(request);             if (!isMultipart) {                 // Inform user about invalid request                 Response<String> responseObject = new Response<String>(false, "Not a multipart request.", "");                 return responseObject;             }              // Create a new file upload handler             ServletFileUpload upload = new ServletFileUpload();              // Parse the request             FileItemIterator iter = upload.getItemIterator(request);             while (iter.hasNext()) {                 FileItemStream item = iter.next();                 String name = item.getFieldName();                 InputStream stream = item.openStream();                 if (!item.isFormField()) {                     String filename = item.getName();                     // Process the input stream                     OutputStream out = new FileOutputStream(filename);                     IOUtils.copy(stream, out);                     stream.close();                     out.close();                 }             }         } catch (FileUploadException e) {             return new Response<String>(false, "File upload error", e.toString());         } catch (IOException e) {             return new Response<String>(false, "Internal server IO error", e.toString());         }          return new Response<String>(true, "Success", "");     }      @RequestMapping(value = "/uploader", method = RequestMethod.GET)     public ModelAndView uploaderPage() {         ModelAndView model = new ModelAndView();         model.setViewName("uploader");         return model;     } } 

where Response is just a simple generic response type I use:

public class Response<T> {     /** Boolean indicating if request succeeded **/     private boolean status;      /** Message indicating error if any **/     private String message;      /** Additional data that is part of this response **/     private T data;      public Response(boolean status, String message, T data) {         this.status = status;         this.message = message;         this.data = data;     }      // Setters and getters     ... } 
like image 69
balajeerc Avatar answered Oct 02 '22 09:10

balajeerc