Hi,
My goal is to do a stream upload of files in a Vaadin Flow application, i.e. to upload a file to an external storage service without keeping all of the file bytes in the application (memory, temp file, etc). For reference, I’m using Vaadin 25 with Springboot 4.
In this blog post Rethinking uploads and downloads in Vaadin 24.8 - A migration guide, it is mentioned that:
Streaming is currently not supported in Spring Boot applications due to special multipart request handling
I naively implemented an UploadHandler from which I get the UploadEvent. Then the InputStream is consumed chunk by chunk, some validation is done, and the bytes and are send through multipart requests to the external service. Here is the FileStorageUploadHandler:
@RequiredArgsConstructor
@Slf4j
public class FileStorageUploadHandler
extends TransferProgressAwareHandler<UploadEvent, FileStorageUploadHandler>
implements UploadHandler {
private final SerializableConsumer<FileStorageMetadata> successCallback;
@Override
public void handleUploadRequest(UploadEvent uploadEvent) {
try {
FileStorageMetadata fileStorageMetadata =
fileStorageService.store(
new FileContent(uploadEvent.getInputStream(), uploadEvent.getFileName()));
log.info("fileStorageMetadata: {}", fileStorageMetadata);
successCallback.accept(fileStorageMetadata);
} catch (FileStorageException e) {
notifyError(uploadEvent, e);
}
}
private void notifyError(UploadEvent uploadEvent, FileStorageException e) {
TransferContext transferContext = getTransferContext(uploadEvent);
getListeners()
.forEach(
listener -> listener.onError(transferContext, new IOException(e.getDefaultMessage())));
}
@Override
protected TransferContext getTransferContext(UploadEvent transferEvent) {
return new TransferContext(
transferEvent.getRequest(),
transferEvent.getResponse(),
transferEvent.getSession(),
transferEvent.getFileName(),
transferEvent.getOwningElement(),
transferEvent.getFileSize());
}
}
I’m trying to understand the limitation of the Upload component because I was able to upload a valid big file (>100MB). However, when there’s an exception being thrown, e.g. validation error, a retry occurs, perhaps the browser XHR request retry.
As can be seen, if fileStorageService.store(…) fails, then the listener for errors are notified.
Could you provide some insight, specifically how to prevent the fileStorageService call everytime?