4.5.2. Загрузка и вывод изображений
Рассмотрим задачу загрузки, хранения и отображения фотографий сотрудников:
-
Сотрудник представлен сущностью
Employee
. -
Файлы изображений хранятся в FileStorage. Сущность
Employee
содержит ссылку на соответствующийFileDescriptor
. -
Экран редактирования
Employee
отображает фотографию, а также дает возможность загрузить, выгрузить и очистить изображение.
Класс сущности со ссылкой на файл изображения:
@Table(name = "SAMPLE_EMPLOYEE")
@Entity(name = "sample$Employee")
public class Employee extends StandardEntity {
...
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "IMAGE_FILE_ID")
protected FileDescriptor imageFile;
public void setImageFile(FileDescriptor imageFile) {
this.imageFile = imageFile;
}
public FileDescriptor getImageFile() {
return imageFile;
}
}
Представление для загрузки Employee
вместе с FileDescriptor
должно содержать все локальные атрибуты FileDescriptor
:
<view class="com.company.sample.entity.Employee"
name="employee-edit">
<property name="name"/>
...
<property name="imageFile"
view="_local">
</property>
</view>
Фрагмент XML-дескриптора экрана редактирования Employee
:
<groupBox caption="Photo" spacing="true"
height="300px" width="300px" expand="image">
<image id="image"
width="100%"
align="MIDDLE_CENTER"
scaleMode="CONTAIN"/>
<hbox align="BOTTOM_LEFT"
spacing="true">
<upload id="uploadField"/>
<button id="downloadImageBtn"
caption="Download"
invoke="onDownloadImageBtnClick"/>
<button id="clearImageBtn"
caption="Clear"
invoke="onClearImageBtnClick"/>
</hbox>
</groupBox>
Компоненты отображения и загрузки/выгрузки фотографии заключены внутрь контейнера groupBox. В верхней его части с помощью компонента image выводится изображение, а в нижней слева направо расположены компонент upload для загрузки файла и кнопки выгрузки и очистки изображения. В результате эта часть экрана должна выглядеть следующим образом:
Теперь рассмотрим контроллер экрана редактирования.
import com.haulmont.cuba.core.entity.FileDescriptor;
import com.haulmont.cuba.core.global.FileStorageException;
import com.haulmont.cuba.gui.components.*;
import com.company.employeeimages.entity.Employee;
import com.haulmont.cuba.gui.data.DataSupplier;
import com.haulmont.cuba.gui.data.Datasource;
import com.haulmont.cuba.gui.export.ExportDisplay;
import com.haulmont.cuba.gui.export.ExportFormat;
import com.haulmont.cuba.gui.upload.FileUploadingAPI;
import javax.inject.Inject;
import java.util.Map;
public class EmployeeEdit extends AbstractEditor<Employee> {
@Inject
private DataSupplier dataSupplier;
@Inject
private FileUploadingAPI fileUploadingAPI;
@Inject
private ExportDisplay exportDisplay;
@Inject
private FileUploadField uploadField;
@Inject
private Button downloadImageBtn;
@Inject
private Button clearImageBtn;
@Inject
private Datasource<Employee> employeeDs;
@Inject
private Image image;
@Override
public void init(Map<String, Object> params) {
uploadField.addFileUploadSucceedListener(event -> {
FileDescriptor fd = uploadField.getFileDescriptor();
try {
fileUploadingAPI.putFileIntoStorage(uploadField.getFileId(), fd);
} catch (FileStorageException e) {
throw new RuntimeException("Error saving file to FileStorage", e);
}
getItem().setImageFile(dataSupplier.commit(fd));
displayImage();
});
uploadField.addFileUploadErrorListener(event ->
showNotification("File upload error", NotificationType.HUMANIZED));
employeeDs.addItemPropertyChangeListener(event -> {
if ("imageFile".equals(event.getProperty()))
updateImageButtons(event.getValue() != null);
});
}
@Override
protected void postInit() {
displayImage();
updateImageButtons(getItem().getImageFile() != null);
}
public void onDownloadImageBtnClick() {
if (getItem().getImageFile() != null)
exportDisplay.show(getItem().getImageFile(), ExportFormat.OCTET_STREAM);
}
public void onClearImageBtnClick() {
getItem().setImageFile(null);
displayImage();
}
private void updateImageButtons(boolean enable) {
downloadImageBtn.setEnabled(enable);
clearImageBtn.setEnabled(enable);
}
private void displayImage() {
if (getItem().getImageFile() != null) {
image.setSource(FileDescriptorResource.class).setFileDescriptor(getItem().getImageFile());
image.setVisible(true);
} else {
image.setVisible(false);
}
}
}
-
В методе
init()
сначала инициализируется компонентuploadField
, предназначенный для загрузки новой фотографии. В случае успешной загрузки из компонента получается экземпляр новогоFileDescriptor
, и соответствующий файл отправляется из временного хранилища в постоянное вызовомFileUploadingAPI.putFileIntoStorage()
. После этогоFileDescriptor
сохраняется в БД вызовом DataSupplier.commit(), и сохраненный экземпляр устанавливается в атрибутеimageFile
редактируемой сущностиEmployee
. Затем вызывается методdisplayImage()
контроллера для отображения загруженной фотографии.Далее в методе
init()
источнику данных, содержащему редактируемый экземплярEmployee
, добавляется слушатель для запрещения или разрешения кнопок выгрузки и очистки файла в зависимости от того, загружен файл или нет. -
Метод
postInit()
вызывает отображение файла и обновляет состояние кнопок в зависимости от наличия загруженного файла. -
Метод
onDownloadImageBtnClick()
вызывается при нажатии кнопкиdownloadImageBtn
и выполняет выгрузку файла с помощью интерфейса ExportDisplay. -
Метод
onClearImageBtnClick()
вызывается при нажатии кнопкиclearImageBtn
и очищает атрибутimageFile
сущностиEmployee
. Удаления файла из хранилища не производится. -
Метод
displayImage()
выгружает файл из хранилища и устанавливает его в качестве содержимого компонентаimage
.