如何在一个纯java的项目中发送play框架中的InputStream,而不发送分块响应?

如何在一个纯java的项目中发送play框架中的InputStream,而不发送分块响应?,java,playframework,playframework-2.3,Java,Playframework,Playframework 2.3,在Java(仅限)Play 2.3项目中,我们需要将InputStream的非分块响应直接发送到客户端。InputStream来自一个远程服务,我们希望从该服务直接流到客户端,而不阻塞或缓冲本地文件。因为我们在读取输入流之前就知道了大小,所以我们不想要分块响应 对于已知大小的输入流,返回结果的最佳方法是什么?(最好不使用Scala) 当查看用于返回文件对象的默认ok(file,…)方法时,它深入到只能从scala访问的播放内部,并使用甚至不能从外部访问的播放内部执行上下文。如果它的工作原理相同就

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

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


当查看用于返回文件对象的默认
ok(file,…)
方法时,它深入到只能从scala访问的播放内部,并使用甚至不能从外部访问的播放内部执行上下文。如果它的工作原理相同就好了,只需使用
InputStream
FWIW,我现在找到了一种为InputStream提供服务的方法,它基本上复制了
结果的逻辑。ok(File)
方法允许直接传入
InputStream

关键是使用scala调用从InputStream创建枚举器:
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;
    }

}
private final MessageDispatcher fileServeContext=Akka.system().dispatchers().lookup(“文件服务上下文”);
受保护的void serveInputStream(InputStream InputStream,字符串文件名,长contentLength){
response().setHeader(
HttpHeaders.CONTENT\u处置,
“附件;文件名=\”“+文件名+\”);
//根据文件扩展名设置内容类型标头。
scala.Option contentType=MimeTypes.forFileName(文件名);
if(contentType.isDefined()){
response().setHeader(CONTENT_TYPE,contentType.get());
}否则{
response();
}
response().setHeader(CONTENT_LENGTH,Long.toString(contentLength));
返回新的WrappedScalResult(new play.api.mvc.Result(
新的ResponseHeader(StatusCode.OK,toScalaMap(response().getHeaders()),
//Enumerator.fromStream()也将在输入流完成后关闭它。
play.api.libs.iteratee.Enumerator$.MODULE$.fromStream(
输入流,
文件大小,
fileServeContext),
play.api.mvc.HttpConnection.KeepAlive());
}
/**
*一个简单的结果,它包装了一个scala结果,因此我们可以从java控制器调用它。
*/
私有静态类WrappedScalResult实现结果{
private play.api.mvc.Result scalaResult;
public WrappedScalaResult(play.api.mvc.Result scalaResult){
this.scalaResult=scalaResult;
}
@凌驾
public play.api.mvc.Result toScala(){
返回scalaResult;
}
}

如果不使用Java进行阻塞,就无法完成此操作。另一方面,您可以不首先写入本地文件就可以完成这项工作。好吧,再考虑一下,您可以尝试一下,看看是否可以在代码中使用它。它非常前卫,所以…很明显,从InputStream读取会阻塞,但我认为netty处理的客户端连接不应该阻塞。。也就是说,如果我使用4k缓冲区,从该输入流读取4k缓冲区会被阻塞,但我会假设在将这些4k缓冲区移交给play之后,框架将释放线程。。(直到需要读取下一个4k缓冲块)-假设我们从中读取InputStream的服务仍然比外部客户端快得多(因为它位于同一个基础设施中),所以大部分时间将等待客户端。是的,这就是为什么需要背压。。。长话短说:您需要在InputStream上有一个回调,然后需要类似于Iteratee+Enumerate的概念来反应性地处理这个问题。当然,您可以尝试用Java编写所有这些内容,这是可能的,但这是一个相当复杂的任务。