Upload
The uploading of files may be handled either with Vaadin Flow using Java, or with Hilla using Lit and React. These are described in the two major sections here.
Handling Uploaded Files in Flow
The Java Flow Upload component provides an API to handle directly uploaded file data, without having to set up an endpoint or a servlet. It uses an UploadHandler
implementation to handle the incoming file data.
UploadHandler
is a functional interface that only requires the handleUploadRequest(UploadEvent)
to be implemented for receiving data or use one of the built-in implementations.
The following built-in implementations of UploadHandler
are available:
-
InMemoryUploadHandler
, stores uploaded files in memory -
FileUploadHandler
, stores uploaded files to the file system -
TemporaryFileUploadHandler
, stores uploaded files to temporary files, typically using the system’s temporary directory. This handler is useful when dealing with large files or when memory usage needs to be minimized.
These are described in the sub-sections that follow.
All of the build-in implementations extend TransferProgressAwareHandler
and support adding progress listeners.
Note
|
The Receiver had implementations for single and multiple file upload.
UploadHandler handles both version so Upload.setMaxFiles(1) needs to be called manually for single-file uploads.
|
Custom UploadHandler Implementations
For more advanced use cases, you can provide custom implementations for UploadHandler
.
You might do this, for example, to upload files to cloud storage or to load data into memory first, validate and save to file.
UploadHandler
is an FunctionalInterface
so it can just be a lambda expression.
Source code
Java
UploadHandler uploadHandler = (event) -> {
try (InputStream inputStream = event.getInputStream()) {
// Process the uploaded file
// ...
}
// Any exception will be caught and fire
// a responseHandled(false, response) method call.
};
The responseHandled
will write the upload status to the response.
By default a success will set it to HttpStatusCode.OK
and any failure will instead set HttpStatusCode.INTERNAL_SERVER_ERROR
.
This can be overridden when implementing the interface.
UploadHandler
can be implemented to override default methods:
Source code
Java
public class CustomUploadHandler implements UploadHandler {
@Override
public void handleUploadRequest(UploadEvent event) {
try (InputStream inputStream = event.getInputStream()) {
// Process the uploaded file
// ...
}
// Any exception will be caught and fire
// a responseHandled(false, response) method call.
}
@Override
public void responseHandled(boolean success, VaadinResponse response) {
// Set your own custom response value for success/fail by overriding this method.
// Default responses are 200 ok for success and 500 internal server error for failure
}
@Override
public long getRequestSizeMax() {
// Return the maximum size, in bytes, of a complete request for multipart upload.
return -1; // -1 means no limit and is the default
}
@Override
public long getFileSizeMax() {
// Return the maximum size, in bytes, of a single file for multipart upload.
return -1; // -1 means no limit and is the default
}
@Override
public long getFileCountMax() {
// Return the maximum amount of files allowed for multipart upload.
return 10000; // -1 means no limit default is 10000
}
}
The responseHandled
method is called after upload has succeeded or an exception has been thrown.
For multipart upload this is called after each part has succeeded.
Maximum values for requestSize, fileSize and fileCount only target multipart uploads that are iterated (multipart uploads where the type is not multipart/form-data
).
-
getRequestSizeMax sets the maximum size for the whole request.
-
getFileSizeMax sets the maximum size for single files in the request.
-
getFileCountMax sets the maximum amount of files in the request.
InMemoryUploadHandler
The InMemoryUploadHandler
stores uploaded files in memory as byte arrays.
The handler is given a successHandler
of type SerializableBiConsumer<UploadMetadata, byte[]> successHandler
.
This successHandler
is called for each successful uploaded file separately.
The successHandler
callback contains UploadMetadata
with information on the uploaded file and a byte[]
that contains the actual data from the upload.
Source code
Java
InMemoryUploadHandler inMemoryHandler = UploadHandler.inMemory(
(metadata, data) -> {
// Get other information about the file.
String fileName = metadata.fileName();
String mimeType = metadata.contentType();
long contentLength = metadata.contentLength();
// Do something with the file data...
// processFile(data, fileName);
});
Upload upload = new Upload(inMemoryHandler);
FileUploadHandler
The FileUploadHandler
stores the upload as a file to the file system.
The handler is given a successHandler
of type SerializableBiConsumer<UploadMetadata, File> successHandler
and a FileFactory
.
This successHandler
is called for each successful uploaded file separately.
The successHandler
callback contains UploadMetadata
with information on the uploaded file and the actual File
where the data was written.
The file used is defined with FileFactory
that gets the filename for the upload and should return the File
to store the data into.
Source code
Java
SerializableBiConsumer<UploadMetadata, File> successHandler = (metadata, file) ->
System.out.printf("File saved to: %s%n", file.getAbsolutePath());
FileFactory fileFactory = (fileName) -> new File("/path/to/uploads", fileName);
FileUploadHandler fileHandler = UploadHandler.toFile(successHandler, fileFactory);
Upload upload = new Upload(fileHandler);
TemporaryFileUploadHandler
The TemporaryFileUploadHandler
works the same as FileUploadHandler
, except that instead of taking in a FileFactory
, it stores the data into a temporary file in the system default temporary-file directory.
The handler is given a successHandler
of type SerializableBiConsumer<UploadMetadata, File> successHandler
.
This successHandler
is called for each successful uploaded file separately.
The successHandler
callback contains UploadMetadata
with information on the uploaded file and the actual File
where the data was written.
Source code
Java
SerializableBiConsumer<UploadMetadata, File> successHandler = (metadata, file) ->
System.out.printf("File saved to: %s%n", file.getAbsolutePath());
TemporaryFileUploadHandler temporaryFileHandler = UploadHandler.toTempFile(successHandler);
Upload upload = new Upload(temporaryFileHandler);
Upload Progress Tracking
The built-in implementations support TransferProgressListeners
which can be added through the fluent API directly to the handler for specific events
Source code
Java
UploadHandler.toTempFile(
(metadata, file) -> System.out.printf("File saved to: %s%n",
file.getAbsolutePath()))
.whenStart(() -> System.out.println("Upload started"))
.onProgress((transferredBytes, totalBytes) -> {
double percentage = (double) transferredBytes / totalBytes * 100;
System.out.printf("Upload progress: %.2f%%\n", percentage);
}).whenComplete((success) -> {
if (success) {
System.out.println("Upload completed successfully");
} else {
System.out.println("Upload failed");
}
});
or giving a TransferProgressListener through the factory methods as a parameter.
Source code
Java
TransferProgressListener progressListener = new TransferProgressListener() {
@Override
public void onStart(TransferContext context) {
Assert.assertEquals(165000, context.contentLength());
Assert.assertEquals("download", context.fileName());
invocations.add("onStart");
}
@Override
public void onProgress(TransferContext context,
long transferredBytes, long totalBytes) {
double percentage = (double) transferredBytes / totalBytes * 100;
System.out.printf("Upload progress: %.2f%%\n", percentage);
}
@Override
public void onComplete(TransferContext context,
long transferredBytes) {
System.out.println("Upload completed successfully");
}
@Override
public void onError(TransferContext context,
IOException reason) {
System.out.println("Upload failed");
}
};
UploadHandler.toTempFile(
(metadata, file) -> System.out.printf("File saved to: %s%n",
file.getAbsolutePath()), progressListener);
To add progress tracking to a custom upload handler, you can extend TransferProgressAwareHandler
:
Source code
Java
public class CustomUploadHandler
extends TransferProgressAwareHandler<UploadEvent, CustomUploadHandler>
implements UploadHandler {
@Override
public void handleUploadRequest(UploadEvent event) {
try (InputStream inputStream = event.getInputStream();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();) {
// Use the TransferUtil.transfer method to copy the data
// to notify progress listeners
TransferUtil.transfer(
inputStream,
outputStream,
getTransferContext(event),
getListeners());
// Process the data
byte[] data = outputStream.toByteArray();
// ...
} catch (IOException e) {
// Notify listeners of the error
notifyError(event, e);
throw new UncheckedIOException(e);
}
}
@Override
protected TransferContext getTransferContext(UploadEvent event) {
return new TransferContext(
event.getRequest(),
event.getResponse(),
event.getSession(),
event.getFileName(),
event.getOwningElement(),
event.getFileSize());
}
}
With this you can add the fluent methods to add handling for specific progress events.
Source code
Java
CustomUploadHandler uploadHandler = new CustomUploadHandler()
.whenStart(() -> System.out.println("Upload started"))
.onProgress((transferredBytes, totalBytes) -> {
double percentage = (double) transferredBytes / totalBytes * 100;
System.out.printf("Upload progress: %.2f%%\n", percentage);
})
.whenComplete((success) -> {
if (success) {
System.out.println("Upload completed successfully");
} else {
System.out.println("Upload failed");
}
});
Handling Upload Requests in Lit and React
When using the Upload web component standalone, you’ll need an upload file handler or endpoint in your backend to handle the file upload request. By default, the Upload component sends a request with the method type POST
, the content type multipart/form-data
, and the request URL (i.e., the current browser location).
Use the target
attribute to specify a different URL that should handle the upload request. It’s also possible to customize other aspects of the request, such as the method or request headers.
Source code
jsx
<Upload
method="PUT"
target="/api/upload-handler"
headers='{ "X-API-KEY": "7f4306cb-bb25-4064-9475-1254c4eff6e5" }'>
</Upload>
jsx
HTML
HTML
EB618652-4822-49DC-9A51-D71237EF100E