Asynchronous file upload in Spring
Asked Answered
C

1

11

Here's what I'm doing. I want to upload multipart file via Ajax to my Spring web app. When the server receives the POST request, it creates a ticket number in the database. It then starts a thread that handles the actual file upload. The server then returns the ticket number.

I am using the CommonsMultipartResolver to handle the request and I have set the resolveLazily flag to true so that the Multipart isn't resolved right away.

So here's something along the lines of what I have

@Controller
public class myController{

    @RequestMapping(value = "/upload", method = RequestMethod.POST)
    @ResponseStatus(value = HttpStatus.OK)
    @ResponseBody
    public String upload(MultipartHttpServletRequest request, String fileName){

        String ticket = dao.createUploadTicket(fileName);
        Runnable run = new Runnable(){

            @Override
            public void run(){

                dao.writeUpdate(ticket, "Getting data from request");
                final MultipartFile file = request.getFile("theFile");
                dao.writeUpdate(ticket, "Multipart file processed");
                try {
                   dao.writeUpdate(ticket, "Saving file to disk");
                   file.transferTo(new File("/myDirectory"));
                   dao.writeUpdate(ticket, "File saved to disk");
                }
                catch(Exception e){
                   dao.writeUpdate(ticket, "File upload failed with the exception " + e.toString());
                }
            }
        };
        Thread t = new Thread(run);
        t.start();
        return ticket;
    }
}

So the point here is that the ticket number can be used to get the progress updates. Say a large file is being uploaded. The client that made the file upload POST (say in this instance an Ajax request) can do it asynchronously and get back a ticket number. The client can the use that ticket number to determine the stage of the file upload and display information in another page.

One other use is that I can have an HTML page that makes a request to the server for all ticket numbers, then shows a "live" view of all the file uploads that are taking place on the server.

I haven't been able to get this to work because as soon as the controller returns, Spring calls cleanupMultipart() in the CommonsMultipartResolver. Since the resolveLazily flag is set to false, when cleanupMultipart() is called, it will begin to resolve and initialize the multipart files. This leads to a race condition between the call to "request.getFile("theFile");" in the runnable and the cleanupMultipart() call eventually leading to an exception.

Anyone have any ideas? Am I breaking some kind of HTTP contract here by wanting to do back-end asynchronous file handling.

Chinatown answered 18/5, 2012 at 23:27 Comment(1)
Commention on my own post. I found the following blogs which may help come up with a solution. blog.springsource.org/2012/05/06/… blogs.oracle.com/enterprisetechtips/entry/… I'll post back if I come up with a working example.Chinatown
B
8

HTTP request is already executed in its own thread, and client can make few request in parallel, asynchronously. So you don't need to start a new thread. Just save/process file as usual, in main thread. Just make 'async file upload' only on client side.

Also, you should send http response only when you've processed the input. I mean you can't read input header, make a http response, and continue reading data from the browser. Consume input -> Process it -> Send output, that how HTTP 1/1.1 protocols works.

If you need a ticket number to send to upload, you could create it before actual uploading, by using a two step upload, like:

  • Ajax GET request to get ticket number
  • POST a file content and ticket number (received from previous step)
  • + ajax GET get current status for ticket, anytime later, async
Brubaker answered 13/6, 2012 at 5:53 Comment(2)
agree. I just want to add a little clarification: what user1404399 is asking is how to "async upload" by a new thread at server side, while the right way to handle this issue is to make ajax post by javascript to the handle-uploading action, and report the progress to user by polling to another action. Is that your idea?Marylyn
Thanks for the replies guys. I had a good feeling that this was not possible, but I was being pushed to find a solution. We didn't end up implementing anything but just a normal file upload. P.S. I updated my display name to something more legible than user1404399Chinatown

© 2022 - 2024 — McMap. All rights reserved.