Table of contents
2.4.2 Excel custom style interceptor
2.4.4 Excel custom column width interceptor
Three, EasyExcel common annotations
4、@ContentLoopMerge和@OnceAbsoluteMerge
foreword
I believe that most web projects will have the need to export and import Excel files. Today we will take a look at how to use Ali's EasyExcel component to realize the need to export Excel files. I have also written several articles about using Apache POI to export Excel before. Interested friends can go and have a look, and the link is also released for everyone:
1. Apache POI export excel (1): single sheet
2. Apache POI export excel (2): multiple sheets
3. JAVA export excel (3): export zip compressed package
1. Introduction to EasyExcel
The well-known frameworks for parsing and generating Excel in Java include Apache POI and JXL . But they all have a serious problem that consumes a lot of memory. POI has a SAX mode API that can solve some memory overflow problems to a certain extent, but POI still has some defects, such as Excel version 07 decompression and storage after decompression. It is done in memory, and the memory consumption is still very large.
EasyExcel rewrites POI's analysis of Excel version 07. A 3M excel still needs about 100M memory for POI sax analysis. Using EasyExcel can reduce it to a few M, and no matter how big the excel is, there will be no memory overflow; version 03 relies on POI's sax mode encapsulates the model conversion on the upper layer, making it easier and more convenient for users.
1. Website
- Official website: https://easyexcel.opensource.alibaba.com/
- github address: https://github.com/alibaba/easyexcel
- gitee address: https://gitee.com/easyexcel/easyexcel
2. Use of EasyExcel
1. Related dependencies
pom.xml
<!-- easyExcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.2.1</version>
<!-- 3+以上版本的easyExcel,使用poi 5+以上版本时,需要手动排除:poi-ooxml-schemas -->
<exclusions>
<exclusion>
<artifactId>poi-ooxml-schemas</artifactId>
<groupId>org.apache.poi</groupId>
</exclusion>
</exclusions>
</dependency>
2. Realize the code
2.1 Entity class
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.test.java.converter.GenderConverter;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 学生类
*/
@Data // lombok注解,会生成getter/setter方法
@ExcelIgnoreUnannotated // 没加导出注解的字段,不导出
public class StudentVo implements Serializable {
/*** 用户ID*/
private Long userId;
/*** 姓名*/
@ExcelProperty(value = "姓名")
@ColumnWidth(10)
private String studentName;
/*** 年龄*/
@ExcelProperty(value = "年龄")
private Integer age;
/*** 手机号*/
@ExcelProperty(value = "手机号")
private String phone;
/*** 性别(1男 2女)*/
@ExcelProperty(value = "性别", converter = GenderConverter.class)
private Integer gender;
/*** 生日*/
@ExcelProperty(value = "生日")
private String birthday;
/*** 分数*/
@ExcelProperty(value = "分数")
@NumberFormat(value = "###.#") // 数字格式化,保留1位小数
private BigDecimal score;
/*** 创建时间*/
@ExcelProperty(value = "创建时间")
@DateTimeFormat("yyyy-MM-dd")
private Date createTime;
}
2.2 Service layer
2.2.1 Service interface class
// 导出学生信息
List<StudentVo> exportStudent();
2.2.2 Implementation class
@Override
public List<StudentVo> exportStudent() {
List<StudentVo> list = new ArrayList<>();
// 我这里使用for循环 创造了10天测试数据,实际的业务场景肯定是从数据库中查出需要导出的数据
for (int i = 1; i <= 10; i++) {
StudentVo student = new StudentVo();
student.setUserId((long) i);
student.setStudentName("王" + i);
student.setAge(18 + i);
student.setPhone("1305678111" + i);
if (i % 2 == 0) {
student.setGender(2);
} else {
student.setGender(1);
}
student.setBirthday("1997-01-01");
student.setCreateTime(new Date());
list.add(student);
}
return list;
}
2.3 Control layer
/**
* 导出学生信息
*/
@GetMapping("/exportStudent")
public void exportStudent(HttpServletResponse response) {
List<StudentVo> list = userService.exportStudent();
// 指定列导出
String column = "studentName,age,phone";// 定义无需导出的列字段
if (StringUtils.isNotEmpty(column)) {
List<String> columns = Arrays.asList(column.split(","));
ExportUtil.exportExcel(response, StudentVo.class, "学生信息", list, columns);
} else {
ExportUtil.exportExcel(response, StudentVo.class, "学生信息", list);
}
}
PS : I wrote two export methods here. One is to export all normally; the other is to export by specifying columns, you only need to pass in the set of column fields that do not need to be exported, and the export will be filtered when exporting.
2.4 Related Tools
2.4.1 Export Util
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.write.metadata.WriteSheet;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* Excel工具类
*/
public class ExportUtil extends EasyExcelFactory {
public ExportUtil() {
}
/**
* 导入Excel
*/
public static void importExcel(InputStream inputStream, Class<?> head, ReadListener readListener) {
read(inputStream, head, readListener).sheet().doRead();
}
/**
* 导出Excel(全部)
*
* @param response 响应
* @param clazz 表头数据
* @param fileName 文件名
* @param list 需要导出的数据
*/
public static void exportExcel(HttpServletResponse response, Class<?> clazz, String fileName, List<?> list) {
baseExportExcel(response, clazz, fileName, list, new ArrayList<>());
}
/**
* 导出Excel(指定列导出)
*
* @param response 响应
* @param clazz 表头数据
* @param fileName 文件名
* @param list 需要导出的数据
* @param excludeColumns 过滤导出的字段名
*/
public static void exportExcel(HttpServletResponse response, Class<?> clazz, String fileName, List<?> list, List<String> excludeColumns) {
baseExportExcel(response, clazz, fileName, list, excludeColumns);
}
public static void baseExportExcel(HttpServletResponse response, Class<?> clazz, String fileName, List<?> list, List<String> excludeColumns) {
try {
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
String name = format.format(new Date());
String filename = URLEncoder.encode(name + fileName + ".xlsx", "UTF-8");
response.setCharacterEncoding("utf-8");
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition", "attachment;filename=" + filename);
ExcelWriter excelWriter = write(response.getOutputStream()).registerWriteHandler(new ExcelColumnWidthHandler()).registerWriteHandler(new ExcelSheetWriteHandler()).registerWriteHandler(new ExcelStyleHandler()).excludeColumnFieldNames(excludeColumns).build();
WriteSheet writeSheet = writerSheet(0, fileName).head(clazz).build();
excelWriter.write(list, writeSheet);
excelWriter.finish();
} catch (Exception var8) {
throw new RuntimeException("导出" + fileName + "失败");
}
}
/**
* 导出Excel(多个sheet导出)
*
* @param response 响应
* @param fileName 文件名
*/
public static ExcelWriter exportExcels(HttpServletResponse response, String fileName) {
try {
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
String name = format.format(new Date());
String filename = URLEncoder.encode(name + fileName + ".xlsx", "UTF-8");
response.setCharacterEncoding("utf-8");
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition", "attachment;filename=" + filename);
return write(response.getOutputStream()).registerWriteHandler(new ExcelColumnWidthHandler()).registerWriteHandler(new ExcelSheetWriteHandler()).registerWriteHandler(new ExcelStyleHandler()).build();
} catch (Exception var3) {
throw new RuntimeException("导出" + fileName + "失败");
}
}
/**
* 创建工作表sheet
*
* @param sheetNo 工作表编号
* @param sheetName 工作表名称
* @param clazz 表头数据
*/
public static WriteSheet createSheet(Integer sheetNo, String sheetName, Class<?> clazz) {
return writerSheet(sheetNo, sheetName).head(clazz).build();
}
}
2.4.2 Excel custom style interceptor
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.AbstractVerticalCellStyleStrategy;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
/**
* Excel自定义样式拦截器
*/
public class ExcelStyleHandler extends AbstractVerticalCellStyleStrategy {
private static final String FONT_NAME = "等线";
private static final Integer[] COLUMN_INDEX = new Integer[]{1, 2, 3, 4, 7, 8, 9};
@Override
protected WriteCellStyle contentCellStyle(Head head) {
// 内容的策略
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
// 背景白色
contentWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
WriteFont contentWriteFont = new WriteFont();
// 字体大小
contentWriteFont.setFontHeightInPoints((short) 12);
// 字体样式
contentWriteFont.setFontName(FONT_NAME);
contentWriteCellStyle.setWriteFont(contentWriteFont);
return contentWriteCellStyle;
}
}
2.4.3 Excel cell interceptor
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.xssf.streaming.SXSSFSheet;
/**
* Excel单元格拦截器
*/
public class ExcelSheetWriteHandler implements SheetWriteHandler {
// 设置100列column
private static final Integer COLUMN = 100;
@Override
public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
}
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
for(int i = 0; i < COLUMN; ++i) {
// 设置为文本格式
SXSSFSheet sxssfSheet = (SXSSFSheet)writeSheetHolder.getSheet();
CellStyle cellStyle = writeWorkbookHolder.getCachedWorkbook().createCellStyle();
// 49为文本格式
cellStyle.setDataFormat((short)49);
// i为列,一整列设置为文本格式
sxssfSheet.setDefaultColumnStyle(i, cellStyle);
}
}
}
2.4.4 Excel custom column width interceptor
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import org.apache.poi.ss.usermodel.Cell;
import java.util.List;
/**
* Excel自定义列宽拦截器
*/
public class ExcelColumnWidthHandler extends AbstractColumnWidthStyleStrategy {
@Override
protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
if (Boolean.TRUE.equals(isHead)) {
int columnWidth = cell.getStringCellValue().length();
columnWidth = Math.max(columnWidth * 2, 20);
if (columnWidth > 255) {
columnWidth = 255;
}
writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256);
}
}
}
The three interceptor classes 2.4.2 , 2.4.3 and 2.4.4 are mainly used to customize some styles of exporting Excel, and small partners can also optimize and modify according to their own needs.
2.4.5 Gender changer
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.ReadConverterContext;
import com.alibaba.excel.converters.WriteConverterContext;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.test.java.base.GenderEnum;
/**
* 性别转换器
*/
public class GenderConverter implements Converter<Integer> {
@Override
public Class<?> supportJavaTypeKey() {
return Integer.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
// 读取Excel文件时将string转换为integer(导入)
@Override
public Integer convertToJavaData(ReadConverterContext<?> context) {
return GenderEnum.convert(context.getReadCellData().getStringValue()).getValue();
}
// 写入Excel文件时将integer转换为string(导出)
@Override
public WriteCellData<?> convertToExcelData(WriteConverterContext<Integer> context) {
return new WriteCellData<>(GenderEnum.convert(context.getValue()).getDescription());
}
}
2.4.6 Gender enumeration
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.stream.Stream;
/**
* 性别枚举
*/
@Getter
@AllArgsConstructor
public enum GenderEnum {
/**
* 未知
*/
UNKNOWN(0, "未知"),
/**
* 男性
*/
MALE(1, "男性"),
/**
* 女性
*/
FEMALE(2, "女性");
private final Integer value;
@JsonFormat
private final String description;
public static GenderEnum convert(Integer value) {
return Stream.of(values())
.filter(bean -> bean.value.equals(value))
.findAny()
.orElse(UNKNOWN);
}
public static GenderEnum convert(String description) {
return Stream.of(values())
.filter(bean -> bean.description.equals(description))
.findAny()
.orElse(UNKNOWN);
}
}
2.5 Results
Export all:
Export specified columns:
Three, EasyExcel common annotations
1、@ExcelProperty
A necessary field annotation. There are three parameters in the annotation value
, index
which converter
respectively represent the column name, column number, and data conversion method. Usually, it is not necessary to set 1. value corresponds to the title text 2. index corresponds to the text line number 3. converter converter, usually Inbound and outbound conversion use, such as gender warehousing 0 and 1, outbound male and femaleconverter
Example :
public class StudentVo {
/*** 姓名*/
@ExcelProperty(value = "姓名", index = 1)
private String studentName;
/*** 手机号*/
@ExcelProperty(value = "手机号", index = 2)
private String phone;
/*** 性别(1男 2女)*/
@ExcelProperty(value = "性别", index = 3,converter = GenderConverter.class)
private Integer gender;
}
2、@ColumnWith
To set the column width, there is only one parameter value, and the unit of value is the character length. The maximum number of characters that can be set is 255 characters, because the maximum number of characters that can be written in an excel cell is 255 characters.
Example :
public class StudentVo implements Serializable {
/*** 姓名*/
@ExcelProperty(value = "姓名")
@ColumnWidth(10)
private String studentName;
}
3、@ContentFontStyle
Annotation used to format the font of the cell content.
parameter | meaning |
---|---|
fontName | font name |
fontHeightInPoints | font height |
italic | whether italics |
strikeout | whether to set delete horizontal line |
color | font color |
typeOffset | Offset |
underline | underline |
bold | Is it bold |
charset | Encoding format |
4、@ContentLoopMerge和@OnceAbsoluteMerge
The same point: These two are annotations for setting merged cells; they are merged according to the specified number of rows and columns, and cannot achieve the same merge.
The difference: @ContentLoopMerge is marked on the field; @OnceAbsoluteMerge is marked on the class.
4.1 @ContentLoopMerge
parameter | meaning |
---|---|
eachRow | specified number of lines |
columnExtend | Specify the number of columns |
Example :
@Data
public class Demo implements Serializable {
@ExcelProperty(value = "商户名称", index = 0)
private String appName;
@ExcelProperty(value = "城市名称", index = 1)
@ContentLoopMerge(eachRow = 2, columnExtend = 3)
private String cityName;
@ExcelProperty(value = "区域名称", index = 2)
private String regionName;
@ExcelProperty(value = "商圈名称", index = 3)
private String businessAreaName;
@ExcelProperty(value = "楼盘名称", index = 4)
private String gardenName;
@ExcelProperty(value = "楼栋名称", index = 5)
private String buildingName;
@ExcelProperty(value = "单元名称", index = 6)
private String unitName;
@ExcelProperty(value = "价格", index = 7)
private Integer price;
}
result :
4.2 @OnceAbsoluteMerge
parameter | meaning |
---|---|
firstRowIndex | Specifies the index of the first row to be merged |
lastRowIndex | Specifies the index of the last row to be merged |
firstColumnIndex | Specifies the index of the first column to be merged |
Example :
@Data
@OnceAbsoluteMerge(firstRowIndex = 1, lastRowIndex = 3 , firstColumnIndex = 1 , lastColumnIndex = 3)
public class Demo implements Serializable {
@ExcelProperty(value = "商户名称", index = 0)
private String appName;
@ExcelProperty(value = "城市名称", index = 1)
private String cityName;
@ExcelProperty(value = "区域名称", index = 2)
private String regionName;
@ExcelProperty(value = "商圈名称", index = 3)
private String businessAreaName;
@ExcelProperty(value = "楼盘名称", index = 4)
private String gardenName;
@ExcelProperty(value = "楼栋名称", index = 5)
private String buildingName;
@ExcelProperty(value = "单元名称", index = 6)
private String unitName;
@ExcelProperty(value = "价格", index = 7)
private Integer price;
}
result :
5、@ContentRowHeight
Used to set row height.
parameter | meaning |
---|---|
value | Row height, -1 stands for automatic row height |
6、@ContentStyle
Used to format content.
parameter | meaning |
---|---|
dataFormat | date format |
hidden | Set the cell to hide using this style |
locked | Set the cell to lock using this style |
quotePrefix | Add a ` symbol in front of the cell, and the number or formula will be displayed as a string |
horizontalAlignment | Set whether to center horizontally |
wrapped | Sets whether the text should wrap. Set this flag to true make all content in a cell visible by displaying it on multiple lines |
verticalAlignment | Set whether to center vertically |
rotation | Sets the rotation angle of the text in the cell. The rotation angle interval of the Excel version 03 is -90°90°, and the rotation angle interval of the Excel version 07 is 0°180° |
indent | Sets the number of spaces to indent text in a cell |
borderLeft | Set the style of the left border |
borderRight | Set the right border style |
borderTop | Set the top border style |
borderBottom | Set the bottom border style |
leftBorderColor | Set the left border color |
rightBorderColor | set the right border color |
topBorderColor | 设置上边框颜色 |
bottomBorderColor | 设置下边框颜色 |
fillPatternType | 设置填充类型 |
fillBackgroundColor | 设置背景色 |
fillForegroundColor | 设置前景色 |
shrinkToFit | 设置自动单元格自动大小 |
7、@HeadFontStyle
用于定制标题字体格式。
参数 | 含义 |
---|---|
fontName | 设置字体名称 |
fontHeightInPoints | 设置字体高度 |
italic | 设置字体是否斜体 |
strikeout | 是否设置删除线 |
color | 设置字体颜色 |
typeOffset | 设置偏移量 |
underline | 设置下划线 |
charset | 设置字体编码 |
bold | 设置字体是否加粗 |
8、@HeadRowHeight
用于设置标题行行高。
参数 | 含义 |
---|---|
value | 设置行高,-1代表自动行高 |
9、@HeadStyle
用于设置标题样式。
参数 | 含义 |
---|---|
dataFormat | 日期格式 |
hidden | 设置单元格使用此样式隐藏 |
locked | 设置单元格使用此样式锁定 |
quotePrefix | 在单元格前面增加`符号,数字或公式将以字符串形式展示 |
horizontalAlignment | 设置是否水平居中 |
wrapped | 设置文本是否应换行。将此标志设置为true 通过在多行上显示使单元格中的所有内容可见 |
verticalAlignment | 设置是否垂直居中 |
rotation | 设置单元格中文本旋转角度。03版本的Excel旋转角度区间为-90°90°,07版本的Excel旋转角度区间为0°180° |
indent | 设置单元格中缩进文本的空格数 |
borderLeft | 设置左边框的样式 |
borderRight | 设置右边框样式 |
borderTop | 设置上边框样式 |
borderBottom | 设置下边框样式 |
leftBorderColor | 设置左边框颜色 |
rightBorderColor | 设置右边框颜色 |
topBorderColor | 设置上边框颜色 |
bottomBorderColor | 设置下边框颜色 |
fillPatternType | 设置填充类型 |
fillBackgroundColor | 设置背景色 |
fillForegroundColor | 设置前景色 |
shrinkToFit | 设置自动单元格自动大小 |
10、@ExcelIgnore
不将该字段转换成Excel。
11、@ExcelIgnoreUnannotated
Fields not annotated with @ExcelProperty are not converted.
12、@NumberFormat
Used for number formatting.
parameter | example | result |
---|---|---|
value | @NumberFormat(value = "###.#") |
66.8 |
@NumberFormat(value = "#.##%") | 66.8% | |
@NumberFormat(value = "0.00") | 66.80 |
The difference between using #.## and 0.00 for the parameter value is that, for example, if the imported value is 66.80, #.## will ignore the following 0 and become 66.8, while 0.00 will not ignore it, keeping the original 66.80, which I use here It is the practice of retaining 2 decimal places when importing. For the parameter of value in @NumberFormat, please refer to java.text.DecimalFormat .
13、@DateTimeFormat
Used for time formatting.
parameter | example | result |
---|---|---|
value | @DateTimeFormat(value="yyyy-MM-dd") | 2023-07-24 |