2015-04-10 1 views
4

В проекте Java (only) Play 2.3 нам необходимо отправить не-chunked ответ InputStream непосредственно клиенту. InputStream поступает из удаленного сервиса, из которого мы хотим напрямую передать клиенту, без блокировки или буферизации в локальный файл. Так как мы знаем размер перед чтением входного потока, мы не хотим, чтобы был отрезанный ответ.Как отправить InputStream в рамки игры в проекте только java без фрагментированных ответов?

Каков наилучший способ возврата результата для входного потока с известным размером? (предпочтительнее без использования Scala).

При поиске по умолчанию метода ok(file, ..) для возвращаемых объектов File он проникает в глубину встроенных игр, которые доступны только из scala, и использует контекст внутреннего исполнения, к которому невозможно получить доступ извне. Было бы неплохо, если бы он работал одинаково, только с InputStream.

+0

Вы не можете сделать это, без блокировки, с помощью Java. С другой стороны, вы можете сделать это, не записывая сначала локальный файл. – sebster

+0

Ну, во-вторых, вы можете попробовать [реактивные потоки] (https://github.com/reactive-streams/reactive-streams-jvm/) и посмотреть, можете ли вы поместиться в коде. Это довольно avantgarde так ... – sebster

+0

хорошо, ясно, что чтение из InputStream будет блокироваться, но я предполагаю, что клиентское соединение, обработанное netty, не должно блокироваться. если я использую буфер 4k, чтение 4k-буфера из этого InputStream будет блокировать, но я бы предположил, что после передачи этих 4k-буфера для воспроизведения фрейма высвободит поток .. (пока следующий буферный блок 4k не будет прочитан). Предполагается, что эта служба, откуда мы читаем InputStream, все еще намного быстрее, чем внешние клиенты (поскольку она живет в одной и той же инфраструктуре), поэтому большинство времени будет ждать клиента. – herbert

ответ

1

FWIW Теперь я нашел способ обслуживать InputStream, который в основном дублирует логику, которую метод Results.ok(File) позволяет прямо передавать в InputStream.

Ключ должен использовать вызов SCALA для создания Enumerator из 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; 
    } 

} 

 Смежные вопросы

  • Нет связанных вопросов^_^