Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Uploading and downloading files

Sometimes the user needs to hand over a file: an image, a document, a spreadsheet. In Vaadin, uploading is done by the Upload component, and downloading by an ordinary Anchor link. In both directions data goes through handlers: an UploadHandler receives a file from the browser, and a DownloadHandler sends it back. This API arrived in Vaadin 24.8 and is the main one in Vaadin 25; the old MemoryBuffer and StreamResource are now deprecated.

Let us give posts a cover image: it can be uploaded in the form, and viewed and downloaded in the preview window.

The cover in the entity

// src/main/java/com/example/blog/Post.java (new fields)
@Lob
private byte[] cover;
 
private String coverType;
 
public byte[] getCover() { return cover; }
public void setCover(byte[] cover) { this.cover = cover; }
public String getCoverType() { return coverType; }
public void setCoverType(String coverType) { this.coverType = coverType; }

cover holds the image bytes, and coverType its type, for example image/png. @Lob (import jakarta.persistence.Lob) tells Hibernate that the value is large and needs a BLOB column.

Uploading

Add an Upload component to the PostFormView constructor:

// src/main/java/com/example/blog/views/PostFormView.java (part of the constructor)
Upload upload = new Upload(UploadHandler.inMemory((metadata, data) -> {
    post.setCover(data);
    post.setCoverType(metadata.contentType());
    Notification.show("Cover uploaded: " + metadata.fileName());
}));
upload.setAcceptedMimeTypes("image/png", "image/jpeg");
upload.setMaxFileSize(1024 * 1024);
upload.setMaxFiles(1);
 
add(form, upload, save);

Imports: com.vaadin.flow.component.upload.Upload and com.vaadin.flow.server.streams.UploadHandler. Below the form, an "Upload File..." button and a "Drop file here" label appear (singular because of setMaxFiles(1)). Click the button and choose a PNG image, or drop it on this area: the file name appears under the button, and "Cover uploaded: cover.png" at the bottom. Click "Save" and the image is written to the database together with the post.

UploadHandler.inMemory reads the file into a byte array and calls the lambda when the upload is finished. The lambda runs with the UI locked, so it can change components directly. metadata describes the file: fileName(), contentType(), contentLength().

HandlerWhere the file goes
UploadHandler.inMemory(...)Into a byte array; for small files
UploadHandler.toTempFile(...)Into a temporary file on disk
UploadHandler.toFile(...)Into a file you choose

setAcceptedMimeTypes and setMaxFileSize are checked already in the browser: another type or a file over a megabyte is rejected with a message in the list.

Showing and downloading

A DownloadHandler sends data to the browser on request. Let us write a method that creates such a handler for the cover:

// src/main/java/com/example/blog/views/PostListView.java (new method)
private DownloadHandler coverHandler(Post post) {
    return DownloadHandler.fromInputStream(event -> new DownloadResponse(
            new ByteArrayInputStream(post.getCover()),
            post.getCoverType().replace("image/", "cover."),
            post.getCoverType(),
            post.getCover().length));
}

DownloadResponse takes a stream with the data, a file name (cover.png or cover.jpeg), the type and the size. Imports: DownloadHandler and DownloadResponse from com.vaadin.flow.server.streams, and java.io.ByteArrayInputStream. Now use the handler in the preview window from the dialogs lesson:

// src/main/java/com/example/blog/views/PostListView.java (part of the preview method)
if (post.getCover() != null) {
    Image cover = new Image(coverHandler(post), "Cover");
    cover.setMaxWidth("100%");
    dialog.add(cover, new Anchor(coverHandler(post), AttachmentType.DOWNLOAD, "Download cover"));
}

Click the "eye" next to a post with a cover: the window shows the image with a "Download cover" link under it. Clicking the link saves the file cover.png: the server answers with the header Content-Disposition: attachment; filename="cover.png". With AttachmentType.INLINE the browser would open the image in a tab instead. Image, Anchor and AttachmentType come from com.vaadin.flow.component.html.

For files on disk there are ready-made DownloadHandler.forFile(file) and DownloadHandler.forClassResource(...).

Common mistake

The cover field was declared without @Lob. The application starts and the upload works, but saving a post with an image larger than 255 bytes fails:

org.springframework.dao.DataIntegrityViolationException:
Value too long for column "COVER BINARY VARYING(255)"

Without @Lob, Hibernate creates a short 255-byte column for a byte array, and no real image fits in it. Add @Lob and restart the application so that the table is created again.

Thanks for reading!

Donate to the site — we work hard for you.