RFC: Upload and download handlers

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());
}, TransferProgressListener.of(
    success -> Notification.show("Success: " + succcess),
    (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"),
  TransferProgressListener.of(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");
3 Likes

Would this allow us to implement scenario’s mentioned here Efficiently serving video files in Java web apps with HTTP range requests | Vaadin, by giving direct access to the request/response ?

Yes, that should be doable. You would still have to implement the Range support manually just as with the current workaround of serving through a separate Servlet. But you would get a little bit of convenience from the built-in security checks and URL handling.

1 Like