# diboot-file 使用说明

# 1、引入依赖

Maven


<dependency>
    <groupId>com.diboot</groupId>
    <artifactId>diboot-file-spring-boot-starter</artifactId>
    <version>{latestVersion}</version>
</dependency>
1
2
3
4
5
6

或Gradle:

compile("com.diboot:diboot-file-spring-boot-starter:{latestVersion}")
1

file组件依赖一张表 upload_file ,用于存储文件/附件上传记录。该表将由diboot-file-starter初次加载时自动初始化。

如果使用diboot-devtools,还需要引入devtools相关依赖,可一键生成相关的上传下载的controller。

# 2、参数配置:

diboot-file组件有以下配置项,用于初始化及设置本地文件的存储起始目录(子目录会按分类与日期自动创建) 配置参数:

# 是否初始化sql,默认true,初始化之后(或非开发环境)可以关闭
diboot.component.file.init-sql=false
# 文件的本地存储路径
diboot.component.file.storage-directory=/myfile
# 上传大小
spring.servlet.multipart.max-request-size=10M
1
2
3
4
5
6

# 3. 使用说明

# 3.1 EasyExcel的增强优化

  • 支持基于Java validator注解的自动数据校验:
@NotNull(message = "用户名不能为空") // 支持validation注解自动校验
@ExcelProperty(value = "状态")
private String username;
1
2
3
  • 支持@ExcelBindDict注解自动转换字典 显示值->存储值
@ExcelBindDict(type = "USER_STATUS")   // 自动转换数据字典 label->value
@ExcelProperty(value = "状态")
private String userStatus;
1
2
3
  • 支持@ExcelBindField注解自动转换关联字段 name->id
@ExcelBindField(entity = Department.class, field = "name", setIdField = "parentId")
@ExcelProperty(value = "上级部门")
private String parentName;

// setIdField="parentId",将转换后的id值设置到parentId
@ExcelIgnore
private Long parentId;
1
2
3
4
5
6
7
  • 支持@ExcelOption注解Excel写入单元格验证 — 下拉选项since v2.3.0
@ExcelOption(dict = "USER_STATUS")   // Excel写入单元格下拉选项(关联字典)
@ExcelBindDict(type = "USER_STATUS")   // 自动转换数据字典 label->value
@ExcelProperty(value = "状态")
private String userStatus;
1
2
3
4

注:同时也支持自定义选项;关联字典优先级大于自定义选项

  • 支持@ExcelComment表头批注 (since v2.4.0
@ExcelComment("该列的批注说明")
@ExcelProperty(value = "状态")
private String userStatus;
1
2
3
  • 轻量封装增强的Excel Data Listener 继承后只需要实现自定义校验 additionalValidate() 和 保存数据的 saveData() 方法。

    注:.csv文件默认读取 UTF8 格式的文件,其它格式可能导致读取不到数据

    • 动态表头的excel读取 DynamicHeadExcelListenersince start

    • 已知表头的excel读取 ReadExcelListenersince v2.4.0

      主要特色

      1. 数据转换异常修补,避免数据丢失
      2. 异常数据导出,以及异常批注提示

      ExcelModel的处理基类,读取的Model需继承BaseExcelModel,以便于 修补、批注

      • 固定头读取FixedHeadExcelListenersince start

        since v2.4.0 继承了 ReadExcelListener

      • 分页读取PageReadExcelListenersince v2.4.0

        其采用分批处理模式,解决了大数据量导入时内存占用过高问题

  • Excel写入处理还提供一些常用封装

    以下简单介绍一下其使用,需要时根据实际使用情况自行指定处理程序

    • Sheet页密码写入 LockedWriteHandler,用于导出数据的列锁定
      // 此处以ID列为例,当需要锁定ID列不可被更改时,
      // 1. 需指定ContentStyle的locked=true,同时也需要在ExcelModel上指定ContentStyle的locked=false(因为默认锁定所有列)
      @ContentStyle(locked = BooleanEnum.FALSE)
      public class EntityExportModel {
      
          @ExcelProperty("ID")
          @ContentStyle(locked = BooleanEnum.TRUE)
          private Long id;
      }
      // 2. 指定锁密码写入程序
      // web 导出
      ExcelHelper.exportExcel(response, fileName, EntityExportModel.class, dataList, new LockedWriteHandler());
      // 写入文件
      ExcelHelper.write(outputStream, fileName, EntityExportModel.class, dataList, new LockedWriteHandler());
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14

      注:LockedWriteHandler的默认密码为当天日期(yyyyMMdd),也可在构造中指定密码;当指定密码为空字符串时,会加锁,但无密码

    • 固定表头 FreezePaneWriteHandler
      // 动态固定表头(自动识别表头行数进行固定)
      new FreezePaneWriteHandler()
      // 指定固定列行(以下是固定两列三行)
      new FreezePaneWriteHandler(2, 3);
      
      // web 导出
      ExcelHelper.exportExcel(response, fileName, EntityExportModel.class, dataList, new FreezePaneWriteHandler());
      // 写入文件
      ExcelHelper.write(outputStream, fileName, EntityExportModel.class, dataList, new FreezePaneWriteHandler());
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
    • 单元格验证OptionWriteHandler(下拉选项)、批注写入CommentWriteHandler;都已默认填加
  • 优化汇总校验错误的提示内容 校验失败提示示例:

{
  "msg": "数据校验不通过: 第 5 行: XXX",
  "code": 4005
}
1
2
3
4

# 3.2 常用文件处理封装

  • 提供BaseFileController用于文件上传下载的Controller继承 使用示例:
    //上传文件
@PostMapping("/upload")
public JsonResult upload(@RequestParam("file") MultipartFile file)throws Exception{
    // 保存文件并创建UploadFile上传记录
    return super.uploadFile(file, Dictionary.class);
}

// 下载文件
@GetMapping("/download/{fileUuid}")
public JsonResult download(@PathVariable("fileUuid")String fileUuid,HttpServletResponse response)throws Exception{
    UploadFile uploadFile=uploadFileService.getEntity(fileUuid);
    if(uploadFile==null){
        return JsonResult.FAIL_VALIDATION("文件不存在");
    }
    // 下载
    HttpHelper.downloadLocalFile(uploadFile.getStoragePath(), uploadFile.getFileName(), response);
    return null;
}

// 自定义文件存储
@Override
protected<T> UploadFile saveFile(MultipartFile file,Class<T> entityClass)throws Exception{
    // 自定义文件存储,默认本地存储
    return super.saveFile(file ,entityClass);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  • 提供BaseExcelFileController用于Excel导入导出类的Controller继承 使用示例:
// 预览excel数据
@PostMapping("/preview")
public JsonResult preview(@RequestParam("file") MultipartFile file)throws Exception{
    return super.excelPreview(file, Entity.class);
}

// 预览无校验错误后 提交
@PostMapping("/previewSave")
public<T> JsonResult previewSave(@RequestParam("uuid") String uuid)throws Exception{
    return super.excelPreviewSave(uuid);
}

// 无预览 直接导入
@PostMapping("/upload")
public<T> JsonResult upload(@RequestParam("file") MultipartFile file)throws Exception{
    return super.uploadExcelFile(file, Entity.class);
}

// 获取表头,用于动态导出列 (since v2.4.0)
@GetMapping("/tableHead")
public JsonResult tableHead() {
    return JsonResult.OK(ExcelHelper.getTableHead(EntityExportModel.class));
}

// 导出数据 【案例一:普通导出;优势:耗时短,缺点:数据量大时耗内存】
@GetMapping("/export")
public JsonResult export(EntityDTO queryDto, @RequestParam(value = "columns", required = false) List<String> columns,
        HttpServletResponse response) throws Exception{
    QueryWrapper<Entity> queryWrapper = super.buildQueryWrapper(queryDto);
    List<EntityVO> voList = entityService.getViewObjectList(queryWrapper, null, EntityVO.class);
    if (V.isEmpty(voList)) {
            return new JsonResult(Status.FAIL_OPERATION, "数据列表为空,导出失败");
    }
    String fileName = "数据列表导出_" + D.today() + ".xlsx";
    List<EntityExportModel> dataList = this.entityList2ExcelList(voList);
    ExcelHelper.exportExcel(response, fileName, EntityExportModel.class, columns, dataList);
    return null;
}

// 导出数据 【案例二:分页导出;优势:大数据量导出时节省内存,缺点:多次查询耗时】(since v2.4.0)
@GetMapping("/export")
public JsonResult export(EntityDTO queryDto, @RequestParam(value = "columns", required = false) List<String> columns,
                         HttpServletResponse response) throws Exception{
    QueryWrapper<Entity> queryWrapper = super.buildQueryWrapper(queryDto);
    if (entityService.getEntityListCount(queryWrapper) == 0) {
        return new JsonResult(Status.FAIL_OPERATION, "数据列表为空,导出失败");
    }
    String fileName = "数据列表导出_" + D.today() + ".xlsx";
    // 创建分页
    Pagination pagination = new Pagination();
    pagination.setPageSize(BaseConfig.getBatchSize());
    ExcelHelper.exportExcel(response, fileName, EntityExportModel.class, columns, () -> {
        List<EntityVO> voList = entityService.getViewObjectList(queryWrapper, pagination, EntityVO.class);
        pagination.setPageIndex(pagination.getPageIndex() + 1);
        return this.entityList2ExcelList(voList);
    });
    return null;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

注:动态导出列前端参考 export组件

# 3.3 其他常用文件处理相关工具类

// 保存上传文件至本地
FileHelper.saveFile(MultipartFile file, String fileName)

// 下载本地文件
HttpHelper.downloadLocalFile(String localFilePath, String exportFileName, HttpServletResponse response)

// 下载网络文件至本地
HttpHelper.downloadHttpFile(String fileUrl, String targetFilePath)

// 图片保存,压缩,加水印等 (需依赖Thumbnails组件)
ImageHelper.saveImage(MultipartFile file, String imgName)
ImageHelper.generateThumbnail(String sourcePath, String targetPath, int width, int height)
ImageHelper.addWatermark(String filePath, String watermark)

// zip压缩
ZipHelper.zipFile(String srcRootDir, File file, ZipOutputStream zos, String... matchKeyword)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 4 样例参考 - diboot-file-example (opens new window)

使用过程中遇到问题,可加群交流。