5.8.6. Loading and Displaying Images
Let’s consider a task of loading, storing and displaying employee photos:
-  An employee is represented by Employeeentity.
-  Image files are stored in the FileStorage. The Employeeentity contains a link to the correspondingFileDescriptor.
-  The Employeeedit 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="250px" width="250px" expand="embeddedImage">
    <embedded id="embeddedImage" width="100%"
              align="MIDDLE_CENTER"/>
    <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 embedded 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.
public class EmployeeEdit extends AbstractEditor<Employee> {
    private Logger log = LoggerFactory.getLogger(EmployeeEdit.class);
    @Inject
    private DataSupplier dataSupplier;
    @Inject
    private FileStorageService fileStorageService;
    @Inject
    private FileUploadingAPI fileUploadingAPI;
    @Inject
    private ExportDisplay exportDisplay;
    @Inject
    private Embedded embeddedImage;
    @Inject
    private FileUploadField uploadField;
    @Inject
    private Button downloadImageBtn;
    @Inject
    private Button clearImageBtn;
    @Inject
    private Datasource<Employee> employeeDs;
    private static final int IMG_HEIGHT = 190;
    private static final int IMG_WIDTH = 220;
    @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() {
        byte[] bytes = null;
        if (getItem().getImageFile() != null) {
            try {
                bytes = fileStorageService.loadFile(getItem().getImageFile());
            } catch (FileStorageException e) {
                log.error("Unable to load image file", e);
                showNotification("Unable to load image file", NotificationType.HUMANIZED);
            }
        }
        if (bytes != null) {
            embeddedImage.setSource(getItem().getImageFile().getName(), new ByteArrayInputStream(bytes));
            embeddedImage.setType(Embedded.Type.IMAGE);
            BufferedImage image;
            try {
                image = ImageIO.read(new ByteArrayInputStream(bytes));
                int width = image.getWidth();
                int height = image.getHeight();
                if (((double) height / (double) width) > ((double) IMG_HEIGHT / (double) IMG_WIDTH)) {
                    embeddedImage.setHeight(String.valueOf(IMG_HEIGHT));
                    embeddedImage.setWidth(String.valueOf(width * IMG_HEIGHT / height));
                } else {
                    embeddedImage.setWidth(String.valueOf(IMG_WIDTH));
                    embeddedImage.setHeight(String.valueOf(height * IMG_WIDTH / width));
                }
            } catch (IOException e) {
                log.error("Unable to resize image", e);
            }
            // refresh image
            embeddedImage.setVisible(false);
            embeddedImage.setVisible(true);
        } else {
            embeddedImage.setVisible(false);
        }
    }
}-  The init()method first initializes theuploadFieldcomponent that is used for uploading new images. In the case of a successful upload, a newFileDescriptorinstance is retrieved from the component and the corresponding files are sent from the temporary client storage toFileStorageby invokingFileUploadingAPI.putFileIntoStorage(). After that, theFileDescriptoris saved to the database by invoking DataSupplier.commit(), and the saved instance is assigned to theimageFileattribute of the editedEmployeeentity. 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 anEmployeeinstance. 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 thedownloadImageBtnbutton is clicked; it downloads the file using the ExportDisplay interface.
-  onClearImageBtnClick()is invoked when theclearImageBtnis clicked; it clears theimageFileattribute of theEmployeeentity. The file is not deleted from storage.
-  displayImage()loads the file from storage into a byte array, sets the content of theembeddedImagecomponent and recalculates its size to preserve image height to width ratio.It should be noted that loading files into byte arrays is acceptable for small files only. If the file size is not predictable, you should only download it using ExportDisplay, which sends the file through input and output streams and never keeps the entire file in memory.