Skip to content

Upload handler #21165

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
mshabarov opened this issue Mar 20, 2025 · 1 comment
Open

Upload handler #21165

mshabarov opened this issue Mar 20, 2025 · 1 comment

Comments

@mshabarov
Copy link
Contributor

mshabarov commented Mar 20, 2025

Describe your motivation

Using an InputStream for in API for uploads is preferable as this is more intuitive when your server-side code reads client data from incoming data stream, better aligned with the Servlet API and makes various use cases simpler, e.g. writing BLOB to database or cloud file storage.

Describe the solution you'd like

Receiver interface is deprecated as well as Upload constructors using it. We introduce a new UploadHandler concept as a replacement:

@FunctionalInterface
public interface UploadHandler {
  void handleUploadRequest(UploadEvent event);
}

This is a layer on top of the generic element-scoped request handler with helpers to give easy access to file name, size, MIME type and an input stream with the actual data (without having to worry about any multipart shenanigans).

Just like with Receiver, the framework should do the regular security checks and in particular also verify the security key from the upload URL before invoking the handler. The session is not locked while the handler is run. The handler may lock the session if necessary but it's not recommended to hold the lock while reading data from the request.

All the magic is in the event object. It gives direct access to the underlying request, response and session as well as various helpers specifically for handling uploads. In case of a multipart request, the upload handler is invoked separately for each appropriate part.

// Shown as an interface to focus on the API. The actual type should probably be a class.
public interface UploadEvent {
  String getFileName();
  String getContentType();
  long getFileSize();

  InputStream getInputStream();

  // The component to which the upload handler is bound
  Component getComponent();

  // The UI that getComponent() is attached to, provided for use with UI:access. The Upload component should automatically register a dummy finish listener if push is not enabled to make sure any pending UI updates are delivered after uploading.
  UI getUI();

  // Request and response are mostly made available for checking headers and cookies. It's not recommend to directly read from the request or write to the response.
  VaadinRequest getRequest();
  VaadinResponse getResponse();
  VaadinSession getSession();
}

In addition to the callback method, the UploadHandler interface also defines factory methods for creating an upload handler instance for various common use cases. The success callbacks in those handlers are run through UI::access so that application code can update the UI directly.

public interface UploadHandler {
  static UploadHandler toFile(BiConsumer<UploadMetadata, File> successHandler, FileFactory fileFactory);
  static UploadHandler toTempFile(BiConsumer<UploadMetadata, File> successHandler);
  static UploadHandler inMemory(BiConsumer<UploadMetadata, byte[]> successHandler);
}

FileFactory is moved to Flow from Flow-components.

@mshabarov
Copy link
Contributor Author

Suggestion to split this into smaller parts:

  • single upload with XHR
  • multipart upload (one or even more sub-tickets).

@mshabarov mshabarov changed the title [Draft] Upload handler Upload handler Mar 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: 🪵Product backlog
Development

No branches or pull requests

1 participant