最近公司有好几个需求都是关于导出统计报表到excel,并发送邮件到对应负责人,纠缠了一番之后,也对导出数据到excel有了一定经验,不至于每做一个导出功能就要写一大堆重复代码。以下是最近几天对easypoi的一些使用经验和心得。
首先是依赖的问题,我们需要添加以下三个easypoi的包,具体的作用也就见名思意了,不做解释。
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-web</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-annotation</artifactId>
<version>4.0.0</version>
</dependency>
在使用之前先来介绍一下相关的easypoi注解:
- @Excel 作用在bean的属性上面,可以定义属性的排列顺序,列宽,行高,格式化等。
- @ExcelTarget 作用在bean上面,可以定义行高,文字的大小,主要是用来针对一个bean做不同的导出逻辑。
- @ExcelEntity 表示可以继续深入导出这个对象的相关字段。
- @ExcelIgnore 看名字就清楚什么意思了
- @ExcelCollection 表示一个集合,导出一对多的关系。
其中上面我使用最多的就是@Excel,接下来就是详细介绍一下这个注解要如何使用,先来看到这个注解里面可以使用属性:
属性 | 类型 | 默认值 | 功能 |
---|---|---|---|
name | String | null | 列名,支持name_id |
needMerge | boolean | fasle | 是否需要纵向合并单元格(用于含有list中,单个的单元格,合并list创建的多个row) |
orderNum | String | "0" | 列的排序,支持name_id |
replace | String[] | {} | 值的替换 导出是{0男,1女} 导入反过来 |
savePath | String | "upload" | 导入文件保存路径 |
type | int | 1 | 导出类型: 1 是文本 2 是图片,3 是函数,10 是数字 默认是文本 |
width | double | 10 | 列宽 |
height | double | 10 | 列高,后期会统一使用@ExcelTarget的height |
isStatistics | boolean | fasle | 自动统计数据,再追加一行统计 |
isHyperlink | boolean | false | 超链接,如果是需要实现接口返回对象 |
isImportField | boolean | true | 校验字段,看看这个字段是不是导入的Excel中有,如果没有说明是错误的Excel,读取失败,支持name_id |
exportFormat | String | "" | 导出的时间格式,以这个是否为空来判断是否需要格式化日期 |
importFormat | String | "" | 导入的时间格式,以这个是否为空来判断是否需要格式化日期 |
format | String | "" | 时间格式,相当于同时设置了exportFormat 和 importFormat |
databaseFormat | String | "yyyyMMddHHmmss" | 导出时间设置,如果字段是Date类型则不需要设置 数据库如果是string 类型,这个需要设置这个数据库格式,用以转换时间格式输出 |
numFormat | String | "" | 数字格式化,参数是Pattern,使用的对象是DecimalFormat |
imageType | int | 1 | 导出类型 1 从file读取 2 是从数据库中读取 默认是文件 同样导入也是一样的 |
suffix | String | "" | 文字后缀,如"¥" 9 变成9¥ |
isWrap | boolean | true | 是否换行 即支持\n |
mergeRely | int[] | {} | 合并单元格依赖关系,比如第二列合并是基于第一列 则{0}就可以了 |
mergeVertical | boolean | fasle | 纵向合并内容相同的单元格 |
fixedIndex | int | -1 | 对应excel的列,忽略名字 |
isColumnHidden | boolean | false | 导出隐藏列 |
其余几个注解暂不做解释,先来进行实操,首先看到我们需要导出数据效果,如图:
然后我们看到对应的对象Bean,如下,这里需要注意第一列和第二列,首先needMerge和mergeVertical都是需要改为true,如果第二列需要在第一列的基础上进行合并就添加mergeRely={0},0就是第一列的意思。
import cn.afterturn.easypoi.excel.annotation.Excel;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@Accessors(chain = true)
public class ExportProductShiftItemDetailDTO implements Serializable {
@Excel(name = "FBOX or SFT",width = 30, orderNum = "0",needMerge = true,mergeVertical = true)
private String productShitItemCode;
@Excel(name = "运输方式",width = 12, orderNum = "1",needMerge = true,mergeVertical = true, mergeRely = {0})
private String shipType;
@Excel(name = "分箱号",width = 20, orderNum = "2")
private String productShiftBoxCode;
@Excel(name = "SKU/POA",width = 12, orderNum = "3",height = 15.0D)
private String productCode;
@Excel(name = "三方条码",width = 20, orderNum = "4")
private String amazonCustomerLabel;
@Excel(name = "店铺",width = 15, orderNum = "5")
private String amazonShop;
@Excel(name = "每箱数量",width = 12, orderNum = "6")
private String quantity;
@Excel(name = "体积",width = 20, orderNum = "7")
private String volume;
@Excel(name = "重量",width = 12, orderNum = "8")
private String weight;
}
然后就是导出数据到excel部分,这里做一下封装,用起来就得心应手,看自己需要,先把数据组装成ExportParams
/**
*@title 表格的顶头标题
*@sheetName 表脚
*@dataList 数据源
*@tClass 数据源的bean模式
*/
public static <T> Map<String, Object> getExcelSheetParam(String title, String sheetName, List<T> dataList, Class<T> tClass) throws SkbException {
Map<String, Object> excelParam = new HashMap<>();
ExportParams params = new ExportParams();
if(StringUtils.isNotBlank(title)){
params.setTitle(title);
}
params.setSheetName(sheetName);
params.setType(ExcelType.XSSF);
excelParam.put("title", params);
excelParam.put("entity", tClass);
excelParam.put("data", dataList);
return excelParam;
}
导出到workbook并输出到response
/**
*
* @param list 数据集
* @param excelType 文件类型ExcelType.HSSF/XSSF
* @return
* @throws SkbException
*/
public static void exportExcel(List<Map<String, Object>> list, ExcelType excelType, String fileName, HttpServletResponse response) throws SkbException{
try {
//设置响应
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName + ".xlsx", "UTF-8"));
response.setContentType("application/vnd.ms-excel");
Workbook workbook = ExcelExportUtil.exportExcel(list, excelType);
if (workbook != null) {
// 打开流
OutputStream outputStream = response.getOutputStream();
list.clear();
workbook.write(outputStream);
// 刷新流
outputStream.flush();
// 关闭流
outputStream.close();
}
}catch (IOException e){
throw new SkbException("导出表格数据失败");
}
}
最后是调用部分
public void exportProductShiftBoxDetail(List<Integer> productShiftItemIdList, HttpServletResponse response) throws SkbException {
List<ExportProductShiftItemDetailDTO> result = productShiftItemMapper.exportProductShiftBoxDetail(productShiftItemIdList);
if(org.apache.commons.collections.CollectionUtils.isNotEmpty(result)){
List<Map<String, Object>> excelParamList = new ArrayList<>();
excelParamList.add(ExportExcelUtil.getExcelSheetParam("调拨装箱信息","sheet1", result, ExportProductShiftItemDetailDTO.class));
ExportExcelUtil.exportExcel(excelParamList, ExcelType.XSSF, "调拨装箱明细", response);
}else {
AssertUtils.success(Boolean.FALSE,"没有要导出的数据");
}
}
打完收工,你们也赶紧去试一下吧。