技术选型
查找资料简单搜索github上的excel项目,找到四个候选者:
- apache poi
- alibaba EasyExcel
- hutool Excel工具集
- 悟耘信息 EasyPoi
apache poi
这几个工具底层都是apache poi。它很强大很灵活,灵活的东西都很复杂,业务不需要这么专业的工具。所以排除
alibaba EasyExcel
这是阿里开源的excel工具,对大数据量优化良好,可以基于注解导入导出,注解功能性不如EasyPoi多。业务数据量不大,所以大可不必。
hutool Excel工具集
hutool的工具做导入导出也很简单,就是不基于注解注解,我不喜欢。
EasyPoi
最终选择EasyPoi,好用的注解,丰富的配置,注解配置即生成下拉,非常契合业务需求。但是也有坑。版本4.3.0.
EasyPoi实战
@Excel注解
文档很详细。
http://doc.wupaas.com/docs/easypoi/easypoi-1c0u96flii98v
@Excel(name = "是否语音" ,orderNum = "140",width = 15 ,replace = {"否_false","是_true"},addressList = true)
private Boolean speech;
下拉
replace = {"判断连续_1","判断次数_2","不做判断_3"},addressList = true
这两个参数就够了,数据库里的1导出为判断连续,判断连续导入为1.
replace里下拉选项可以通过反射动态生成。
replace坑
曾用的3.0.1版本的poi,如果excel不按照replace里面配置的填,导入时方法importExcel直接报错,都不给你数据验证的机会,直接是一个空指针。
好在高版本修复了,如果不按照replace配置的来,此字段给你一个null值,然后就可以自己验证了。
导出
ExportParams是一个配置类,里面有哪些配置可以看看。
ExportParams exportParams = new ExportParams();
exportParams.setTitle(fileName);
exportParams.setSheetName("generalRule_List");
exportParams.setType(ExcelType.XSSF);
exportParams.setStyle(MyExcelStyleUtil.class);
try {
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, BasicRuleTileExcel.class, basicRuleTileExcels);
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Type", "application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
workbook.write(response.getOutputStream());
} catch (Exception e) {
log.error("导出异常:", e);
throw new AlarmException("文件导出异常");
}
导入
ImportParams importParams = new ImportParams();
importParams.setTitleRows(1);
importParams.setHeadRows(1);
// 开启验证
importParams.setNeedVerify(true);
importParams.setVerifyHandler(ruleExcelVerifyHandler);
List<BasicRuleTileExcel> read = Lists.newArrayList();
ExcelImportResult<BasicRuleTileExcel> readMore = new ExcelImportResult<>();
try {
readMore = ExcelImportUtil.importExcelMore(file.getInputStream(), BasicRuleTileExcel.class, importParams);
read = readMore.getList();
} catch (Exception e) {
throw new AlarmException("excel读取错误,请检查下拉框和数据格式合法性");
}
ExcelImportUtil里有importExcelMore和importExcel方法。
importExcel返回的就是importExcelMore里的list,
@Data
public class ExcelImportResult<T> {
/**
* 结果集
*/
private List<T> list;
/**
* 失败数据
*/
private List<T> failList;
/**
* 是否存在校验失败
*/
private boolean verifyFail;
/**
* 数据源
*/
private Workbook workbook;
/**
* 失败的数据源
*/
private Workbook failWorkbook;
private Map<String,Object> map;
public ExcelImportResult() {
}
public ExcelImportResult(List<T> list, boolean verifyFail, Workbook workbook) {
this.list = list;
this.verifyFail = verifyFail;
this.workbook = workbook;
}
}
failList是验证错误的列表。
数据验证
开启验证,导入参数传入验证类。
// 开启验证
importParams.setNeedVerify(true);
importParams.setVerifyHandler(ruleExcelVerifyHandler);
验证类得实现IExcelVerifyHandler
@Slf4j
@Component
public class RuleExcelVerifyHandler implements IExcelVerifyHandler<BasicRuleTileExcel> {
if (obj.getFinishMethod()==2){
if (ObjectUtil.isEmpty(obj.getFinishTerm()) || ObjectUtil.isEmpty(obj.getFinishTermUnit()) || ObjectUtil.isEmpty(obj.getFinishTimes2())){
saveError(obj.getCode(), "累计时长,累计时长单位累计次数缺一不可");
return handlerFail();
}
if (!("s".equals(obj.getFinishTermUnit()) || "m".equals(obj.getFinishTermUnit()) ||"h".equals(obj.getFinishTermUnit()))){
saveError(obj.getCode(), "累计时长单位填写[s,m,h]中的一个");
return handlerFail();
}
}
return handlerSuccess();
}
private ExcelVerifyHandlerResult handlerFail() {
ExcelVerifyHandlerResult result = new ExcelVerifyHandlerResult();
result.setSuccess(false);
result.setMsg("校验不通过");
return result;
}
private ExcelVerifyHandlerResult handlerSuccess() {
ExcelVerifyHandlerResult result = new ExcelVerifyHandlerResult();
result.setSuccess(true);
result.setMsg("通过");
return result;
}
}
obj是excel表的一行记录,每一次验证返回ExcelVerifyHandlerResult对象
@Data
public class ExcelVerifyHandlerResult {
/**
* 是否正确
*/
private boolean success;
/**
* 错误信息
*/
private String msg;
public ExcelVerifyHandlerResult() {
}
public ExcelVerifyHandlerResult(boolean success) {
this.success = success;
}
public ExcelVerifyHandlerResult(boolean success, String msg) {
this.success = success;
this.msg = msg;
}
}
ExcelVerifyHandlerResult对象的success=false,则验证失败,此行数据存入ExcelImportResult.failList.
ExcelVerifyHandlerResult.msg是失败信息。
样式
导出参数设置样式类
exportParams.setStyle(MyExcelStyleUtil.class);
首先,它默认有个样式类ExcelExportStylerDefaultImpl,所以可以仿照它继承AbstractExcelExportStyler,实现IExcelExportStyler。
也可以偷懒直接继承ExcelExportStylerDefaultImpl,修改对应地方。
public class ExcelExportStylerDefaultImpl extends AbstractExcelExportStyler
implements IExcelExportStyler {
public ExcelExportStylerDefaultImpl(Workbook workbook) {
super.createStyles(workbook);
}
@Override
public CellStyle getTitleStyle(short color) {
CellStyle titleStyle = workbook.createCellStyle();
titleStyle.setAlignment(HorizontalAlignment.CENTER);
titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
titleStyle.setWrapText(true);
return titleStyle;
}
@Override
public CellStyle stringSeptailStyle(Workbook workbook, boolean isWarp) {
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setDataFormat(STRING_FORMAT);
if (isWarp) {
style.setWrapText(true);
}
return style;
}
@Override
public CellStyle getHeaderStyle(short color) {
CellStyle titleStyle = workbook.createCellStyle();
Font font = workbook.createFont();
font.setFontHeightInPoints((short) 12);
titleStyle.setFont(font);
titleStyle.setAlignment(HorizontalAlignment.CENTER);
titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
return titleStyle;
}
@Override
public CellStyle stringNoneStyle(Workbook workbook, boolean isWarp) {
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setDataFormat(STRING_FORMAT);
if (isWarp) {
style.setWrapText(true);
}
return style;
}
}
重写这些方法,至于样式代码具体怎么写,就不研究了。我这里只重写了表头样式。
public class MyExcelStyleUtil extends ExcelExportStylerDefaultImpl {
public MyExcelStyleUtil(Workbook workbook) {
super(workbook);
}
@Override
public CellStyle getTitleStyle(short color) {
CellStyle titleStyle = workbook.createCellStyle();
titleStyle.setAlignment(HorizontalAlignment.CENTER);
titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
titleStyle.setWrapText(true);
Font font = this.workbook.createFont();
font.setBold(true);//字体加粗
titleStyle.setFillForegroundColor(IndexedColors.AQUA.getIndex());//背景色
titleStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
return titleStyle;
}
}