一、简介
EasyPoi 功能如同名字 easy,主打的功能就是容易,让一个没见接触过poi的人员就可以方便的写出 Excel 导出,Excel 模板导出,Excel 导入,Word 模板导出,通过简单的注解和模板语言(熟悉的表达式语法),完成以前复杂的写法
如果想了解 JeecgBoot 的 Autopoi,可以参考原作者的另一篇博客,SpringBoot 中使用 JeecgBoot 的 Autopoi 导出 Excel
https://blog.csdn.net/qq_40065776/article/details/107824221
二、引入 EasyPoi
EasyPoi 在 SpringBoot 中也是做了很好的封装,让我们能够在 SpringBoot 快速地使用 EasyPoi 进行开发
<!-- easypoi -->
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-spring-boot-starter</artifactId>
<version>4.2.0</version>
</dependency>
我们只需要引入这一个依赖即可,这是对 SpringBoot 做了很好的支持
三、源码解读
1、@Excel 源码解读
通过查阅源码,我们不难从 cn.afterturn.easypoi.excel.annotation.Excel 注解中发现
/**
* Copyright 2013-2015 JueYue (qrb.jueyue@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.afterturn.easypoi.excel.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Excel 导出基本注释
* @author JueYue
* 2014年6月20日 下午10:25:12
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Excel {
/**
* 导出时,对应数据库的字段 主要是用户区分每个字段, 不能有annocation重名的 导出时的列名
* 导出排序跟定义了annotation的字段的顺序有关 可以使用a_id,b_id来确实是否使用
*/
public String name();
/**
* 展示到第几个可以使用a_id,b_id来确定不同排序
*/
public String orderNum() default "0";
/**
* 值得替换 导出是{a_id,b_id} 导入反过来,所以只用写一个
*/
public String[] replace() default {};
/**
* 字典名称
*/
public String dict() default "";
}
以上是 @Excel 注解的代码片段,我们可以看出该注解中支持两种字典替换方式
- 1、replace,该方式支持直接写入注解参数中,如:
@Excel(name = "性别", width = 15, replace = "男_1,女_2")
@TableField("sex")
private Integer sex;
我们用 1 表示 男性,2 表示 女性,这样我们在导出的时候,就能够自动替换掉数据中的魔法值,但是这样我们往往要在注解参数中写入过多的代码,而且我们的字典往往是动态变化的,这样的局限性太大
- 2、dict,字典方式,传入字典参数中,如:
@Excel(name = "性别", width = 15, dict = "sex")
@TableField("sex")
private Integer sex;
这里我们只传入的字典的 key,这样我们在查询出数据的时候,写入 Excel 文件时,在进行动态替换,即可替换掉数据中的魔法值,增加数据的可读性
2、IExcelDictHandler 源码解读
上一步,我们已经知道了在 EasyPoi 中是支持自定义字典查询导出的,那么我们该如何实现它呢?通过阅读 cn.afterturn.easypoi.handler.inter.IExcelDictHandler 接口中的代码,代码如下:
package cn.afterturn.easypoi.handler.inter;
import java.util.List;
import java.util.Map;
/**
* @author jueyue on 18-2-2.
* @version 3.0.4
*/
public interface IExcelDictHandler {
/**
* 返回字典所有值
* key: dictKey
* value: dictValue
* @param dict 字典Key
* @return
*/
default public List<Map> getList(String dict) {
return null;
}
/**
* 从值翻译到名称
*
* @param dict 字典Key
* @param obj 对象
* @param name 属性名称
* @param value 属性值
* @return
*/
public String toName(String dict, Object obj, String name, Object value);
/**
* 从名称翻译到值
*
* @param dict 字典Key
* @param obj 对象
* @param name 属性名称
* @param value 属性值
* @return
*/
public String toValue(String dict, Object obj, String name, Object value);
}
接口中提供了三个方法:
-
1、getList,通过字典 key 查询该 key 下的所有字典数据,例如:sex 下的 {“1”:“男”, “2”:“女”}
-
2、toName,字典的翻译功能,从值翻译到名称,例如:sex: 1 --> “男”,一般导出的时候使用
-
3、toValue,与 toName 相反,从名称翻译到值,例如:sex: “男” --> 1,一般导入的时候使用
既然我们知道在 EasyPoi 中提供了字典翻译的接口,那我们只需要提供一个实现类,重写接口中的方法即可,IExcelDictHandlerImpl.java 实现 IExcelDictHandler 接口,代码如下:
package com.zyxx.common.excel;
import cn.afterturn.easypoi.handler.inter.IExcelDictHandler;
import com.zyxx.sys.service.SysDictDetailService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
* 支持字典参数设置
* 举例: @Excel(name = "性别", width = 15, dicCode = "sex")
* 1、导出的时候会根据字典配置,把值1,2翻译成:男、女;
* 2、导入的时候,会把男、女翻译成1,2存进数据库;
* * @Author lizhou
*/
@Slf4j
@Component
public class IExcelDictHandlerImpl implements IExcelDictHandler {
@Autowired
private SysDictDetailMapper testSysDictDetailMapper;
private static SysDictDetailMapper sysDictDetailMapper;
@PostConstruct
public void init() {
sysDictDetailMapper = this.testSysDictDetailMapper;
}
/**
* 从值翻译到名称
*
* @param dict 字典Key
* @param obj 对象
* @param name 属性名称
* @param value 属性值
* @return
*/
@Override
public String toName(String dict, Object obj, String name, Object value) {
return sysDictDetailMapper.getTextByDictAndValue(dict, String.valueOf(value));
}
/**
* 从名称翻译到值
*
* @param dict 字典Key
* @param obj 对象
* @param name 属性名称
* @param value 属性值
* @return
*/
@Override
public String toValue(String dict, Object obj, String name, Object value) {
return null;
}
}
-
1、这里我们导出,只使用了 toName(从值翻译到名称)这个方法,所以,只写了一个方法
-
2、我们需要使用 @Component 注解将它加载到 Spring 容器中
-
3、@PostConstruct 该注解被用来修饰一个非静态的 void() 方法。被 @PostConstruct
修饰的方法会在服务器加载 Servlet 的时候运行,并且只会被服务器执行一次.PostConstruct
在构造函数之后执行,init() 方法之前执行
四、开始导出
1、定义实体类
package com.zyxx.sys.entity;
import cn.afterturn.easypoi.excel.annotation.Excel;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.zyxx.common.annotation.Dict;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* <p>
* 用户信息表
* </p>
* * @author lizhou
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("sys_user_info")
@ApiModel(value = "SysUserInfo对象", description = "用户信息表")
public class SysUserInfo extends Model<SysUserInfo> {
@ApiModelProperty(value = "ID")
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@Excel(name = "姓名", width = 15)
@ApiModelProperty(value = "姓名")
@TableField("name")
private String name;
@Excel(name = "电话", width = 15)
@ApiModelProperty(value = "电话")
@TableField("phone")
private String phone;
@Excel(name = "性别", width = 15, dict = "sex")
@TableField("sex")
@Dict(dictCode = "user_sex")
private Integer sex;
@Excel(name = "状态", width = 15, dict = "status")
@TableField("status")
private Integer status;
}
@Excel 注解解释如下:
- name,表头名称
- width,列宽
- dict,字典 key
2、导出 API 接口
controller 层提供导出 API
@ApiOperation(value = "导出用户信息", notes = "导出用户信息")
@GetMapping(value = "/export")
public void exportXls(HttpServletResponse response) {
// 查询数据
List<SysUserInfo> list = sysUserInfoService.list(1, Integer.MAX_VALUE);
// 导出数据,数据,数据类型,文件名称,表名,响应对象
ExportExcelUtil.exportExcel(list, SysUserInfo.class, "用户信息表", "用户信息统计", response);
}
3、导出工具类
/**
* 导出excel
*
* @param list 数据集合
* @param pojoClass 数据类型
* @param fileName 文件名称
* @param title 表明
* @param response 响应对象
*/
public static void exportExcel(List<?> list, Class<?> pojoClass, String fileName, String title, HttpServletResponse response) {
ExportParams exportParams = new ExportParams(title, null);
// 自定义字典查询规则
exportParams.setDictHandler(new IExcelDictHandlerImpl());
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list);
if (workbook != null) {
try {
response.setCharacterEncoding("UTF-8");
response.setHeader("content-Type", "application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xls");
workbook.write(response.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
}
exportParams.setDictHandler(new IExcelDictHandlerImpl());,我们传入了自定义的字典查询规则
五、测试导出
我们调取导出数据的 API 接口,即可导出文件,导出效果如下:
六、总结
可以看出,自定义字典查询导出方式,其实和 JeecgBoot 的 Autopoi 方式都大同小异,后面是发现了 JeecgBoot 的 Autopoi 和 hutool 的读取文件 ExcelReader 有冲突,放弃了 JeecgBoot 的 Autopoi,EasyPoi 确实是一款强大的 Excel 操作产品!!!
如您在阅读中发现不足,欢迎留言!!!