5

Using a naked servlet's doPost, when a file upload starts, doPost is immediately called. I can then stream the files from the request object using the commons FileItemIterator.

Using Spring MVC, I can't seem to get the controller method to fire until after the file(s) have all been received by the server, which is not ideal.

I want my servlet/controller method to process as many files as it can and perform some rollback operations if an upload is interrupted. I can't do that with Spring MVC currently.

public void doPost(HttpServletRequest request, HttpServletResponse res){
//I can immediately stream the response here 
}

vs.

@RequestMapping(value="/uploadFiles", method= RequestMethod.POST)
public @ResponseBody String addFiles(ContentManagerTicket ticket, HttpServletRequest request){
//I can't do anything until the files are received - whether i use a HttpServletRequset or MultiPartFile
}

Any ideas? Thanks!

Dan
  • 3,182
  • 1
  • 27
  • 27
  • Have you configured multipart support in spring? – M. Deinum Jun 24 '14 at 13:58
  • I can accept files just fine in Spring, I have the commons multipart setup. My issue is that I want the controller method to fire immediately, even before the files are completely uploaded (i'll stream them) – Dan Jun 24 '14 at 15:14
  • 1
    It is due to this setup that you cannot stream files, hence my question if you have configured multipart support. The file upload support parses the files already so that they are available for retrieval as method arguments. The parsing/retrieval of files takes place before it even reaches your controller. – M. Deinum Jun 24 '14 at 18:05
  • You are right, thank you! Disabling the multipart resolver solved the issue and the controller fired immediately! Other controllers rely on that resolver though so I'll have to cherry pick my new one to be excluded. – Dan Jun 24 '14 at 19:58

1 Answers1

7

You want streaming file uploads however when using Spring’s multipart (file upload) support it uses the classic approach. This basically means that all multipart parts of a request are parsed before the request is actually handed down to the controller. This is needed because a MultipartFile can be used as method argument and for this to work it needs to be available to the controller.

If you want to handle streaming file uploads you will have to disable Spring's multipart support and do the parsing yourself in the controller, the same way you would do in a servlet.

@Controller
public class FileUploadController {

    @RequestMapping("/upload")
    public void upload(HttpServletRequest request) {
        boolean isMultipart = ServletFileUpload.isMultipartContent(request);
        if (!isMultipart) {
            // Inform user about invalid request
        }

        // 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()) {
                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
            ...
            }
        }
    }
}

See also how to upload a file using commons file upload streaming api and Apache commons fileupload "Streaming API"

Community
  • 1
  • 1
M. Deinum
  • 115,695
  • 22
  • 220
  • 224
  • Thanks for writing that up! The app I'm working on already relied on the MultiPartResolver so I had to extend it and override the "isMultiPart" method to filter out certain requests by the requestUri path. – Dan Jun 25 '14 at 14:56
  • 1
    I just came across this and looks like I have a similar issue but I don't know how to disable Spring's multipart support. I removed any multipart configuration but I believe a default one is used (maybe I'm mistaken). I have the code above but iter.hasNext() is always false which I gather may be down to the request being parsed beforehand. Would you happen to have any instructions on how to disable the parsing by Spring? – Ruairi O'Brien Nov 11 '14 at 17:50
  • 6
    Sorry, never mind. I am using Spring boot and was using AutoConfiguration so the solution there was: @EnableAutoConfiguration(exclude = {MultipartAutoConfiguration.class}). Thanks! – Ruairi O'Brien Nov 11 '14 at 18:17
  • Thanks @Ruairi, I had the same issue and it was driving me crazy (Spring consuming my inputstream before ServletFileUpload). – Mike Cantrell Dec 23 '14 at 17:12
  • And how do you disable Spring multipart support in a raw Spring (not Spring Boot) project? I've founde lots of pages telling how to disable it in Spring Boot but none tells how to do it in raw Spring. By the way, I am using Spring 5. – joanlofe Dec 13 '18 at 19:37