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