Загрузка и скачивание файлов
Иногда пользователю нужно передать файл: картинку, документ, таблицу. В Vaadin за загрузку отвечает компонент Upload, а за скачивание — обычная ссылка Anchor. В обе стороны данные идут через обработчики (handlers): UploadHandler получает файл от браузера, DownloadHandler отдаёт его обратно. Этот API появился в Vaadin 24.8 и в Vaadin 25 стал основным; старые MemoryBuffer и StreamResource теперь помечены устаревшими (deprecated).
Добавим статьям обложку: в форме её можно загрузить, а в окне просмотра — увидеть и скачать.
Обложка в сущности
// src/main/java/com/example/blog/Post.java (новые поля)
@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 — байты картинки, coverType — её тип, например image/png. @Lob (импорт jakarta.persistence.Lob) говорит Hibernate, что значение большое и для него нужна колонка типа BLOB.
Загрузка
В конструктор PostFormView добавьте компонент Upload:
// src/main/java/com/example/blog/views/PostFormView.java (фрагмент конструктора)
Upload upload = new Upload(UploadHandler.inMemory((metadata, data) -> {
post.setCover(data);
post.setCoverType(metadata.contentType());
Notification.show("Обложка загружена: " + metadata.fileName());
}));
upload.setAcceptedMimeTypes("image/png", "image/jpeg");
upload.setMaxFileSize(1024 * 1024);
upload.setMaxFiles(1);
add(form, upload, save);Импорты: com.vaadin.flow.component.upload.Upload и com.vaadin.flow.server.streams.UploadHandler. Под формой появится кнопка «Upload File...» и надпись «Drop file here» (в единственном числе — из-за setMaxFiles(1)). Нажмите кнопку и выберите PNG-картинку или перетащите её на эту область — под кнопкой появится имя файла, а внизу уведомление «Обложка загружена: cover.png». Нажмите «Сохранить» — картинка запишется в базу вместе со статьёй.
UploadHandler.inMemory читает файл в массив байтов и вызывает лямбду, когда загрузка закончена. Лямбда выполняется с блокировкой интерфейса, поэтому в ней можно сразу менять компоненты. metadata — сведения о файле: fileName(), contentType(), contentLength().
| Обработчик | Куда попадает файл |
|---|---|
UploadHandler.inMemory(...) | В массив байтов; для небольших файлов |
UploadHandler.toTempFile(...) | Во временный файл на диске |
UploadHandler.toFile(...) | В файл, который вы указали |
setAcceptedMimeTypes и setMaxFileSize проверяются ещё в браузере: другой тип или файл больше мегабайта будет отклонён с сообщением в списке.
Показ и скачивание
DownloadHandler отдаёт браузеру данные по запросу. Напишем метод, который делает такой обработчик для обложки:
// src/main/java/com/example/blog/views/PostListView.java (новый метод)
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 получает поток с данными, имя файла (cover.png или cover.jpeg), тип и размер. Импорты — DownloadHandler и DownloadResponse из com.vaadin.flow.server.streams и java.io.ByteArrayInputStream. Теперь используем обработчик в окне просмотра из урока про диалоги:
// src/main/java/com/example/blog/views/PostListView.java (фрагмент метода preview)
if (post.getCover() != null) {
Image cover = new Image(coverHandler(post), "Обложка");
cover.setMaxWidth("100%");
dialog.add(cover, new Anchor(coverHandler(post), AttachmentType.DOWNLOAD, "Скачать обложку"));
}Нажмите «глаз» у статьи с обложкой: в окне появится картинка, а под ней ссылка «Скачать обложку». Клик по ссылке сохраняет файл cover.png: сервер отвечает с заголовком Content-Disposition: attachment; filename="cover.png". С AttachmentType.INLINE браузер открыл бы картинку во вкладке. Классы Image, Anchor и AttachmentType — из com.vaadin.flow.component.html.
Для файлов с диска есть готовые DownloadHandler.forFile(file) и DownloadHandler.forClassResource(...).
Частая ошибка
Поле cover объявили без @Lob. Приложение запускается, загрузка проходит, но при сохранении статьи с картинкой больше 255 байт появляется ошибка:
org.springframework.dao.DataIntegrityViolationException:
Value too long for column "COVER BINARY VARYING(255)"Без @Lob Hibernate создаёт для массива байтов короткую колонку на 255 байт — любая настоящая картинка в неё не помещается. Добавьте @Lob и перезапустите приложение, чтобы таблица создалась заново.