在Play Framework 2.0中将文件上传为流
问题内容:
我正在编写一个Play 2.0
Java应用程序,该应用程序允许用户上传文件。这些文件存储在我使用Java库访问的第三方服务上,我在此API中使用的方法具有以下签名:
void store(InputStream stream, String path, String contentType)
我设法使用以下简单控制器使上传正常工作:
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();
}
我担心的是该解决方案效率不高,因为默认情况下,播放框架会将客户端上传的所有数据存储在服务器上的临时文件中,然后在控制器中调用我的uploadFile()方法。
在传统的servlet应用程序中,我将以这种方式编写一个servlet:
myApi.store(request.getInputStream(), ...)
我到处搜索,没有找到任何解决方案。我发现的最接近的示例是为什么在Play Framework2.0中使调用挂起错误或在BodyParser的Iteratee中完成了请求?但是我没有找到如何修改它以满足我的需求。
play2中是否有一种方法可以实现此行为,即让客户端上传的数据直接通过Web应用程序直接到达另一个系统?
谢谢。
问题答案:
我已经能够使用以下Scala控制器代码将数据流式传输到第三方API:
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")
}
}
}
UploadFileWorker是一个非常简单的Java类,其中包含对第三方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) {}
}
}
}
它不是完全完美的,因为我希望将路径恢复为Action的参数,但我无法做到这一点。因此,我添加了一段javascript,它更新了输入字段的名称(并因此更新了partName),并且可以解决问题。