4.5.2. Loading and Displaying Images
Let’s consider a task of loading, storing and displaying employee photos:
-
An employee is represented by
Employee
entity. -
Image files are stored in the FileStorage. The
Employee
entity contains a link to the correspondingFileDescriptor
. -
The
Employee
edit screen shows the picture and also supports uploading, downloading and clearing the picture.
Entity class with a link to the image file:
@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;
}
}
A view for loading an Employee
together with FileDescriptor
should include all local attributes of FileDescriptor
:
<view class="com.company.sample.entity.Employee"
name="employee-edit">
<property name="name"/>
...
<property name="imageFile"
view="_local">
</property>
</view>
A fragment of the Employee
edit screen XML descriptor:
<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>
Components used to display, upload and download images are contained within the groupBox container. Its top part shows a picture using the image component, while its bottom part from left to right contains the upload component and buttons to download and clear the image. As a result, this part of the screen should look like this:
Now, let us have a look at the edit screen controller.
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);
}
}
}
-
The
init()
method first initializes theuploadField
component that is used for uploading new images. In the case of a successful upload, a newFileDescriptor
instance is retrieved from the component and the corresponding files are sent from the temporary client storage toFileStorage
by invokingFileUploadingAPI.putFileIntoStorage()
. After that, theFileDescriptor
is saved to the database by invoking DataSupplier.commit(), and the saved instance is assigned to theimageFile
attribute of the editedEmployee
entity. Then, the controller’sdisplayImage()
method is invoked to display the uploaded image.After that, a listener is added in the
init()
method to the datasource containing anEmployee
instance. The listener enables or disables download and clear buttons, depending on the fact whether the file has been loaded or not. -
postInit()
method performs file display and refreshes the button states, depending on the existence of a loaded file. -
onDownloadImageBtnClick()
is invoked when thedownloadImageBtn
button is clicked; it downloads the file using the ExportDisplay interface. -
onClearImageBtnClick()
is invoked when theclearImageBtn
is clicked; it clears theimageFile
attribute of theEmployee
entity. The file is not deleted from storage. -
displayImage()
loads the file from storage and sets the content of theimage
component.