在项目B端中,经常会有Excel表格导入导出功能,java中的Apache POI 是一个针对Excel功能非常好的操作库。关于POI网上也有很多的文章,这里主要说一下阿里的EasyExcel实现导表功能;
之前我们项目中的Excel导出功能使用的是poi userModel模式,相信在过去的一段时间中,应该也有不少的项目采用的是这种模式,此种模式的解析速度很快,但是弊端也很大,如果表数据量一旦很大内存就很容易爆掉了。因为userModel的数据解析类似于Dom方式解析,将数据全部加载到内存,然后生成Dom树的过程。
注意:userModel是将所有数据全部加载到内存中进行解析,所以他的解析速度快,内存消耗非常大!但是这也是可能导致OOM(内存溢出)的原因。
导表OOM的解决方案:
1.将数据量减小,比如分批次导出(原来你导的一周的数据分成按天导出,数据量就能小很多)
2.前端js导表
3.阿里EasyExcel
这里着重谈一下阿里EasyExcel工具类:
导表的效果如图:
使用easyExcel首先导入以下依赖:
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.6</version>
</dependency>
这里说明一下:上面表格样式最终的呈现需要在实体类上使用部分注解配合,可以通过下面的注解来设置行高列宽,设置标题名和主标题名。
下面是我在项目中针对导表使用到的工具类(因为项目中只用到导出暂时没有表格导入,所以这里我就没写导入了)
/**
* @Author: LQ
* @Date: 2019/9/25 16:17
*/
public class EasyExcelUtils {
/**
* 导出Excel(一个sheet)
*
* @param response HttpServletResponse
* @param list 数据list
* @param fileName 导出的文件名
* @param sheetName 导入文件的sheet名
* @param clazz 实体类
*/
public static <T> void writeExcel(HttpServletResponse response, List<T> list, String fileName, String sheetName, Class<T> clazz) {
OutputStream outputStream = getOutputStream(response, fileName);
ExcelWriter excelWriter = EasyExcel.write(outputStream, clazz).build();
WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();
excelWriter.write(list, writeSheet);
excelWriter.finish();
}
public static <T> void writeStyleExcel(List<T> list, String fileName, String sheetName, Class<T> clazz) {
// 头的策略
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
// 背景设置为红色
headWriteCellStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
WriteFont headWriteFont = new WriteFont();
headWriteFont.setFontHeightInPoints((short) 10);
headWriteFont.setFontName("宋体");
headWriteCellStyle.setWriteFont(headWriteFont);
// 内容的策略
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
// 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定
contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
// 背景绿色
contentWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
WriteFont contentWriteFont = new WriteFont();
// 字体大小
contentWriteFont.setFontHeightInPoints((short) 10);
contentWriteFont.setFontName("宋体");
contentWriteCellStyle.setWriteFont(contentWriteFont);
// 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
HorizontalCellStyleStrategy horizontalCellStyleStrategy =
new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
// 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcel.write(fileName, clazz).registerWriteHandler(horizontalCellStyleStrategy).sheet(sheetName)
.doWrite(list);
}
/**
* 导出Excel(带样式)
*
* @return
*/
public static <T> void writeStyleExcel(HttpServletResponse response, List<T> list, String fileName, String sheetName, Class<T> clazz) {
// 头的策略
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
// 背景设置为红色
headWriteCellStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
WriteFont headWriteFont = new WriteFont();
headWriteFont.setFontHeightInPoints((short) 10);
headWriteFont.setFontName("宋体");
headWriteCellStyle.setWriteFont(headWriteFont);
// 内容的策略
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
// 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定
contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
// 背景绿色
contentWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
WriteFont contentWriteFont = new WriteFont();
// 字体大小
contentWriteFont.setFontHeightInPoints((short) 10);
contentWriteFont.setFontName("宋体");
contentWriteCellStyle.setWriteFont(contentWriteFont);
// 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
OutputStream outputStream = getOutputStream(response, fileName);
EasyExcel.write(outputStream, clazz).registerWriteHandler(horizontalCellStyleStrategy).sheet(sheetName).doWrite(list);
}
/**
* 导出时生成OutputStream
*/
private static OutputStream getOutputStream(HttpServletResponse response, String fileName) {
//创建本地文件
String filePath = fileName + ".xlsx";
File file = new File(filePath);
try {
if (!file.exists() || file.isDirectory()) {
file.createNewFile();
}
fileName = new String(filePath.getBytes(), "ISO-8859-1");
response.addHeader("Content-Disposition", "filename=" + fileName);
return response.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
除了阿里的easyexcel,在我另一篇文章:https://blog.csdn.net/qq_43655835/article/details/102371136 中也有对excel导入导出的工具类封装,两种封装最终都能实现表格导出,封装方式有差别,可以根据自己的需要来使用。