7.4. 加载和显示图片

我们看看加载、存储和显示员工图片的任务:

  • 员工由 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;
    }
}

同时加载 EmployeeFileDescriptor视图需要包含 FileDescriptor 的所有本地属性:

<view entity="demo_Employee"
      name="employee-edit"
      extends="_local">
    <property name="imageFile"
              view="_local"/>
</view>

Employee 编辑界面 XML 描述的一个片段:

<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 组件显示一张图片,而它的底部从左到右包含上传控件以及下载和清除图片的按钮。因此,界面的这一部分如下所示:

images recipe

现在,现在我们来看看编辑界面控制器

import com.company.sales.entity.Employee;
import com.haulmont.cuba.core.entity.FileDescriptor;
import com.haulmont.cuba.core.global.DataManager;
import com.haulmont.cuba.core.global.FileStorageException;
import com.haulmont.cuba.gui.Notifications;
import com.haulmont.cuba.gui.components.Button;
import com.haulmont.cuba.gui.components.FileDescriptorResource;
import com.haulmont.cuba.gui.components.FileUploadField;
import com.haulmont.cuba.gui.components.Image;
import com.haulmont.cuba.gui.export.ExportDisplay;
import com.haulmont.cuba.gui.export.ExportFormat;
import com.haulmont.cuba.gui.model.InstanceContainer;
import com.haulmont.cuba.gui.screen.*;
import com.haulmont.cuba.gui.upload.FileUploadingAPI;

import javax.inject.Inject;

@UiController("sales_Employee.edit")
@UiDescriptor("employee-edit.xml")
@EditedEntityContainer("employeeDc")
@LoadDataBeforeShow
public class EmployeeEdit extends StandardEditor<Employee> {

    @Inject
    private InstanceContainer<Employee> employeeDc;
    @Inject
    private Image image;
    @Inject
    private FileUploadField uploadField;
    @Inject
    private FileUploadingAPI fileUploadingAPI;
    @Inject
    private DataManager dataManager;
    @Inject
    private Notifications notifications;
    @Inject
    private Button downloadImageBtn;
    @Inject
    private Button clearImageBtn;

    @Subscribe
    protected void onInit(InitEvent event) { (1)
        uploadField.addFileUploadSucceedListener(uploadSucceedEvent -> {
            FileDescriptor fd = uploadField.getFileDescriptor(); (2)
            try {
                fileUploadingAPI.putFileIntoStorage(uploadField.getFileId(), fd);
            } catch (FileStorageException e) {
                throw new RuntimeException("Error saving file to FileStorage", e);
            }
            getEditedEntity().setImageFile(dataManager.commit(fd)); (3)
            displayImage(); (4)
        });

        uploadField.addFileUploadErrorListener(uploadErrorEvent ->
                notifications.create()
                        .withCaption("File upload error")
                        .show());

        employeeDc.addItemPropertyChangeListener(employeeItemPropertyChangeEvent -> { (5)
            if ("imageFile".equals(employeeItemPropertyChangeEvent.getProperty()))
                updateImageButtons(employeeItemPropertyChangeEvent.getValue() != null);
        });
    }

    @Subscribe
    protected void onAfterShow(AfterShowEvent event) { (6)
        displayImage();
        updateImageButtons(getEditedEntity().getImageFile() != null);
    }

    public void onDownloadImageBtnClick() { (7)
        if (getItem().getImageFile() != null)
            exportDisplay.show(getItem().getImageFile(), ExportFormat.OCTET_STREAM);
    }

    public void onClearImageBtnClick() { (8)
        getEditedEntity().setImageFile(null);
        displayImage();
    }

    private void updateImageButtons(boolean enable) {
        downloadImageBtn.setEnabled(enable);
        clearImageBtn.setEnabled(enable);
    }

    private void displayImage() { (9)
        if (getEditedEntity().getImageFile() != null) {
            image.setSource(FileDescriptorResource.class).setFileDescriptor(getEditedEntity().getImageFile());
            image.setVisible(true);
        } else {
            image.setVisible(false);
        }
    }
}
1 onInit() 方法首先初始化用于上传新图片的 uploadField 组件。
2 在成功上传的情况下,从组件获取新的 FileDescriptor 实例,并通过调用 FileUploadingAPI.putFileIntoStorage() 将相应的文件从临时客户端存储发送到 FileStorage
3 之后,通过调用 DataManager.commit()FileDescriptor 保存到数据库中,并将保存的实例分配给 Employee 实体的 imageFile 属性。
4 然后,调用控制器的 displayImage() 方法显示上传的图片。
5 之后,将一个监听器添加到包含 Employee 实例的数据容器中。监听器根据文件加载的状态启用或禁用下载和清除按钮。
6 onAfterShow() 方法根据加载的文件是否存在来决定是否显示图片并且更新按钮状态。
7 点击 downloadImageBtn 按钮时调用 onDownloadImageBtnClick(),使用 ExportDisplay 接口下载文件。
8 点击 clearImageBtn 时调用 onClearImageBtnClick();它只是清除了 Employee 实体的 imageFile 属性。该文件不会从存储中删除。
9 displayImage() 方法从存储中加载文件并设置 image 组件的内容。