java - 使用 CompletableFuture 时如何避免编译器警告关闭 OutputStream?

标签 java playframework java-8

我使用 CompletableFuture 进行异步操作,下载文件并通过 OutputStream 保存其内容。下面的代码可以工作,但编译器会警告我使用 try-with-resources 或在 finally-clause 中关闭 OutputStream,尽管它是在 Future 在 whenComplete 中完成后关闭的。

代码:

final OutputStream outputStream = Files.newOutputStream(file.toPath());
final String url = "https://example.com/some-download.zip";
final CompletionStage<WSResponse> futureResponse = this.client
        .url(url)
        .setMethod("GET")
        .stream();

futureResponse.thenCompose(res -> {
    downloadTask.setTotalBytes(res);
    Source<ByteString, ?> responseBody = res.getBodyAsSource();

    Sink<ByteString, CompletionStage<akka.Done>> outputWriter =
            Sink.foreach(bytes -> {
                downloadTask.addReceivedBytes(bytes.size());
                System.out.println(downloadTask.getProgressAsString());
                outputStream.write(bytes.toArray());
            });

    return responseBody.runWith(outputWriter, this.materializer);
}).whenComplete((res, error) -> {
    try {
        outputStream.close();
    } catch (final IOException e) {
        e.printStackTrace();
    }
});

警告:

enter image description here

问题:

当我使用 try-with-resources 时,程序会在将任何内容写入文件之前关闭 OutPutStream,因为 CompletableFuture 的异步性质不会阻塞。

那么有没有办法在 CompletionStage 中声明 OutputStream 并将其向下传递?

最佳答案

@AndyTurner 在评论中的建议是正确的。我只需稍微调整一下代码即可使其工作。

我没有意识到responseBody.runWith()返回另一个CompletionStage,因此使用try-with-resourcesfinally关闭了thenComponse block 中的OutputStream,因此返回的CompletionStage(来自responseBody.runWith() )无法再写入。这导致了错误。因此,我们只需使用 .toCompletableFuture().get() 在同一 block 中“同步”处理 responseBody.runWith() 即可。这没有问题,因为 block 本身在另一个线程中运行,即它保持异步。由于我们不再返回任何内容,因此我们还需要使用 thenAccept,它需要一个 Consumer,而不是 thenCompose,它需要一个 Function

final String url = "https://example.com/some-download.zip";
final CompletionStage<WSResponse> futureResponse = this.client
        .url(url)
        .setMethod("GET")
        .stream();

futureResponse
        .thenAccept(res -> {
            try (OutputStream outputStream = Files.newOutputStream(file.toPath())) {
                downloadTask.setTotalBytes(res);
                Source<ByteString, ?> responseBody = res.getBodyAsSource();

                Sink<ByteString, CompletionStage<akka.Done>> outputWriter =
                        Sink.foreach(bytes -> {
                            downloadTask.addReceivedBytes(bytes.size());
                            System.out.println(downloadTask.getProgressAsString());
                            outputStream.write(bytes.toArray());
                        });
                responseBody.runWith(outputWriter, this.materializer).toCompletableFuture().get();
            } catch (IOException | InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        });

关于java - 使用 CompletableFuture 时如何避免编译器警告关闭 OutputStream?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50045394/

相关文章:

java - 嵌入 Tomcat-7 以仅在 https 中运行

java - JUnit 引用相同的 var 值

java - 将 JPanel 添加到不同类中的另一个 JPanel

scala - 以编程方式为 WSClient 指定证书

java - 如何在 String.replaceAll 中使用 RegEx 在搜索和替换时忽略中间字符

java - 在 Tomcat 6-Java 7/8 中运行 Tomcat 6-Java 6 WebApps

java - 用于单元测试的 Play Framework 2 Java 模拟插件

scala - 使用 Play2/Scala 进行定期 WS 调用以提供 Enumerator 的最佳方法?

java.time : DateTimeParseException for date "20150901023302166"

java - collect(supplier, accumulator, combiner)的组合器的组合顺序在哪里定义?