【问题标题】:How to send a InputStream in play framework in an java only project without chunked responses?如何在没有分块响应的纯 java 项目中在播放框架中发送 InputStream?
【发布时间】:2015-04-10 09:58:39
【问题描述】:

在 Java(仅限)Play 2.3 项目中,我们需要将InputStream 的非分块响应直接发送到客户端。 InputStream 来自一个远程服务,我们希望从该服务直接流式传输到客户端,而不阻塞或缓冲到本地文件。由于我们在读取输入流之前就知道大小,因此我们不想要分块响应。

为已知大小的输入流返回结果的最佳方法是什么? (最好不使用 Scala)。

在查看用于返回 File 对象的默认 ok(file, ..) 方法时,它深入到只能从 scala 访问的 play 内部,它使用甚至无法从外部访问的 play-internal 执行上下文。如果它的工作原理相同,那就太好了,只需 InputStream

【问题讨论】:

  • 你不能在没有阻塞的情况下使用 Java。另一方面,您可以在不先写入本地文件的情况下执行此操作。
  • 好吧,再想一想,您可以尝试reactive-streams 看看您是否可以将其放入您的代码中。很前卫所以...
  • 好吧,很明显从 InputStream 读取会阻塞,但我认为 netty 处理的客户端连接不应该阻塞.. 即。如果我使用的是 4k 缓冲区,则从该 InputStream 读取 4k 缓冲区会阻塞,但我会假设在将这些 4k 缓冲区交给播放框架后会释放线程..(直到需要读取下一个 4k 缓冲区块) - 假设是,我们从中读取 InputStream 的服务仍然比外部客户端快得多(因为它位于相同的基础架构中),因此大部分时间将等待客户端。
  • 是的,这就是你需要背压的原因......长话短说:你需要在 InputStream 上有一个回调,然后你需要类似于 Iteratee + Enumeratee 的概念来反应地处理这个问题。当然,您可以尝试用 Java 编写所有这些,这是可能的,但它是一个任务伙伴。

标签: java playframework playframework-2.3


【解决方案1】:

FWIW 我现在找到了一种提供 InputStream 的方法,它基本上复制了 Results.ok(File) 方法允许直接传入 InputStream 的逻辑。

关键是使用 scala 调用从 InputStream 创建一个 Enumerator:play.api.libs.iteratee.Enumerator$.MODULE$.fromStream

private final MessageDispatcher fileServeContext = Akka.system().dispatchers().lookup("file-serve-context");

protected void serveInputStream(InputStream inputStream, String fileName, long contentLength) {
    response().setHeader(
            HttpHeaders.CONTENT_DISPOSITION,
            "attachment; filename=\"" + fileName + "\"");

    // Set Content-Type header based on file extension.
    scala.Option<String> contentType = MimeTypes.forFileName(fileName);
    if (contentType.isDefined()) {
        response().setHeader(CONTENT_TYPE, contentType.get());
    } else {
        response().setHeader(CONTENT_TYPE, ContentType.DEFAULT_BINARY.getMimeType());
    }

    response().setHeader(CONTENT_LENGTH, Long.toString(contentLength));

    return new WrappedScalaResult(new play.api.mvc.Result(

        new ResponseHeader(StatusCode.OK, toScalaMap(response().getHeaders())),

        // Enumerator.fromStream() will also close the input stream once it is done.
        play.api.libs.iteratee.Enumerator$.MODULE$.fromStream(
            inputStream,
            FILE_SERVE_CHUNK_SIZE,
            fileServeContext),

        play.api.mvc.HttpConnection.KeepAlive()));
}

/**
 * A simple Result which wraps a scala result so we can call it from our java controllers.
 */
private static class WrappedScalaResult implements Result {

    private play.api.mvc.Result scalaResult;

    public WrappedScalaResult(play.api.mvc.Result scalaResult) {
        this.scalaResult = scalaResult;
    }

    @Override
    public play.api.mvc.Result toScala() {
        return scalaResult;
    }

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多