The new UploadHandler API in Vaadin 24.8 improves the file handling for uploading and downloading operations significantly. The former API was buffering the content in temporary files instead of processing the input stream directly for up- and download operations while the new API gives a more natural way of handling the streams.
You can keep using the old API which will not be removed in the Vaadin 24 series. This article shows how the new API simplifies your work by using data streams directly and how easy it is to switch from the previous version.
Previous API
The former API only addressed the upload process, but not the download process. It uses the Receiver
interface with implementing classes like MemoryBuffer
, MultiFileMemoryBuffer
, FileBuffer
and MultiFileBuffer
. The “Buffer” (which implements Receiver
) is initialized directly next to the upload component and stores the data either in RAM or as files in the project directory.
In the upload component, the data can be accessed as a stream within a corresponding listener (e.g., Upload.addSucceededListener
) and processed on the server. For different upload scenarios, you’ll need to implement your own classes based on the storage target and whether you're handling single or multi-file uploads.
New API for Upload
With Vaadin 24.8, we’re introducing a new API that lets you process uploaded data on the fly—more efficiently and without the need to implement core functionality yourself. You now have the UploadHandler
as a functional interface, which processes incoming data in its handleUploadRequest(UploadEvent)
method. You can directly access the details and contents of the uploaded file via the UploadEvent
. The contents of the file is provided as an InputStream
, as you have probably learned to do with e.g. Servlet API.
```
var upload = new Upload(event -> {
//handle upload as InputStream
var is = event.getInputStream();
...
});
```
There are couple of built-in implementations of the UploadHandler
, in case you are for example storing files to your server’s file system:
InMemoryUploadHandler
(stores data in memory as byte arrays)FileUploadHandler
(stores data in the file system)TemporaryFileUploadHandler
(stores data in temporary files)
Unlike with the previous Receiver API, multifile uploads are not related to UploadHandler
. The Upload
component can call the handler multiple times, one for each file. To make your Upload accept only a single file, simply set upload.setMaxFiles(1)
or to enforce a 1MB file size limit set upload.setMaxFileSize(1 * 1024 * 1024)
.
For further information and code examples feel free to take a look into our documentation: Vaadin Upload Documentation.
New API for download
As an equivalent to downloading data, there is now the DownloadHandler
which can be passed to suitable components (e.g., the Anchor Element
). As a developer you can simply generate data or write the content of the file to the OutputStream
via DownloadEvent
. The DownloadEvent
also provides some helper methods to control for example the file name and mime type. The underlying request, response and session are also available for custom needs.
```java
DownloadHandler dh = event -> {
var os = event.getOutputStream();
// prepare data for download and add to stream, typicaly you
// pass it to an external library that writes to contens of
// the file (e.g. PDF generation)
...
};
```
There are also a couple of helpers available, in case you e.g. want to push a file from your jar file or from a local file system to the end users. You should note that basic web servers, servlet containers and e.g. Spring MVC are better optimised for serving static files. These helpers for example don’t support range requests (important for media files) or provide caching, but become handy in certain cases when caching is not needed.
forFile
(files form server file system)forClassResource
(resources from classpath)forServletResource
(resources from servlet context)fromInputStream
(content from any input stream)
For individual download processes, it is also possible to directly implement the DownloadHandler
and implement a custom handler. An example for downloading from an S3 Storage can be found in our documentation.
How to switch from the legacy API – A quick guide
Here's an example view in Vaadin 24.8, still using the old API:
```java
var buffer = new MultiFileMemoryBuffer();
var upload = new Upload(buffer);
upload.addSucceededListener(event -> {
// Practical Example: pass input stream
var contentIS = buffer.getInputStream(event.getFileName());
// Note, that with this old Vaadin API,
// the contents is buffered in memory !!!
processWithExternalLibrary(contentIS);
});
```
A little hint upfront for warning-free compilation: Depending on your IDE configuration, using the former API might trigger warnings in your code. You can suppress these with the annotation @SuppressWarnings("removal")
to continue using the old API. After migrating to the new API the annotation can be removed.
```java
@SuppressWarnings("removal") //remove after migration
@PageTitle("Simplified Legacy Up- & Download Handling")
@Route("file-load-handling/legacy")
@Menu(order = 0, icon = LineAwesomeIconUrl.FILE_CODE)
public class LegacyFileLoadHandlingView extends VerticalLayout {
...
}
```
To migrate, you'll need to remove existing receivers and buffers. Replace them with either a custom UploadHandler
implementation or one of the framework's built-in options. The key distinction between a custom UploadHandler
and the framework's provided ones lies in how they handle UI-locking and the intended nature of the upload process.
If you want to process on the InputStream
directly, you can implement the UploadHandler
interface with the handleUploadRequest(UploadEvent event)
method. As a developer you should note that pretty much all event listeners in Vaadin, the upload- and download handlers are not locking your UI instance. As reading or generating files can often take several seconds – especially with a slower network – locking would pause your whole UI while the upload is being processed. Thus, if you're making UI updates (e.g., component changes or notifications), ensure to access the UI directly via UI.access(() -> {})
to avoid exceptions:
```java
UploadHandler uploadHandler = event -> {
// kepp the UI object for potential updates
var ui = event.getUI();
processWithExternalLibrary(event.getInputStream())
.thenAccept(result -> {
ui.access(() -> {
updateUI(results);
});
});
};
var upload = new Upload(uploadHandler);
```
If your previous Upload
component implemented a SucceededListener
to forward uploaded data as a file or byte array to a service or update the UI, you can use one of the built-it factory methods to lock the UI automatically. For example toTempFile
first streams the contents to disk and then locks the UI and notifies your callback:
```java
var toFileUploadHandler = UploadHandler.toTempFile((metadata, file) -> {
// Practical Example: pass input stream
var fileIS = new FileInputStream(file);
// Note, that UI is now blocked during the file processing
processWithExternalLibrary(fileIS);
// No UI.access neccessary
SuccessNotification.show("File uploaded successfully");
}));
var upload = new Upload(toFileUploadHandler);
```
Notes and known limitations
- Streaming is currently not supported in Spring Boot applications due to special multipart request handling. Consequently, the provided progress handling API does not update during upload but only after it is finished.
- It's essential to manually set
spring.servlet.multipart.max-file-size
andspring.servlet.multipart.max-request-size
properties in Spring Boot project. - As a workaround, you can use
UploadFileHandler
from Viritin for streaming directly from the browser's upload. - Even though you can use the deprecated API, it might be removed in Vaadin 25, so you need to make sure, migrate to the new File Handling.
These limitations will be addressed in upcoming Vaadin releases.
Advantages in a nutshell
- Simplified API
- Single handler with various hooks for each use case
- Flexible custom implementations via lambda expressions
- Process on real data streams possible
Conclusion
With a few straightforward steps, you can integrate the new Upload Handling API into your existing source code. The use of built-in UploadHandlers
ensures efficient and targeted usage without cumbersome implementations.