Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Uploading file as stream in play framework 2.0

I'm writing a play 2.0 java application that allows users to upload files. Those files are stored on a third-party service I access using a Java library, the method I use in this API has the following signature:

void store(InputStream stream, String path, String contentType)

I've managed to make uploads working using the following simple controller:

public static Result uploadFile(String path) {
    MultipartFormData body = request().body().asMultipartFormData();
    FilePart filePart = body.getFile("files[]");
    InputStream is    = new FileInputStream(filePart.getFile())
    myApi.store(is,path,filePart.getContentType()); 
    return ok();
  }

My concern is that this solution is not efficient because by default the play framework stores all the data uploaded by the client in a temporary file on the server then calls my uploadFile() method in the controller.

In a traditional servlet application I would have written a servlet behaving this way:

myApi.store(request.getInputStream(), ...)

I have been searching everywhere and didn't find any solution. The closest example I found is Why makes calling error or done in a BodyParser's Iteratee the request hang in Play Framework 2.0? but I didn't found how to modify it to fit my needs.

Is there a way in play2 to achieve this behavior, i.e. having the data uploaded by the client to go "through" the web-application directly to another system ?

Thanks.

like image 836
Davz Avatar asked Aug 22 '12 05:08

Davz


People also ask

How do I upload a file to play framework?

The standard way to upload files in a web application is to use a form with a special multipart/form-data encoding, which lets you mix standard form data with file attachment data. Note: The HTTP method used to submit the form must be POST (not GET ). The getRef() method gives you a reference to a TemporaryFile .

How do I upload files to spring boot?

Spring Boot file uploader Create a Spring @Controller class; Add a method to the controller class which takes Spring's MultipartFile as an argument; Save the uploaded file to a directory on the server; and. Send a response code to the client indicating the Spring file upload was successful.

What is multipart file upload?

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.

What is spring boot MultipartFile?

public interface MultipartFile extends InputStreamSource. A representation of an uploaded file received in a multipart request. The file contents are either stored in memory or temporarily on disk. In either case, the user is responsible for copying file contents to a session-level or persistent store as and if desired ...


1 Answers

I've been able to stream data to my third-party API using the following Scala controller code:

def uploadFile() = 
    Action( parse.multipartFormData(myPartHandler) ) 
    {
      request => Ok("Done")
    }

def myPartHandler: BodyParsers.parse.Multipart.PartHandler[MultipartFormData.FilePart[Result]] = {
        parse.Multipart.handleFilePart {
          case parse.Multipart.FileInfo(partName, filename, contentType) =>
            //Still dirty: the path of the file is in the partName...
            String path = partName;

            //Set up the PipedOutputStream here, give the input stream to a worker thread
            val pos:PipedOutputStream = new PipedOutputStream();
            val pis:PipedInputStream  = new PipedInputStream(pos);
            val worker:UploadFileWorker = new UploadFileWorker(path,pis);
            worker.contentType = contentType.get;
            worker.start();

            //Read content to the POS
            Iteratee.fold[Array[Byte], PipedOutputStream](pos) { (os, data) =>
              os.write(data)
              os
            }.mapDone { os =>
              os.close()
              Ok("upload done")
            }
        }
   }

The UploadFileWorker is a really simple Java class that contains the call to the thrid-party API.

public class UploadFileWorker extends Thread {
String path;
PipedInputStream pis;

public String contentType = "";

public UploadFileWorker(String path, PipedInputStream pis) {
    super();
    this.path = path;
    this.pis = pis;
}

public void run() {
    try {
        myApi.store(pis, path, contentType);
        pis.close();
    } catch (Exception ex) {
        ex.printStackTrace();
        try {pis.close();} catch (Exception ex2) {}
    }
}

}

It's not completely perfect because I would have preferred to recover the path as a parameter to the Action but I haven't been able to do so. I thus have added a piece of javascript that updates the name of the input field (and thus the partName) and it does the trick.

like image 180
Davz Avatar answered Oct 02 '22 23:10

Davz