From 47dc6b96aefb1f153d027362bec8ab4999e9208c Mon Sep 17 00:00:00 2001 From: Joe Di Pol Date: Thu, 21 Mar 2024 17:09:13 -0700 Subject: [PATCH] Update streaming example so it starts and downloads last uploaded file --- examples/webserver/streaming/README.md | 13 ++++++--- .../examples/webserver/streaming/Main.java | 2 +- .../webserver/streaming/StreamingService.java | 27 ++++++++++++------- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/examples/webserver/streaming/README.md b/examples/webserver/streaming/README.md index ca171f6c584..6ef6e9346a4 100644 --- a/examples/webserver/streaming/README.md +++ b/examples/webserver/streaming/README.md @@ -1,9 +1,14 @@ # Streaming Example -This application uses NIO and data buffers to show the implementation of a simple streaming service. - Files can be uploaded and downloaded in a streaming fashion using `Subscriber` and -`Producer`. As a result, service runs in constant space instead of proportional -to the size of the file being uploaded or downloaded. +This application is an example of a very simple streaming service. It leverages the +fact that Helidon uses virtual threads to perform simple input/output stream blocking +operations in the endpoint handlers. As a result, this service runs in constant space instead +of proportional to the size of the file being uploaded or downloaded. + +There are two endpoints: + +- `upload` : uploads a file to the service +- `download` : downloads the previously uploaded file ## Build and run diff --git a/examples/webserver/streaming/src/main/java/io/helidon/examples/webserver/streaming/Main.java b/examples/webserver/streaming/src/main/java/io/helidon/examples/webserver/streaming/Main.java index 068d176669f..81a9c2627b1 100644 --- a/examples/webserver/streaming/src/main/java/io/helidon/examples/webserver/streaming/Main.java +++ b/examples/webserver/streaming/src/main/java/io/helidon/examples/webserver/streaming/Main.java @@ -25,7 +25,7 @@ */ public class Main { - static final String LARGE_FILE_RESOURCE = "/large-file.bin"; + static final String LARGE_FILE_PATH = "target/classes/large-file.bin"; private Main() { } diff --git a/examples/webserver/streaming/src/main/java/io/helidon/examples/webserver/streaming/StreamingService.java b/examples/webserver/streaming/src/main/java/io/helidon/examples/webserver/streaming/StreamingService.java index faac94da8ec..af50f95e858 100644 --- a/examples/webserver/streaming/src/main/java/io/helidon/examples/webserver/streaming/StreamingService.java +++ b/examples/webserver/streaming/src/main/java/io/helidon/examples/webserver/streaming/StreamingService.java @@ -16,6 +16,7 @@ package io.helidon.examples.webserver.streaming; +import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.net.URISyntaxException; @@ -26,6 +27,7 @@ import java.util.Objects; import java.util.logging.Logger; +import io.helidon.http.Status; import io.helidon.webserver.http.HttpRules; import io.helidon.webserver.http.HttpService; import io.helidon.webserver.http.ServerRequest; @@ -38,14 +40,13 @@ public class StreamingService implements HttpService { private static final Logger LOGGER = Logger.getLogger(StreamingService.class.getName()); - private final Path filePath; + // Last file uploaded (or default). Since we don't do any locking + // when operating on the file this example is not safe for concurrent requests. + private volatile File file; StreamingService() { - try { - filePath = Paths.get(Objects.requireNonNull(getClass().getResource(Main.LARGE_FILE_RESOURCE)).toURI()); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } + // Default download file; + file = Paths.get(Main.LARGE_FILE_PATH).toFile(); } @Override @@ -59,22 +60,28 @@ private void upload(ServerRequest request, ServerResponse response) { try { Path tempFilePath = Files.createTempFile("large-file", ".tmp"); Files.copy(request.content().inputStream(), tempFilePath, StandardCopyOption.REPLACE_EXISTING); + file = tempFilePath.toFile(); response.send("File was stored as " + tempFilePath); } catch (IOException e) { throw new UncheckedIOException(e); } - LOGGER.info("Exiting upload ..."); + LOGGER.info("Exiting upload after uploading " + file.length() + " bytes..."); } private void download(ServerRequest request, ServerResponse response) { LOGGER.info("Entering download ..." + Thread.currentThread()); - long length = filePath.toFile().length(); + if (!file.canRead()) { + LOGGER.warning("Can't read file " + file.getAbsolutePath()); + response.status(Status.INTERNAL_SERVER_ERROR_500).send(); + return; + } + long length = file.length(); response.headers().contentLength(length); try { - Files.copy(filePath, response.outputStream()); + Files.copy(file.toPath(), response.outputStream()); } catch (IOException e) { throw new UncheckedIOException(e); } - LOGGER.info("Exiting download ..."); + LOGGER.info("Exiting download after serving " + length + " bytes..."); } }