Tabla de contenido
2.2.1 Clase de interfaz de servicio
2.4.2 Interceptor de estilo personalizado de Excel
2.4.3 Interceptor de celdas de Excel
2.4.4 Interceptor de ancho de columna personalizado de Excel
Tres anotaciones comunes de EasyExcel
4, @ContentLoopMerge y @OnceAbsoluteMerge
11, @ExcelIgnorarSin anotaciones
prefacio
Creo que la mayoría de los proyectos web tendrán la necesidad de exportar e importar archivos de Excel.Hoy veremos cómo usar el componente EasyExcel de Ali para darse cuenta de la necesidad de exportar archivos de Excel . También he escrito varios artículos sobre el uso de Apache POI para exportar Excel antes. Los amigos interesados pueden ir y echar un vistazo, y el enlace también se publica para todos:
1. Excel de exportación de PDI de Apache (1): hoja única
2. Apache POI export excel (2): varias hojas
3. Exportación de JAVA excel (3): exportar paquete comprimido zip
1. Introducción a EasyExcel
Los marcos conocidos para analizar y generar Excel en Java incluyen Apache POI y JXL . Pero todos tienen un problema grave que consume mucha memoria.POI tiene una API en modo SAX que puede resolver algunos problemas de desbordamiento de memoria hasta cierto punto, pero POI todavía tiene algunos defectos, como la descompresión de la versión 07 de Excel y el almacenamiento después de la descompresión. Se hace en la memoria, y el consumo de memoria sigue siendo muy grande.
EasyExcel ha reescrito el análisis de POI de la versión 07 de Excel. Un excel de 3M todavía necesita alrededor de 100 M de memoria para el análisis de saxofón de POI. El uso de EasyExcel puede reducirlo a unos pocos M, y no importa qué tan grande sea el Excel, no habrá desbordamiento de memoria; versión 03 se basa en el modo de saxofón de POI que encapsula la conversión del modelo en la capa superior, lo que lo hace más fácil y conveniente para los usuarios.
1. Sitio web
- Sitio web oficial: https://easyexcel.opensource.alibaba.com/
- dirección de github: https://github.com/alibaba/easyexcel
- dirección de la casa rural: https://gitee.com/easyexcel/easyexcel
2. Uso de EasyExcel
1. Dependencias relacionadas
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. Darse cuenta del código
2.1 Clase de entidad
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 Capa de servicio
2.2.1 Clase de interfaz de servicio
// 导出学生信息
List<StudentVo> exportStudent();
2.2.2 Clase de implementación
@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 Capa de control
/**
* 导出学生信息
*/
@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);
}
}
PD : Escribí dos métodos de exportación aquí. Una es exportar todo normalmente; la otra es exportar especificando columnas, solo necesita pasar el conjunto de campos de columna que no necesitan exportarse, y la exportación se filtrará al exportar.
2.4 Herramientas relacionadas
2.4.1 Utilidad de exportación
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 Interceptor de estilo personalizado de Excel
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 Interceptor de celdas de Excel
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 Interceptor de ancho de columna personalizado de Excel
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);
}
}
}
Las tres clases de interceptor 2.4.2 , 2.4.3 y 2.4.4 se utilizan principalmente para personalizar algunos estilos de exportación de Excel, y los socios pequeños también pueden optimizar y modificar según sus propias necesidades.
2.4.5 cambiador de género
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 Enumeración de género
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 Resultados
Exportar todo:
Exportar columnas especificadas:
Tres anotaciones comunes de EasyExcel
1, @ExcelPropiedad
Una anotación de campo necesaria. Hay tres parámetros en la anotación value
, index
que converter
representan respectivamente el nombre de la columna, el número de columna y el método de conversión de datos. Por lo general, no es necesario establecer 1. El valor corresponde al texto del título. 2. El índice corresponde al número de línea de texto 3. El convertidor convertidor, generalmente uso de conversión entrante y saliente, como almacenamiento de género 0 y 1, saliente masculino y femeninoconverter
Ejemplo :
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, @ColumnaCon
Para establecer el ancho de columna, solo hay un valor de parámetro, y la unidad de valor es la longitud del carácter. El número máximo de caracteres que se pueden establecer es de 255 caracteres, porque el número máximo de caracteres que se pueden escribir en una celda de Excel tiene 255 caracteres.
Ejemplo :
public class StudentVo implements Serializable {
/*** 姓名*/
@ExcelProperty(value = "姓名")
@ColumnWidth(10)
private String studentName;
}
3, @ContentFontStyle
Anotación utilizada para dar formato a la fuente del contenido de la celda.
parámetro | significado |
---|---|
nombre de la fuente | nombre de la fuente |
fontHeightInPoints | altura de fuente |
itálico | si cursiva |
tachar | si configurar eliminar línea horizontal |
color | color de fuente |
tipoDesplazamiento | Compensar |
subrayar | subrayar |
atrevido | ¿Es audaz? |
juego de caracteres | Formato de codificación |
4, @ContentLoopMerge y @OnceAbsoluteMerge
El mismo punto: estas dos son anotaciones que se usan para establecer las celdas combinadas; se combinan de acuerdo con el número especificado de filas y columnas, y no pueden lograr el mismo contenido combinado.
La diferencia: @ContentLoopMerge está marcado en el campo; @OnceAbsoluteMerge está marcado en la clase.
4.1 @ContentLoopMerge
parámetro | significado |
---|---|
cada fila | número especificado de líneas |
columnExtend | Especificar el número de columnas |
Ejemplo :
@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;
}
resultado :
4.2 @OnceAbsoluteFusion
parámetro | significado |
---|---|
índiceprimerafila | Especifica el índice de la primera fila que se fusionará |
último índice de fila | Especifica el índice de la última fila que se fusionará |
primerÍndiceColumna | Especifica el índice de la primera columna que se fusionará |
Ejemplo :
@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;
}
resultado :
5, @ContentRowHeight
Se utiliza para establecer la altura de la fila.
parámetro | significado |
---|---|
valor | Altura de fila, -1 significa altura de fila automática |
6, @ Estilo de contenido
Se utiliza para dar formato al contenido.
parámetro | significado |
---|---|
formato de datos | formato de fecha |
oculto | Configure la celda para que se oculte usando este estilo |
bloqueado | Configure la celda para que se bloquee usando este estilo |
citaPrefijo | Agregue un símbolo ` delante de la celda y el número o fórmula se mostrará como una cadena |
alineación horizontal | Establecer si centrar horizontalmente |
envuelto | Establece si el texto debe ajustarse. Establezca esta marca para true que todo el contenido de una celda sea visible mostrándolo en varias líneas |
alineamiento vertical | Establecer si centrar verticalmente |
rotación | Establece el ángulo de rotación del texto en la celda. El intervalo del ángulo de rotación de la versión 03 de Excel es -90°90°, y el intervalo del ángulo de rotación de la versión 07 de Excel es 0°180° |
sangrar | Establece el número de espacios para sangrar el texto en una celda |
borde izquierdo | Establecer el estilo del borde izquierdo |
bordeDerecha | Establecer el estilo de borde derecho |
bordesuperior | Establecer el estilo del borde superior |
borde inferior | Establecer el estilo del borde inferior |
color del borde izquierdo | Establecer el color del borde izquierdo |
rightBorderColor | establecer el color del borde derecho |
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, @ExcelIgnorarSin anotaciones
Los campos no anotados con @ExcelProperty no se convierten.
12, @ formato de número
Se utiliza para formatear números.
parámetro | ejemplo | resultado |
---|---|---|
valor | @NumberFormat(valor = "###.#") |
66.8 |
@NumberFormat(valor = "#.##%") | 66,8% | |
@NumberFormat(valor = "0.00") | 66.80 |
La diferencia entre usar #.## y 0.00 para el valor del parámetro es que, por ejemplo, si el valor importado es 66.80, #.## ignorará el siguiente 0 y se convertirá en 66.8, mientras que 0.00 no lo ignorará, manteniendo el original. 66.80, que uso aquí Es la práctica de retener 2 lugares decimales al importar Para el parámetro de valor en @NumberFormat, consulte java.text.DecimalFormat .
13, @ Formato de fecha y hora
Se utiliza para formatear la hora.
parámetro | ejemplo | resultado |
---|---|---|
valor | @DateTimeFormat(valor="aaaa-MM-dd") | 2023-07-24 |