Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Загрузка и скачивание файлов

Иногда пользователю нужно передать файл: картинку, документ, таблицу. В 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 и перезапустите приложение, чтобы таблица создалась заново.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.