我正在编写一个 play 2 应用程序,但我正在努力解决文件流问题。我使用具有以下签名的方法使用第三方 API 检索我的文件:
FileMetadata getFile(OutputStream destination, String fileId)
在传统的 Servlet 应用程序中,如果我想将内容发送给我的客户端,我会执行以下操作:
HttpServletResponse resp;
myService.getFile(resp.getOutpuStream, fileId);
我的问题是,在我的 play 2 Controller 类中,我无法访问底层的 OuputStream,因此我的控制器方法的最简单实现是:
public static downloadFile(String id) {
ByteArrayOutputStream baos = new BAOS(...);
myApi.getFile(baos,id); //Load inside temp Array
ByteArrayInputStream bais = new BAIS(baos.toByteArray())
return Ok(bais);
}
它会起作用,但它需要在提供之前将整个内容加载到内存中,所以它不是一个选项(文件可能很大)。
我正在考虑一个解决方案,包括:
- 在我的控制器中定义一个 ByteArrayOutputStream (baos)
- 在参数中使用此 baos 调用第三方 API
- 使用 play framework 的 chunk return 发送 baos 的内容,一旦第 3 方 API 写入内容
问题是我不知道它是否可能(对 getFile 的调用被阻塞,因此它需要多个具有共享 OutputStream 的线程),也不知道它是否过大。
有人曾经遇到过这种问题并找到了解决方案吗?我提出的解决方案可以解决我的问题吗?
任何见解将不胜感激。
谢谢
编辑 1 根据 kheraud 的建议,我设法找到了一个可行但仍不完美的解决方案(代码如下)。
不幸的是,如果在调用 getFile 方法期间出现问题,则不会将错误发送回客户端(因为我返回了 Ok),并且浏览器会无限期地等待一个永远不会出现的文件。
有没有办法处理这种情况?
public static Result downloadFile(String fileId {
Thread readerThread = null;
try {
PipedOutputStream pos = new PipedOutputStream();
PipedInputStream pis = new PipedInputStream(pos);
//Reading must be done in another thread
readerThread = new DownloadFileWorker(fileId,pos);
readerThread.start();
return ok(pis);
} catch (Exception ex) {
ex.printStackTrace();
return internalServerError(ex.toString());
}
}
static class DownloadFileWorker extends Thread{
String fileId;
PipedOutputStream pos;
public DownloadFileWorker(String fileId, PipedOutputStream pos) {
super();
this.fileId = fileId
this.pos = pos;
}
public void run(){
try {
myApi.getFile(pos,fileId);
pos.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
编辑 2
我找到了一种避免无限加载页面的方法,只需在工作线程的 catch() 部分添加一个 pos.close 即可。客户端最终得到一个零 KB 文件,但我想这比无限等待要好。