Herbert Poul
Herbert Poul

Reputation: 4883

How to send a InputStream in play framework in an java only project without chunked responses?

In a Java (only) Play 2.3 project we need to send a non-chunked response of an InputStream directly to the client. The InputStream comes from a remote service from which we want to stream directly to the client, without blocking or buffering to a local file. Since we know the size before reading the input stream, we do not want a chunked response.

What is the best way to return a result for an input stream with a known size? (preferable without using Scala).

When looking at the default ok(file, ..) method for returning File objects it goes deep into play internals which are only accessible from scala, and it uses the play-internal execution context which can't even be accessed from outside. Would be nice if it would work identical, just with an InputStream.

Upvotes: 4

Views: 2288

Answers (1)

Herbert Poul
Herbert Poul

Reputation: 4883

FWIW I have now found a way to serve an InputStream, which basically duplicates the logic which the Results.ok(File) method to allow directly passing in an InputStream.

The key is to use the scala call to create an Enumerator from an 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;
    }

}

Upvotes: 1

Related Questions