We’re consider some new framework features to simplify how uploads and downloads are handled. The idea is to use a layered approach that gives direct access to the low-level HTTP request handling mechanism for full control and flexibility and on top of that built-in handler implementations for the most common use cases. The new API would replace Receiver
for uploads and StreamResource
for downloads.
For uploads, we would invert control so that the primary mode of operation is to read bytes from an InputStream
. For downloads, we would give direct control over response headers at the time when the request is handled and provide a listener for download progress and completion.
The full RFC is in a separate document. You can comment directly in that document or in this discussion. As a teaser for what’s in the full document, here’s some imagined usage examples.
Upload to a temp file
new Upload(UploadHandler.toTempFile((File file) -> Notifiation.show("Stored: " + file)));
(It makes no sense to show the server-side file path in a Notification
- the point is just to show that you can update the UI from the callback.)
Upload to a byte[]
and show progress
new Upload(UploadHandler.inMemory((meta, data) -> {
Notifiation.show("Got " + data.length + " bytes for " + meta.getFileName());
}).onProgress(
(transferredBytes, totalBytes) -> Notification.show("Received " + transferredBytes),
32768 // progress interval in bytes
));
Receive a file and count the number of lines directly from the input stream
new Upload(event -> {
int c = countLines(event.getInputStream());
event.getUI().access(() -> Notifiation.show(c + " lines in " + event.getFileName()));
});
Image using a class resource
new Image(DownloadHandler.forClassResource(MyView.class, "logo.png"));
Download a File and show a notification when completed (requires @Push
)
new Anchor(DownloadHandler.forFile(new File("tos.pdf")
.whenComplete(success -> Notification.show("Success: " + succcess)),
"Download terms of service");
Serve a file from the database
String attachmentId = ...;
new Anchor(DownloadHandler.fromStream(event -> {
try(ResultSet row = fetchAttachmentFromDatabase(attachmentId)) {
return new DownloadResponse(row.getBlob("data").getOutputStream(), row.getLong("size"),
row.getString("name"), row.getString("mime"));
} catch (Exception e) {
return DownloadReponse.error(500);
}
}}, "Download attachment");
Stream directly to an OutputStream
new Anchor(event -> {
event.setFileName("random_bytes");
OutputStream out = event.getOutputStream();
while(true) {
int next = ThreadLocalRandom.nextInt(-1, 256);
if (next == -1) break;
out.write(next);
}
}, "Download random bytes");