源码地址:https://github.com/alibaba/easyexcel
升级版本 升级版本 升级版本
依赖
<!--阿里easyExcel工具包-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.0.2</version>
</dependency>
对应工具类
/**
* @author yy
* @version 2.0
* @date 2019/7/30 16:27
* @deprecated 版本升级为对应的2.0.0以上
* 性能更加高效 导出数据更加稳定
* 支持 64M内存1分钟内读取75M(46W行25列)
**/
@Component
public class ExcelUtil {
/**
* 导出 Excel :一个 sheet,带表头.
*
* @param response HttpServletResponse
* @param list 数据 list,每个元素为一个 BaseRowModel
* @param fileName 导出的文件名
* @param sheetName 导入文件的 sheet 名
* @param model 映射实体类,Excel 模型
* @throws Exception 异常
*/
public void writeExcel(HttpServletResponse response, List<? extends Object> data,
String fileName, String sheetName, Class model) throws Exception {
// 头的策略
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
//设置表头居中对齐
headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
// 内容的策略
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
//设置内容靠左对齐
contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);
// 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
HorizontalCellStyleStrategy horizontalCellStyleStrategy =
new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcel.write(getOutputStream(fileName, response), model)
.excelType(ExcelTypeEnum.XLSX)
.sheet(sheetName)
.registerWriteHandler(horizontalCellStyleStrategy)
//最大长度自适应 目前没有对应算法优化 建议注释掉不用 会出bug
// .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.doWrite(data);
}
/**
* 导出文件时为Writer生成OutputStream.
*
* @param fileName 文件名
* @param response response
* @return ""
*/
private OutputStream getOutputStream(String fileName,
HttpServletResponse response) throws Exception {
try {
fileName = URLEncoder.encode(fileName, "UTF-8");
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf8");
response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".xlsx");
response.setHeader("Pragma", "public");
response.setHeader("Cache-Control", "no-store");
response.addHeader("Cache-Control", "max-age=0");
return response.getOutputStream();
} catch (IOException e) {
throw new Exception("导出excel表格失败!", e);
}
}
/**
* 简单的读
* @param fileName
* @param clazz
* @return
*/
public List<?> simpleRead(String fileName, Class clazz) {
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
EasyExcel.read(fileName, clazz, new DataListener()).sheet().doRead();
return new DataListener().saveData();
}
***由于本身listener类没有归spring管理 所以无法获取容器内部的bean实例***
class DataListener extends AnalysisEventListener<User> {
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private final int BATCH_COUNT = 10;
private List<User> list = new ArrayList<>();
public List<User> getList() {
return list;
}
/**
* 这个每一条数据解析都会来调用
*
* @param data
* one row value. It is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(User data, AnalysisContext context) {
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
list.add(data);
// 达到BATCH_COUNT了,就需要对list进行清空,防止数据几万条数据在内存,容易OOM
if (list.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
list.clear();
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
// saveData();
LOGGER.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*/
private List<?> saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size());
return list;
}
}
实体映射Excelproperty 对应导出Excel表头
@Data
@ColumnWidth(22)
@ContentRowHeight(15) //由于新版本并没有对单元格设置默认值
所以需要手动对单元格进行赋值
public class User extends BaseRowModel implements Serializable {
/**
* value: 单表头如下 如果多表头 则 @ExcelProperty(value = {"姓名1","姓名2","姓名3"}, index = 0)
* index: 列的号, 0表示第一列
* @ExcelIgnore 生成Excel忽略那个字段
*/
/**
* 姓名
*/
@ExcelProperty(value = "姓名", index = 0)
private String name;
/**
* 年龄
*/
@ExcelProperty(value = "年龄", index = 1)
private String age;
/**
* 性别
*/
@ExcelProperty(value = "性别", index = 2)
private String sex;
}
测试
@Autowired
private ExcelUtil excelUtil;
public void testWright() {
List<User> list = new HashList<>();
for (int i = 0; i < 10000; i++) {
User user = new User();
user.setName(UUID.randomUUID().toString());
user.setAge("10");
user.setSex("男");
list.add(user);
}
excelUtil.writeExcel(response, list, "test", "testsheet", user.getClass());
}
@Autowired
private ExcelUtil excelUtil;
public void testread(){
User user = new User();
List<User> list = excelUtil.simpleRead("报表路径",user.getClass());
//打印每一个读取的对象
list.forEach(System.out::println);
}
目前读取报表的监听器需要每次在读取报表时重新new 所以不可以给spring容器管理 只能使用构造方法自己实例化 导致了无法获取容器里面的bean实例