java - 使用 Play Framework 和第三方 API 流式传输大文件

标签 java playframework streaming

我正在编写一个 play 2 应用程序,但我正在努力解决文件流问题。 我使用具有以下签名的方法使用第三方 API 检索我的文件:

FileMetadata getFile(OutputStream destination, String fileId)

在传统的 Servlet 应用程序中,如果我想将内容发送到我的客户端,我会这样做:

HttpServletResponse resp;
myService.getFile(resp.getOutpuStream, fileId);

我的问题是,在我的 play 2 Controller 类中,我无法访问底层的 OuputStream,因此我的 Controller 方法的最简单实现是:

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);
 }

它会工作,但它需要在提供之前将整个内容加载到内存中,所以它不是一个选项(文件可能很大)。

我在考虑一个解决方案,包括:

  • 在我的 Controller 中定义一个 ByteArrayOutputStream (baos)
  • 使用这个baos参数调用第三方API
  • 使用play框架的chunk return发送内容 一旦由第 3 方 API 在里面写了一些东西,baos

问题是我不知道它是否可行(对 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 的文件,但我想这比无限等待要好。

最佳答案

Play2 Scala 框架中有一些东西是为此而设计的:Enumerators。这与您的想法非常接近。

你应该看看这个 doc page for details

我没有在 Play2 Java API 中找到类似的东西,但是查看 fw 代码源,你有一个:

public static Results.Status ok(java.io.InputStream content, int chunkSize)

接缝是您正在寻找的方法。实现可以在 play.mvc.Resultsplay.core.j.JavaResults 类中找到。

关于java - 使用 Play Framework 和第三方 API 流式传输大文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11860971/

相关文章:

unit-testing - Play 2 个框架单元测试 - java.lang.RuntimeException : DataSource user is null?

ruby - 为什么我的流式命令对于 MapReduce 基本程序会失败?

java - 使用 -libjars 的 MapReduce 流作业,自定义分区程序失败 : "class not found"

java - Android 应用程序中的 XML 解析

java - jframe 中的网格布局面板

java - @for 在排序映射上进行迭代 keySet() 不遵守顺序

java - 如何在 Scala Play 框架内比较表单上的日期/时间?

android - 将视频从android流式传输到桌面

java - Spring @RequestScope 是否有 Micronaut 类似物?

java - 从 Android 上传照片