在 Play 框架 2.0 中将文件作为流上传

2022-09-03 02:33:49

我正在编写一个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();
  }

我担心的是这个解决方案效率不高,因为默认情况下,play框架将客户端上传的所有数据存储在服务器上的临时文件中,然后在控制器中调用我的 uploadFile() 方法。

在传统的 servlet 应用程序中,我会编写一个以这种方式运行的 servlet:

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

我一直在到处寻找,没有找到任何解决方案。我发现的最接近的例子是为什么在Play Framework 2.0中调用错误或在BodyParser的迭代请求中完成请求挂起?但我没有找到如何修改它以满足我的需求。

在play2中是否有一种方法可以实现这种行为,即让客户端上传的数据直接“通过”Web应用程序“到另一个系统?

谢谢。


答案 1

我已经能够使用以下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) {}
    }
}

}

它并不完全完美,因为我更希望将路径恢复为操作的参数,但我无法做到这一点。因此,我添加了一段javascript,用于更新输入字段的名称(以及partName),它就可以了。


答案 2

推荐