Skip to content

Commit

Permalink
Update streaming example so it starts and downloads last uploaded file
Browse files Browse the repository at this point in the history
  • Loading branch information
barchetta committed Mar 22, 2024
1 parent be27e30 commit 47dc6b9
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 15 deletions.
13 changes: 9 additions & 4 deletions examples/webserver/streaming/README.md
Original file line number Diff line number Diff line change
@@ -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<DataChunk>` and
`Producer<DataChunk>`. 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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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
Expand All @@ -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...");
}
}

0 comments on commit 47dc6b9

Please sign in to comment.