Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asynchronous file upload in Spring

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.

like image 298
CAL5101 Avatar asked May 18 '12 23:05

CAL5101


People also ask

What is asynchronous file upload?

This feature allows you to upload and remove files asynchronously. When multiple files are chosen in Asynchronous upload,files will be uploaded one by one to the server. User interaction with the page will not be interrupted at the time of upload. User can also remove the file even after uploading.

How do I upload a file to spring?

With the server running, you need to open a browser and visit http://localhost:8080/ to see the upload form. Pick a (small) file and press Upload. You should see the success page from the controller. If you choose a file that is too large, you will get an ugly error page.

Is Spring synchronous or asynchronous?

By default, Spring uses a SimpleAsyncTaskExecutor to actually run these methods asynchronously. But we can override the defaults at two levels: the application level or the individual method level.

How do you get asynchronous in spring boot?

Spring Boot uses a SimpleAsyncTaskExector to run an async method. This Executor runs by default and it can be overridden at two levels- at the individual method levels or at the application level. 3. Add @Async Annotation to a service Method for performing thread's task without interrupting another parallerl process.


1 Answers

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
like image 146
Igor Artamonov Avatar answered Oct 05 '22 23:10

Igor Artamonov