Использование хранилища файлов
Хранилище файлов – это абстракция, обеспечивающая различные реализации того, как и где хранятся файлы, и предоставляющая единый интерфейс для доступа к файлам и создания ссылок на них из сущностей модели данных.
Jmix поставляется с двумя реализациями хранения файлов: локальной и AWS. При создании нового проекта в Studio в него включена локальная реализация.
В файловом хранилище можно хранить файлы любого размера, поскольку передача файлов в хранилище и из него выполняется путем копирования небольших фрагментов данных между входными и выходными потоками, поэтому файлы никогда не загружаются в память полностью. |
Примеры
Работа с файлами в UI
В этом разделе приводится пример работы с файлами в файловом хранилище с использованием компонентов UI.
Во-первых, создайте в сущности атрибут типа FileRef
, например:
@JmixEntity
@Entity
@Table(name = "ATTACHMENT")
public class Attachment {
// ...
@Column(name = "FILE_")
private FileRef file;
public FileRef getFile() {
return file;
}
public void setFile(FileRef file) {
this.file = file;
}
При запуске приложения Studio генерирует скрипт миграции базы данных для создания соответствующего столбца строкового типа, поскольку FileRef
имеет строковое представление в формате URI.
Для загрузки файлов с экрана UI используйте компонент FileStorageUploadField, привязанный к атрибуту сущности:
<data>
<instance id="attachmentDc"
class="files.ex1.entity.Attachment">
<fetchPlan extends="_base"/>
<loader/>
</instance>
</data>
<layout spacing="true">
<form dataContainer="attachmentDc">
<column>
<fileStorageUpload id="fileField" property="file"/>
Чтобы скачать прикрепленные файлы, добавьте в таблицу на экране браузера специальный столбец:
<groupTable id="attachmentsTable"
width="100%"
dataContainer="attachmentsDc">
<columns>
<column id="fileName" caption="File"/>
И определите генератор столбца:
@Autowired
private UiComponents uiComponents;
@Autowired
private Downloader downloader; (1)
@Install(to = "attachmentsTable.fileName", subject = "columnGenerator")
private Component attachmentsTableFileColumnGenerator(Attachment attachment) {
if (attachment.getFile() != null) {
LinkButton linkButton = uiComponents.create(LinkButton.class);
linkButton.setAction(new BaseAction("download")
.withCaption(attachment.getFile().getFileName())
.withHandler(actionPerformedEvent ->
downloader.download(attachment.getFile()) (2)
)
);
return linkButton;
} else {
return new Table.PlainTextCell("<empty>");
}
}
1 | Используйте бин Downloader для скачивания файлов. |
2 | Метод download() принимает значение FileRef и извлекает файл из хранилища файлов, указанного в объекте FileRef . Имя и тип файла также закодированы в FileRef , поэтому веб-браузер правильно выбирает, загружать или отображать файл. |
Использование интерфейса FileStorage
В следующем примере показано, как работать напрямую с интерфейсом FileStorage.
Первый метод сохраняет в файловом хранилище полученный из веб-службы файл. Второй метод загружает файл из хранилища и сохраняет его в локальной файловой системе.
Используется та же сущность Attachment
, что и в предыдущем примере.
@Autowired
private FileStorageLocator fileStorageLocator; (1)
@Autowired
private DataManager dataManager;
private void getAndSaveImage() {
try {
(2)
URLConnection connection = new URL("https://picsum.photos/300").openConnection();
try (InputStream responseStream = connection.getInputStream()) {
(3)
FileStorage fileStorage = fileStorageLocator.getDefault();
FileRef fileRef = fileStorage.saveStream("photo.jpg", responseStream);
(4)
Attachment attachment = dataManager.create(Attachment.class);
attachment.setFile(fileRef);
dataManager.save(attachment);
}
} catch (IOException e) {
throw new RuntimeException("Error getting image", e);
}
}
private void saveToLocalFile(Attachment attachment, Path path) {
FileStorage fileStorage = fileStorageLocator.getDefault();
FileRef fileRef = attachment.getFile();
(5)
InputStream inputStream = fileStorage.openStream(fileRef);
try {
(6)
Files.copy(inputStream, path.resolve(fileRef.getFileName()),
StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
throw new RuntimeException("Error saving image", e);
}
}
1 | FileStorageLocator позволяет работать с определенным хранилищем файлов, если вы определили в проекте несколько хранилищ. Если у вас одно хранилище файлов (что является ситуацией по умолчанию), интерфейс FileStorage можно внедрить напрямую. |
2 | Получение входного потока для веб-ресурса. Вместо класса URLConnection можно использовать HttpClient , представленный в Java 11, или стороннюю библиотеку, такую как Apache HttpClient. |
3 | Сохранение содержимого ресурса в хранилище файлов. Возвращаемый объект FileRef является ссылкой на файл в хранилище. |
4 | Сохранение ссылки на атрибут сущности. |
5 | Получение входного потока для загрузки файла из хранилища файлов. |
6 | Сохранение файла в локальной файловой системе. |
Локальное хранилище файлов
Реализация локального хранилища позволяет хранить файлы в локальной файловой системе сервера приложения или в любом сетевом хранилище (NAS).
Чтобы использовать локальное хранилище файлов в своем приложении, убедитесь, что файл build.gradle
содержит следующую строку в разделе dependencies
:
implementation 'io.jmix.localfs:jmix-localfs-starter'
Файлы хранятся в специальной структуре каталогов, которая поддерживается файловым хранилищем. По умолчанию корневым каталогом является ${user.dir}/.jmix/work/filestorage
, где ${user.dir}
является рабочим каталогом пользователя (где была запущена JVM). Его можно изменить, указав рабочий каталог в свойстве приложения jmix.core.work-dir, или нужный путь целиком в свойстве jmix.localfs.storage-dir
, например:
jmix.localfs.storage-dir = /opt/file-storage
Хранилище файлов AWS
Реализация хранилища файлов AWS позволяет хранить файлы в Amazon S3.
Чтобы использовать в приложении хранилище файлов AWS, установите дополнение AWS File Storage из каталога, как описано в разделе Дополнения, или вручную добавьте следующую строку в раздел dependencies
файла build.gradle
:
implementation 'io.jmix.awsfs:jmix-awsfs-starter'
Если вы планируете использовать только хранилище файлов AWS, удалите зависимость локального хранилища файлов из build.gradle
(строку, содержащую io.jmix.localfs:jmix-localfs-starter
). В противном случае см. следующий раздел о том, как настроить несколько хранилищ файлов.
Определите свойства приложения:
jmix.awsfs.access-key = <access key ID>
jmix.awsfs.secret-access-key = <secret access key>
jmix.awsfs.region = <AWS region, for example eu-north-1>
jmix.awsfs.bucket = <S3 bucket name>
jmix.awsfs.chunk-size = <optional upload chunk size in KB, default is 8192>
jmix.awsfs.endpoint-url = <optional endpoint URL for S3-compatible cloud storages>
The S3 bucket in the selected region must be created beforehand.
Использование нескольких хранилищ
Если вы хотите использовать в приложении несколько хранилищ файлов, укажите имя хранилища по умолчанию в свойстве приложения:
jmix.core.default-file-storage = fs
Имя локального хранилища файлов – fs
; имя хранилища файлов AWS – s3
.
Чтобы использовать несколько хранилищ одного типа, определите дополнительные хранилища с уникальными именами. Например, чтобы определить дополнительное локальное хранилище файлов с корневым каталогом в /var/tmp/myfs
, добавьте следующий код в основной класс приложения:
@Bean
FileStorage myFileStorage() {
return new LocalFileStorage("myfs", "/var/tmp/myfs");
}
Для программной работы с различными хранилищами используйте бин FileStorageLocator
. Он позволяет получить файловое хранилище по его имени.
Компонент FileStorageUploadField имеет атрибут fileStorage
для указания имени хранилища файлов. Если он не установлен, компонент использует файловое хранилище по умолчанию.