更多学习资料,可以关注下方微信公众号,回复关键字:Java高级资料
即可免费获取完整的Java高级开发工程师视频资料。
一丶简介
EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。
二、传统解析的弊端
Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到几M,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便。
三、快速感受
excel内容
读Excel
简单的读取excel文件
*/
@Test
public void read() {
String fileName = "demo.xlsx";
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
}
写Excel
简单的写入数据到excel
*/
@Test
public void simpleWrite() {
String fileName = "demo.xlsx";
EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
}
Web上传与下载
excel文件的下载
*/
@GetMapping("download")
public void download(HttpServletResponse response) throws IOException {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename=demo.xlsx");
EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data());
}
/**
excel文件的上传
*/
@PostMapping("upload")
@ResponseBody
public String upload(MultipartFile file) throws IOException {
EasyExcel.read(file.getInputStream(), DemoData.class, new DemoDataListener()).sheet().doRead();
return "success";
}
四、详解读取Excel
简单读取
对象
public class DemoData {
private String string;
private Date date;
private Double doubleData;
}
监听器
public class DemoDataListener extends AnalysisEventListener<DemoData> {
List<DemoData> list = new ArrayList<DemoData>();
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*/
public DemoDataListener() {}
* 这个每一条数据解析都会来调用
*
* @param data
* @param context
*/
@Override
public void invoke(DemoData data, AnalysisContext context) {
System.out.println("解析到一条数据:{}", JSON.toJSONString(data));
list.add(data);
}
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
System.out.println(JSON.toJSONString(list));
}
}
代码
@Test
public void simpleRead() {
String fileName = "demo.xlsx";
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
fileName = "demo.xlsx";
ExcelReader excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build();
ReadSheet readSheet = EasyExcel.readSheet(0).build();
excelReader.read(readSheet);
excelReader.finish();
}
指定列的下标或名称
对象
public class DemoData {
* 强制读取第三个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配
*/
@ExcelProperty(index = 2)
private Double doubleData;
* 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据
*/
@ExcelProperty("字符串标题")
private String string;
@ExcelProperty("日期标题")
private Date date;
}
代码
@Test
public void indexOrNameRead() {
String fileName = "demo.xlsx";
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
}
读取多个sheet
代码
@Test
public void repeatedRead() {
String fileName = "demo.xlsx";
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).doReadAll();
fileName = "demo.xlsx";
ExcelReader excelReader = EasyExcel.read(fileName).build();
ReadSheet readSheet1 =
EasyExcel.readSheet(0).head(DemoData.class).registerReadListener(new DemoDataListener()).build();
ReadSheet readSheet2 =
EasyExcel.readSheet(1).head(DemoData.class).registerReadListener(new DemoDataListener()).build();
excelReader.read(readSheet1, readSheet2);
excelReader.finish();
}
自定义格式转换
对象
@Data
public class ConverterData {
* converter属性定义自己的字符串转换器
*/
@ExcelProperty(converter = CustomStringConverter.class)
private String string;
* 这里用string 去接日期才能格式化
*/
@DateTimeFormat("yyyy年MM月dd日 HH时mm分ss秒")
private String date;
* 我想接收百分比的数字
*/
@NumberFormat("#.##%")
private String doubleData;
}
自定义转换器
public class CustomStringStringConverter implements Converter<String> {
@Override
public Class supportJavaTypeKey() {
return String.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
* 这里读的时候会调用
*
* @param cellData
* NotNull
* @param contentProperty
* Nullable
* @param globalConfiguration
* NotNull
* @return
*/
@Override
public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return "自定义:" + cellData.getStringValue();
}
* 这里是写的时候会调用 不用管
*
* @param value
* NotNull
* @param contentProperty
* Nullable
* @param globalConfiguration
* NotNull
* @return
*/
@Override
public CellData convertToExcelData(String value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return new CellData(value);
}
}
代码
@Test
public void converterRead() {
String fileName = "demo.xlsx";
EasyExcel.read(fileName, ConverterData.class, new ConverterDataListener())
.sheet().doRead();
}
多行头
代码
@Test
public void complexHeaderRead() {
String fileName = "demo.xlsx";
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet()
.headRowNumber(1).doRead();
}
读取表头数据
监听器
* 这里会一行行的返回头
* 监听器只需要重写这个方法就可以读取到头信息
* @param headMap
* @param context
*/
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
LOGGER.info("解析到一条头数据:{}", JSON.toJSONString(headMap));
}
代码
@Test
public void headerRead() {
String fileName = "demo.xlsx";
EasyExcel.read(fileName, DemoData.class, new ReadDataListener()).sheet().doRead();
}
异常处理
监听器
* 监听器实现这个方法就可以在读取数据的时候获取到异常信息
*/
@Override
public void onException(Exception exception, AnalysisContext context) {
LOGGER.error("解析失败,但是继续解析下一行:{}", exception.getMessage());
if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
LOGGER.error("第{}行,第{}列解析异常", excelDataConvertException.getRowIndex(),
excelDataConvertException.getColumnIndex());
}
}
web读取
代码
@PostMapping("upload")
@ResponseBody
public String upload(MultipartFile file) throws IOException {
EasyExcel.read(file.getInputStream(), UploadData.class, new UploadDataListener(uploadDAO)).sheet().doRead();
return "SUCCESS";
}
五、详解写入Excel
数据写入公用方法
private List<DemoData> data() {
List<DemoData> list = new ArrayList<DemoData>();
for (int i = 0; i < 10; i++) {
DemoData data = new DemoData();
data.setString("字符串" + i);
data.setDate(new Date());
data.setDoubleData(0.56);
list.add(data);
}
return list;
}
简单写入
对象
public class DemoData {
@ExcelProperty("字符串标题")
private String string;
@ExcelProperty("日期标题")
private Date date;
@ExcelProperty("数字标题")
private Double doubleData;
}
代码
@Test
public void simpleWrite() {
String System.currentTimeMillis() + ".xlsx";
EasyExcel.write(fileName, DemoData.class).sheet("写入方法一").doWrite(data());
// 写法2,方法二需要手动关闭流
fileName = System.currentTimeMillis() + ".xlsx";
ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build();
WriteSheet writeSheet = EasyExcel.writerSheet("写入方法二").build();
excelWriter.write(data(), writeSheet);
/// 千万别忘记finish 会帮忙关闭流
excelWriter.finish();
}
导出指定的列
代码
@Test
public void excludeOrIncludeWrite() {
String fileName = "excludeOrIncludeWrite" + System.currentTimeMillis() + ".xlsx";
Set<String> excludeColumnFiledNames = new HashSet<String>();
excludeColumnFiledNames.add("date");
EasyExcel.write(fileName, DemoData.class).excludeColumnFiledNames(excludeColumnFiledNames).sheet("忽略date")
.doWrite(data());
fileName = "excludeOrIncludeWrite" + System.currentTimeMillis() + ".xlsx";
// 根据用户传入字段 假设我们只要导出 date
Set<String> includeColumnFiledNames = new HashSet<String>();
includeColumnFiledNames.add("date");
EasyExcel.write(fileName, DemoData.class).includeColumnFiledNames(includeColumnFiledNames).sheet("导出date")
.doWrite(data());
}
指定写入的列
对象
public class IndexData {
* 导出的excel第二列和第四列将空置
*/
@ExcelProperty(value = "字符串标题", index = 0)
private String string;
@ExcelProperty(value = "日期标题", index = 2)
private Date date;
@ExcelProperty(value = "数字标题", index = 4)
private Double doubleData;
}
代码
@Test
public void indexWrite() {
String fileName = "indexWrite" + System.currentTimeMillis() + ".xlsx";
EasyExcel.write(fileName, IndexData.class).sheet("模板").doWrite(data());
}
复杂头写入
对象
public class ComplexHeadData {
* 主标题 将整合为一个单元格效果如下:
* —————————————————————————
* | 主标题 |
* —————————————————————————
* |字符串标题|日期标题|数字标题|
* —————————————————————————
*/
@ExcelProperty({"主标题", "字符串标题"})
private String string;
@ExcelProperty({"主标题", "日期标题"})
private Date date;
@ExcelProperty({"主标题", "数字标题"})
private Double doubleData;
}
代码
@Test
public void complexHeadWrite() {
String fileName = "complexHeadWrite" + System.currentTimeMillis() + ".xlsx";
EasyExcel.write(fileName, ComplexHeadData.class).sheet("模板").doWrite(data());
}
自定义格式转
public class ConverterData {
* 自定义的转换
*/
@ExcelProperty(value = "字符串标题", converter = CustomStringConverter.class)
private String string;
* 我想写到excel 用年月日的格式
*/
@DateTimeFormat("yyyy年MM月dd日 HH时mm分ss秒")
@ExcelProperty("日期标题")
private Date date;
* 我想写到excel 用百分比表示
*/
@NumberFormat("#.##%")
@ExcelProperty(value = "数字标题")
private Double doubleData;
}
代码
@Test
public void converterWrite() {
String "converterWrite" + System.currentTimeMillis() + ".xlsx";
EasyExcel.write(fileName, ConverterData.class).sheet("模板").doWrite(data());
}
图片导出
对象
@Data
@ContentRowHeight(200)
@ColumnWidth(200 / 8)
public class ImageData {
private File file;
private InputStream inputStream;
* 如果string类型 必须指定转换器,string默认转换成string,该转换器是官方支持的
*/
@ExcelProperty(converter = StringImageConverter.class)
private String string;
private byte[] byteArray;
* 根据url导出 版本2.1.1才支持该种模式
*/
private URL url;
}
代码
@Test
public void imageWrite() throws Exception {
String fileName = "imageWrite" + System.currentTimeMillis() + ".xlsx";
InputStream inputStream = null;
try {
List<ImageData> list = new ArrayList<ImageData>();
ImageData imageData = new ImageData();
list.add(imageData);
String imagePath = "converter" + File.separator + "img.jpg";
imageData.setByteArray(FileUtils.readFileToByteArray(new File(imagePath)));
imageData.setFile(new File(imagePath));
imageData.setString(imagePath);
inputStream = FileUtils.openInputStream(new File(imagePath));
imageData.setInputStream(inputStream);
imageData.setUrl(new URL(
"https://raw.githubusercontent.com/alibaba/easyexcel/master/src/test/resources/converter/img.jpg"));
EasyExcel.write(fileName, ImageData.class).sheet().doWrite(list);
} finally {
if (inputStream != null) {
inputStream.close();
}
}
}
列宽、行高
对象
@Data
@ContentRowHeight(10)
@HeadRowHeight(20)
@ColumnWidth(25)
public class WidthAndHeightData {
@ExcelProperty("字符串标题")
private String string;
@ExcelProperty("日期标题")
private Date date;
* 宽度为50,覆盖上面的宽度25
*/
@ColumnWidth(50)
@ExcelProperty("数字标题")
private Double doubleData;
}
代码
@Test
public void widthAndHeightWrite() {
String fileName = "widthAndHeightWrite" + System.currentTimeMillis() + ".xlsx";
EasyExcel.write(fileName, WidthAndHeightData.class).sheet("模板").doWrite(data());
}
合并单元格
代码
@Test
public void mergeWrite() {
String fileName = "mergeWrite" + System.currentTimeMillis() + ".xlsx";
LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(2, 0);
EasyExcel.write(fileName, DemoData.class).registerWriteHandler(loopMergeStrategy).sheet("合并单元格")
.doWrite(data());
}
动态表头
代码
@Test
public void dynamicHeadWrite() {
String fileName = "dynamicHeadWrite" + System.currentTimeMillis() + ".xlsx";
EasyExcel.write(fileName)
.head(head()).sheet("模板")
.doWrite(data());
}
private List<List<String>> head() {
List<List<String>> list = new ArrayList<List<String>>();
List<String> head0 = new ArrayList<String>();
head0.add("字符串" + System.currentTimeMillis());
List<String> head1 = new ArrayList<String>();
head1.add("数字" + System.currentTimeMillis());
List<String> head2 = new ArrayList<String>();
head2.add("日期" + System.currentTimeMillis());
list.add(head0);
list.add(head1);
list.add(head2);
return list;
}
web数据写出
代码
@GetMapping("download")
public void download(HttpServletResponse response) throws IOException {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode("数据写出", "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data());
}
六、详解填充样板写入
这里的案例填充都是模板向下,如果需要横向填充只需要模板设置好就可以。
简单的填充
Excel模板
Excel最终效果
对象
public class FillData {
private String name;
private double number;
}
代码
@Test
public void simpleFill() {
String templateFileName = "simple.xlsx";
String fileName = System.currentTimeMillis() + ".xlsx";
FillData fillData = new FillData();
fillData.setName("知春秋");
fillData.setNumber(25);
EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(fillData);
fileName = System.currentTimeMillis() + ".xlsx";
Map<String, Object> map = new HashMap<String, Object>();
map.put("name", "知春秋");
map.put("number", 25);
EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(map);
}
复杂的填充
使用List集合的方法批量写入数据,点表示该参数是集合
Excel模板
Excel最终效果
代码
@Test
public void complexFill() {
String templateFileName = "complex.xlsx";
String fileName = System.currentTimeMillis() + ".xlsx";
ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build();
WriteSheet writeSheet = EasyExcel.writerSheet().build();
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
excelWriter.fill(data(), fillConfig, writeSheet);
excelWriter.fill(data(), fillConfig, writeSheet);
Map<String, Object> map = new HashMap<String, Object>();
excelWriter.fill(map, writeSheet);
excelWriter.finish();
}
七、API
详细参数介绍
关于常见类解析
Ø EasyExcel 入口类,用于构建开始各种操作
Ø ExcelReaderBuilder ExcelWriterBuilder 构建出一个 ReadWorkbook WriteWorkbook,可以理解成一个excel对象,一个excel只要构建一个
Ø ExcelReaderSheetBuilder ExcelWriterSheetBuilder 构建出一个 ReadSheet WriteSheet对象,可以理解成excel里面的一页,每一页都要构建一个
Ø ReadListener 在每一行读取完毕后都会调用ReadListener来处理数据
Ø WriteHandler 在每一个操作包括创建单元格、创建表格等都会调用WriteHandler来处理数据
Ø 所有配置都是继承的,Workbook的配置会被Sheet继承,所以在用EasyExcel设置参数的时候,在EasyExcel…sheet()方法之前作用域是整个sheet,之后针对单个sheet
读
注解
Ø ExcelProperty 指定当前字段对应excel中的那一列。可以根据名字或者Index去匹配。当然也可以不写,默认第一个字段就是index=0,以此类推。千万注意,要么全部不写,要么全部用index,要么全部用名字去匹配。千万别三个混着用,除非你非常了解源代码中三个混着用怎么去排序的。
Ø ExcelIgnore 默认所有字段都会和excel去匹配,加了这个注解会忽略该字段
Ø DateTimeFormat 日期转换,用String去接收excel日期格式的数据会调用这个注解。里面的value参照java.text.SimpleDateFormat
Ø NumberFormat 数字转换,用String去接收excel数字格式的数据会调用这个注解。里面的value参照java.text.DecimalFormat
Ø ExcelIgnoreUnannotated 默认不加ExcelProperty 的注解的都会参与读写,加了不会参与
参数
通用参数
Ø ReadWorkbook,ReadSheet 都会有的参数,如果为空,默认使用上级。
Ø converter 转换器,默认加载了很多转换器。也可以自定义。
Ø readListener 监听器,在读取数据的过程中会不断的调用监听器。
Ø headRowNumber 需要读的表格有几行头数据。默认有一行头,也就是认为第二行开始起为数据。
Ø head 与clazz二选一。读取文件头对应的列表,会根据列表匹配数据,建议使用class。
Ø clazz 与head二选一。读取文件的头对应的class,也可以使用注解。如果两个都不指定,则会读取全部数据。
Ø autoTrim 字符串、表头等数据自动trim
Ø password 读的时候是否需要使用密码
ReadWorkbook(理解成excel对象)参数
Ø excelType 当前excel的类型 默认会自动判断
Ø inputStream 与file二选一。读取文件的流,如果接收到的是流就只用,不用流建议使用file参数。因为使用了inputStream easyexcel会帮忙创建临时文件,最终还是file
Ø file 与inputStream二选一。读取文件的文件。
Ø autoCloseStream 自动关闭流。
Ø readCache 默认小于5M用 内存,超过5M会使用 EhCache,这里不建议使用这个参数。
ReadSheet(就是excel的一个Sheet)参数
Ø sheetNo 需要读取Sheet的编码,建议使用这个来指定读取哪个Sheet
Ø sheetName 根据名字去匹配Sheet,excel 2003不支持根据名字去匹配
写
注解
Ø ExcelProperty index 指定写到第几列,默认根据成员变量排序。value指定写入的名称,默认成员变量的名字,多个value可以参照快速开始中的复杂头
Ø ExcelIgnore 默认所有字段都会写入excel,这个注解会忽略这个字段
Ø DateTimeFormat 日期转换,将Date写到excel会调用这个注解。里面的value参照java.text.SimpleDateFormat
Ø NumberFormat 数字转换,用Number写excel会调用这个注解。里面的value参照java.text.DecimalFormat
Ø ExcelIgnoreUnannotated 默认不加ExcelProperty 的注解的都会参与读写,加了不会参与
参数
通用参数
Ø WriteWorkbook,WriteSheet ,WriteTable都会有的参数,如果为空,默认使用上级。
Ø converter 转换器,默认加载了很多转换器。也可以自定义。
Ø writeHandler 写的处理器。可以实现WorkbookWriteHandler,SheetWriteHandler,RowWriteHandler,CellWriteHandler,在写入excel的不同阶段会调用
Ø relativeHeadRowIndex 距离多少行后开始。也就是开头空几行
Ø needHead 是否导出头
Ø head 与clazz二选一。写入文件的头列表,建议使用class。
Ø clazz 与head二选一。写入文件的头对应的class,也可以使用注解。
Ø autoTrim 字符串、表头等数据自动trim
WriteWorkbook(理解成excel对象)参数
Ø excelType 当前excel的类型 默认xlsx
Ø outputStream 与file二选一。写入文件的流
Ø file 与outputStream二选一。写入的文件
Ø templateInputStream 模板的文件流
Ø templateFile 模板文件
Ø autoCloseStream 自动关闭流。
Ø password 写的时候是否需要使用密码
Ø useDefaultStyle 写的时候是否是使用默认头
WriteSheet(就是excel的一个Sheet)参数
Ø sheetNo 需要写入的编码。默认0
Ø sheetName 需要些的Sheet名称,默认同sheetNo
WriteTable(就把excel的一个Sheet,一块区域看一个table)参数
Ø tableNo 需要写入的编码。默认0
更多学习资料,可以关注下方微信公众号,回复关键字:Java高级资料
即可免费获取完整的Java高级开发工程师视频资料。
评论