3.5.2.1.17. 文件上传控件

FileUploadField 允许用户上传文件到服务器。这个控件包含标题 、 已上传文件的链接 、 还有两个按钮:上传按钮和清除文件选择按钮。当点击上传按钮的时候,会弹出系统标准的文件选择器,用户可以在这里选择需要上传的文件。如果是要上传多个文件,可以用 FileMultiUploadField 控件。

gui upload 7.0

该控件对应的 XML 名称:upload

对于 FileDescriptor 类型的实体属性,可以在 FieldGroup 内用 datasource 属性使用此控件,也可以在 Form 中通过 dataContainer 属性使用,或者也能单独使用。如果此控件绑定到任何数据组件,上传的文件会被立即保存到文件存储,对应的 FileDescriptor 实例会保存到数据库。

<upload fileStoragePutMode="IMMEDIATE"
        dataContainer="personDc"
        property="photo"/>

还可以通过编程的方式控制文件和 FileDescriptor 的保存:

  • 在界面的 XML 描述中声明这个控件:

    <upload id="uploadField"
            fileStoragePutMode="MANUAL"/>
  • 在界面控制器中,需要注入该控件本身,还需要注入 FileUploadingAPIDataManager 这两个接口。然后,订阅上传成功或出错的事件:

    @Inject
    private FileUploadField uploadField;
    @Inject
    private FileUploadingAPI fileUploadingAPI;
    @Inject
    private DataManager dataManager;
    @Inject
    private Notifications notifications;
    
    @Subscribe("uploadField")
    public void onUploadFieldFileUploadSucceed(FileUploadField.FileUploadSucceedEvent event) {
        File file = fileUploadingAPI.getFile(uploadField.getFileId()); (1)
        if (file != null) {
            notifications.create()
                    .withCaption("File is uploaded to temporary storage at " + file.getAbsolutePath())
                    .show();
        }
    
        FileDescriptor fd = uploadField.getFileDescriptor(); (2)
        try {
            fileUploadingAPI.putFileIntoStorage(uploadField.getFileId(), fd); (3)
        } catch (FileStorageException e) {
            throw new RuntimeException("Error saving file to FileStorage", e);
        }
        dataManager.commit(fd); (4)
        notifications.create()
                .withCaption("Uploaded file: " + uploadField.getFileName())
                .show();
    }
    
    @Subscribe("uploadField")
    public void onUploadFieldFileUploadError(UploadField.FileUploadErrorEvent event) {
        notifications.create()
                .withCaption("File upload error")
                .show();
    }
1 此时如果需要的话,可以取到保存在临时存储的文件。
2 一般来说,文件需要保存到中间件的文件存储中。
3 将文件保存至 FileStorage。
4 将文件描述器保存到数据库。

该控件将所有选择的文件上传到客户端层 的临时存储(temporary storage)并且调用 FileUploadSucceedEvent 监听器。在这个监听器里面,从 uploadField 获取了一个 FileDescriptorcom.haulmont.cuba.core.entity.FileDescriptor (别跟 java.io.FileDescriptor 混淆了) 是一个持久化实体,唯一定义一个上传的文件,并且也用这个类从系统下载文件。

FileUploadingAPI.putFileIntoStorage() 方法把文件从客户端层的临时存储移动到文件存储。这个方法的参数是临时存储中文件的标识符和对应的 FileDescriptor 对象,这两个参数都是由 FileUploadField 提供的。

当上传文件到 FileStorage 完成后,通过调用 DataManager.commit()FileDescriptor 实例存到数据库。这个方法会返回保存的实体,可以用来赋值给其它实体里关联这个文件的属性。这里只是简单的把 FileDescriptor 存到了数据库。从 Administration > External Files 界面可以看到这个文件。

FileUploadErrorEvent 监听器会在从客户端上传文件到临时存储出错的时候被调用。

以下是可以订阅的事件,用来跟踪上传进度:

  • AfterValueClearEvent

  • BeforeValueClearEvent

  • FileUploadErrorEvent

  • FileUploadFinishEvent

  • FileUploadStartEvent

  • FileUploadSucceedEvent

  • ValueChangeEvent

fileUploadField 的属性:

  • fileStoragePutMode - 定义文件和相应的 FileDescriptor 怎么存储。

    • IMMEDIATE 模式,在文件存到客户端层临时存储之后立即存储。

    • MANUAL 模式, 需要手动在 FileUploadSucceedListener 里面编码实现。

      当在 FieldGroup 里面使用 FileUploadField 的时候,默认模式是 IMMEDIATE,其它情况下,默认模式是 MANUAL

  • uploadButtonCaptionuploadButtonIconuploadButtonDescription 这三个 XML 属性可以设置上传按钮的属性。

  • showFileName - 控制上传文件的名称是否要显示在上传按钮旁边,默认是 false 不显示。

  • showClearButton - 控制是否要显示清空按钮,默认 false 不显示。

  • clearButtonCaptionclearButtonIconclearButtonDescription 这三个 XML 属性可以设置清空按钮的属性。

  • accept XML 属性 (或者相应的 setAccept() 方法) 用来设置文件选择对话框里面的文件类型掩码,但是用户还是可以选择“所有文件”来上传任意文件。

    这个属性的值需要是以英文逗号分隔的文件扩展名,比如: *.jpg,*.png

  • 最大可上传的文件大小是由 cuba.maxUploadSizeMb 应用程序属性定义的,默认是 20MB。如果用户选择了更大的文件的话,会有相应的提示信息,并且中断上传过程。

  • fileSizeLimit XML 属性 (或者相应的 setFileSizeLimit() 方法) 用来设置最大允许上传的文件大小,以字节为单位。

    <upload id="uploadField" fileSizeLimit="2000"/>
  • permittedExtensions XML 属性 (或者相应的 setPermittedExtensions() 方法) 设置允许的文件扩展名白名单。

    这个属性的值需要是字符串的集合,其中每个字符串是以 . 开头的允许的文件扩展名,比如:

    uploadField.setPermittedExtensions(Sets.newHashSet(".png", ".jpg"));
  • dropZone - 允许设置一个特殊的 BoxLayout 用来作为从浏览器外部拖拽文件可以放置的目标容器区域。这个目标区域可以覆盖整个对话框的窗口。当文件被拖拽到这块区域的时候,这个容器会被高亮显示,否则目标区域不会显示。

    <layout spacing="true"
            width="100%">
        <vbox id="dropZone"
              height="AUTO"
              spacing="true">
            <textField id="textField"
                       caption="Title"
                       width="100%"/>
            <textArea id="textArea"
                      caption="Description"
                      width="100%"
                      rows="5"/>
            <checkBox caption="Is reference document"
                      width="100%"/>
            <upload id="upload"
                    dropZone="dropZone"
                    showClearButton="true"
                    showFileName="true"/>
        </vbox>
        <hbox spacing="true">
            <button caption="mainMsg://actions.Apply"/>
            <button caption="mainMsg://actions.Cancel"/>
        </hbox>
    </layout>
    gui dropZone

    如果想要 dropZone 不变并且一直显示,需要给这个容器设置预定义的样式名称 dropzone-container。此时这个容器应该是空的,只包含一个 label 组件:

    <layout spacing="true"
            width="100%">
        <textField id="textField"
                   caption="Title"
                   width="100%"/>
        <checkBox caption="Is reference document"
                  width="100%"/>
        <upload id="upload"
                dropZone="dropZone"
                showClearButton="true"
                showFileName="true"/>
        <vbox id="dropZone"
              height="150px"
              spacing="true"
              stylename="dropzone-container">
            <label stylename="dropzone-description"
                   value="Drop file here"
                   align="MIDDLE_CENTER"/>
        </vbox>
        <hbox spacing="true">
            <button caption="mainMsg://actions.Apply"/>
            <button caption="mainMsg://actions.Cancel"/>
        </hbox>
    </layout>
    gui dropZone static
  • pasteZone 允许设置一个特殊的容器用来处理粘贴(paste)的快捷键。此时需要这个容器内部的一个文字输入控件获得焦点(focused)。这个功能只支持基于 Chromium 的浏览器。

    <upload id="uploadField"
            pasteZone="vboxId"
            showClearButton="true"
            showFileName="true"/>

参考 在 CUBA 应用程序中使用图片 指南,了解上传文件的一些更加复杂的示例。