Зачем говорить, что EasyExcel может позволить вам попрощаться с easypoi? Прежде чем говорить об этой проблеме, давайте сначала разберемся с easypoi.
easypoi
Функция easypoi похожа на название easy, а основная функция проста, так что человек, который никогда не встречал poi, может легко написать экспорт Excel, экспорт шаблона Excel, импорт Excel, экспорт шаблона Word с помощью простых аннотаций и языка шаблонов ( знаком с синтаксисом выражений poi), завершите предыдущее сложное написание
Это определение, приведенное в официальном EasyPoi, после использования этого инструмента, обнаруженного во время импорта и экспорта в Excel, который очень удобно. В частности, какая-то простая Excel
Например, для этого простого excel easypoi действительно лучший выбор.Вам нужно только ввести зависимости mavn, добавить pojo, добавить комментарий, а затем вы можете экспортировать.
Но при столкновении с более сложным превосходством, например следующим:
Подобно такому сложному заголовку, один лист имеет несколько листов, несколько листов и объединенные ячейки в различных сложных ситуациях, с easypoi сложнее справиться, с другой стороны, easyExcel лучше.
easyExcel обрабатывает простой Excel так же просто, как easypoi, и может обрабатывать сложный Excel за один шаг. Разработчикам нужно всего лишь написать небольшой код стиля, чтобы напрямую добиться желаемого эффекта.Давайте взглянем на мощь easyExcel.
Внедрение зависимостей maven
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.10</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.15</version>
</dependency>
Новое предприятие
@Data
@Accessors(chain = true)
@FieldNameConstants
@HeadRowHeight(value = 25)
@ContentRowHeight(value = 18)
@ColumnWidth(value = 20)
@HeadStyle(fillBackgroundColor = 64)
@HeadFontStyle(bold = false)
@ContentStyle(borderTop= BorderStyle.THIN,borderLeft = BorderStyle.THIN,borderRight = BorderStyle.THIN,borderBottom = BorderStyle.THIN)
public class ComplexSubjectEasyExcel {
@ExcelProperty(value = {"科目余额表","编制单位: 测试单位321412","科目编码","科目编码"},index = 0)
private String subjectId;
@ExcelProperty(value = {"科目余额表","编制单位: 测试单位321412","科目名称","科目名称"},index = 1)
private String subjectName;
@HeadFontStyle(bold = true)
@ExcelProperty(value = {"科目余额表","编制单位: 测试单位321412","期初余额","借方"},index = 2)
private BigDecimal firstBorrowMoney;
@HeadFontStyle(bold = true)
@ExcelProperty(value = {"科目余额表","编制单位: 测试单位321412","期初余额","贷方"},index = 3)
private BigDecimal firstCreditMoney;
@HeadFontStyle(bold = true)
@ExcelProperty(value = {"科目余额表","2021年9月至2021年9月","本期发生额","借方"},index = 4)
private BigDecimal nowBorrowMoney;
@HeadFontStyle(bold = true)
@ExcelProperty(value = {"科目余额表","2021年9月至2021年9月","本期发生额","贷方"},index = 5)
private BigDecimal nowCreditMoney;
@HeadFontStyle(bold = true)
@ExcelProperty(value = {"科目余额表","2021年9月至2021年9月","本年累计发生额","借方"},index = 6)
private BigDecimal yearBorrowMoney;
@HeadFontStyle(bold = true)
@ExcelProperty(value = {"科目余额表","2021年9月至2021年9月","本年累计发生额","贷方"},index = 7)
private BigDecimal yearCreditMoney;
@HeadFontStyle(bold = true)
@ExcelProperty(value = {"科目余额表","单位:元","期末余额","借方"},index = 8)
private BigDecimal endBorrowMoney;
@HeadFontStyle(bold = true)
@ExcelProperty(value = {"科目余额表","单位:元","期末余额","贷方"},index = 9)
private BigDecimal endCreditMoney;
}
Значение аннотации @ExcelProperty представляет собой массив, по индексу сверху вниз будет объединен заголовок одного и того же значения. Этот способ слияния заголовков гораздо более интуитивен и удобен, чем вложение сущностей easypoi. После того, как мы запросим данные списка страниц, нам не нужно вкладывать и собирать объекты массива, что экономит много работы.Если вы хотите, чтобы заголовок заголовка был динамическим, вы также можете установить его в виде #{title} (конечно, это мой собственный пакет)
инструмент экспорта
Инструмент экспорта easyExcel, описанный ниже, можно скопировать напрямую и настроить соответствующим образом.
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.builder.ExcelWriterTableBuilder;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.WriteTable;
import org.springframework.util.Assert;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.*;
/**
* easyExcel工具
*/
public class EasyExcelUtilsV1 {
public static final String FILE_PATH = "/home/easy/excel/";
public static final Map<String,List<ExcelAnnotationValue>> annotationValues = new HashMap<>();
private static String outputStream(String fileName){
try {
String path = FILE_PATH+new Date().getTime() +"/";
String filePath = path+fileName+".xls";
File dir = new File(path);
if(!dir.exists()){
dir.mkdirs();
}
File file = new File(filePath);
if(file.exists()){
file.deleteOnExit();
}
file.createNewFile();
return filePath;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 默认导出方式 单个sheet
*/
public static String defaultExportOssUrl(List<?> list, Class<?> pojoClass, String fileName, String sheetName, Map<String,String> vars){
resetCLassAnnotationValue(pojoClass);
setExportClassAnnotation(pojoClass,vars);
String filePath = outputStream(fileName);
EasyExcel.write(filePath,pojoClass)
.sheet(sheetName)
.registerWriteHandler(new CustomCellWriteHandler())
.doWrite(list);
return getExcelOssUrl(filePath,fileName);
}
/**
* 默认导出方式 单个sheet
*/
public static String defaultExportOssUrl(List<?> list, Class<?> pojoClass, CellWriteHandler handler, String fileName, String sheetName, Map<String,String> vars){
resetCLassAnnotationValue(pojoClass);
setExportClassAnnotation(pojoClass,vars);
String filePath = outputStream(fileName);
EasyExcel.write(filePath,pojoClass)
.sheet(sheetName)
.registerWriteHandler(new CustomCellWriteHandler())
.registerWriteHandler(handler)
.doWrite(list);
return getExcelOssUrl(filePath,fileName);
}
/**
* 默认导出excel 单个sheet
*/
public static String defaultExportOssUrl(List<?> list, Class<?> pojoClass, List<WriteHandler> handlers, String fileName, String sheetName, Map<String,String> vars) {
resetCLassAnnotationValue(pojoClass);
setExportClassAnnotation(pojoClass,vars);
String filePath = outputStream(fileName);
ExcelWriterSheetBuilder builder = EasyExcel.write(filePath,pojoClass)
.sheet(sheetName);
if(!ObjectUtils.isEmpty(handlers)){
for(WriteHandler handler : handlers){
builder.registerWriteHandler(handler);
}
}
builder.doWrite(list);
return getExcelOssUrl(filePath,fileName);
}
/**
* 默认导出excel 单个sheet 多个table
*/
public static String defaultExportOssUrl(EasyExcelMoreSheetMoreTableEntity entity, String fileName, Map<String,String> vars) {
String filePath = outputStream(fileName);
ExcelWriter excelWriter = EasyExcel.write(filePath).build();
String sheetName = entity.getSheetName();
List<WriteHandler> handlers = entity.getHandlers();
List<EasyExcelMoreSheetEntity> list = entity.getList();
try {
WriteSheet writeSheet = EasyExcel.writerSheet(0, sheetName).build();
for (int i = 0; i < list.size(); i++) {
EasyExcelMoreSheetEntity sheetEntity = list.get(i);
List date = sheetEntity.getList();
Class clazz = sheetEntity.getClazz();
resetCLassAnnotationValue(clazz);
setExportClassAnnotation(clazz,vars);
ExcelWriterTableBuilder tableBuilder = EasyExcel.writerTable(i);
if (!ObjectUtils.isEmpty(handlers)) {
for (WriteHandler handler : handlers) {
tableBuilder.registerWriteHandler(handler);
}
}
WriteTable table = tableBuilder.head(clazz).needHead(true).build();
excelWriter.write(date, writeSheet, table);
}
}catch (Exception e){
e.printStackTrace();
}finally {
excelWriter.finish();
}
return getExcelOssUrl(filePath,fileName);
}
/**
* 多个sheet页导出
*/
public static String moreSheetExportOssUrl(List<EasyExcelMoreSheetEntity> entities,String fileName){
String filePath = outputStream(fileName);
ExcelWriter excelWriter = EasyExcel.write(filePath).build();
try {
for (int i = 0; i < entities.size(); i++) {
EasyExcelMoreSheetEntity entity = entities.get(i);
Class clazz = entity.getClazz();
List list = entity.getList();
Map<String,String> vars = entity.getVars();
resetCLassAnnotationValue(clazz);
setExportClassAnnotation(clazz,vars);
String sheetName = entity.getSheetName();
List<WriteHandler> handlers = entity.getHandlers();
ExcelWriterSheetBuilder builder = EasyExcel.writerSheet(i, sheetName);
if(!ObjectUtils.isEmpty(handlers)){
for(WriteHandler handler :handlers){
builder.registerWriteHandler(handler);
}
}
WriteSheet writeSheet = builder.head(clazz).build();
excelWriter.write(list, writeSheet);
}
}catch (Exception e){
e.printStackTrace();
}finally {
excelWriter.finish();
}
return getExcelOssUrl(filePath,fileName);
}
@SuppressWarnings("unchecked")
public static String moreSheetMoreTableExportOssUrl(List<EasyExcelMoreSheetMoreTableEntity> entities,String fileName){
String filePath = outputStream(fileName);
ExcelWriter excelWriter = EasyExcel.write(filePath).build();
try {
for (int i = 0; i < entities.size(); i++) {
EasyExcelMoreSheetMoreTableEntity entity = entities.get(i);
List<EasyExcelMoreSheetEntity> list = entity.getList();
String sheetName = entity.getSheetName();
List<WriteHandler> handlers = entity.getHandlers();
ExcelWriterSheetBuilder sheetBuilder = EasyExcel.writerSheet(i, sheetName);
if(!ObjectUtils.isEmpty(handlers)){
for(WriteHandler handler :handlers){
sheetBuilder.registerWriteHandler(handler);
}
}
//创建sheet
WriteSheet writeSheet = sheetBuilder.build();
//创建table
Assert.isTrue(!ObjectUtils.isEmpty(list),"缺少table数据");
for(int j = 0 ; j < list.size() ; j++){
EasyExcelMoreSheetEntity tableEntity = list.get(j);
Map<String,String> vars = tableEntity.getVars();
List<?> date = tableEntity.getList();
Class<?> clazz = tableEntity.getClazz();
resetCLassAnnotationValue(clazz);
setExportClassAnnotation(clazz, vars);
ExcelWriterTableBuilder tableBuilder = EasyExcel.writerTable(j);
if(j > 0){
tableBuilder.relativeHeadRowIndex(2);
}
WriteTable table = tableBuilder.head(clazz).needHead(true).build();
excelWriter.write(date,writeSheet,table);
}
}
}catch (Exception e){
e.printStackTrace();
}finally {
excelWriter.finish();
}
return getExcelOssUrl(filePath,fileName);
}
public static void defaultExport(List<?> list, Class<?> pojoClass, String filePath, String sheetName) {
EasyExcel.write(filePath,pojoClass)
.sheet(sheetName)
.registerWriteHandler(new CustomCellWriteHandler())
.doWrite(list);
}
private static String getExcelOssUrl(String filePath,String fileName) {
InputStream in = null;
try{
//临时缓冲区
in = new FileInputStream(filePath);
} catch (Exception e){
e.printStackTrace();
}
// 此处可以调用腾讯云的cos 或者阿里云的oss todo
String url = "";
return url;
}
public static void setExportClassAnnotation(Class<?> clazz,Map<String,String> map){
Field[] fields = clazz.getDeclaredFields();
for(Field field : fields){
ExcelProperty property = field.getAnnotation(ExcelProperty.class);
if(property != null){
List<String> newValues = new ArrayList<>();
String[] values = property.value();
for(String value : values){
value = replace(value,map);
newValues.add(value);
}
InvocationHandler h = Proxy.getInvocationHandler(property);
try {
Field annotationField = h.getClass().getDeclaredField("memberValues");
annotationField.setAccessible(true);
Map memberValues = (Map) annotationField.get(h);
memberValues.put("value",newValues.toArray(new String[]{}));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private static void resetCLassAnnotationValue(Class<?> clazz){
String className = clazz.getSimpleName();
List<ExcelAnnotationValue> values = annotationValues.get(className);
if(ObjectUtils.isEmpty(values)){
//如果静态资源是空的,保存
Field[] fields = clazz.getDeclaredFields();
values = new ArrayList<>();
for(Field field : fields){
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
if(!ObjectUtils.isEmpty(excelProperty)) {
String[] vs = excelProperty.value();
ExcelAnnotationValue value = new ExcelAnnotationValue()
.setFieldName(field.getName())
.setValues(vs);
values.add(value);
}
}
annotationValues.put(className,values);
return;
}
Field[] fields = clazz.getDeclaredFields();
for(Field field : fields){
String fieldName = field.getName();
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
if(!ObjectUtils.isEmpty(excelProperty)){
ExcelAnnotationValue value = values.stream().filter(v->v.getFieldName().equals(fieldName)).findFirst().orElse(null);
if(!ObjectUtils.isEmpty(value)){
String[] oldValues = value.getValues();
InvocationHandler handler = Proxy.getInvocationHandler(excelProperty);
try {
Field annotationField = handler.getClass().getDeclaredField("memberValues");
annotationField.setAccessible(true);
Map memberValues = (Map) annotationField.get(handler);
memberValues.put("value",oldValues);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
public static String replace(String el,Map<String,String> map){
if(map == null){
return el;
}
String evl = el;
for(Map.Entry<String,String> m : map.entrySet()){
String key = m.getKey();
String value = m.getValue();
el = el.replaceAll("#\{"+key+"\}",value);
if(!evl.equals(el)) {
return el;
}
}
return el;
}
}
Конфигурация слияния таблиц
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import java.util.List;
/**
* 合并单元格handler
*
* @author zl
*/
public class CustomCellMergeStrategy implements CellWriteHandler {
private int[] mergeColumnIndex;
private int mergeRowIndex;
public CustomCellMergeStrategy() {
}
public CustomCellMergeStrategy(int mergeRowIndex, int[] mergeColumnIndex) {
this.mergeRowIndex = mergeRowIndex;
this.mergeColumnIndex = mergeColumnIndex;
}
@Override
public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {
}
@Override
public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
}
@Override
public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
}
@Override
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> list, Cell cell, Head head, Integer integer, Boolean aBoolean) {
int curRowIndex = cell.getRowIndex();
int curColIndex = cell.getColumnIndex();
if (curRowIndex > mergeRowIndex) {
for (int i = 0; i < mergeColumnIndex.length; i++) {
if (curColIndex == mergeColumnIndex[i]) {
mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex);
break;
}
}
}
}
/**
* 当前单元格向上合并
*
* @param writeSheetHolder
* @param cell 当前单元格
* @param curRowIndex 当前行
* @param curColIndex 当前列
*/
private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {
Object curData = cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex);
Object preData = preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue();
// 将当前单元格数据与上一个单元格数据比较
Boolean dataBool = preData.equals(curData);
//此处需要注意:因为我是按照序号确定是否需要合并的,所以获取每一行第一列数据和上一行第一列数据进行比较,如果相等合并
Boolean bool = cell.getRow().getCell(0).getStringCellValue().equals(cell.getSheet().getRow(curRowIndex - 1).getCell(0).getStringCellValue());
if (dataBool && bool) {
Sheet sheet = writeSheetHolder.getSheet();
List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
boolean isMerged = false;
for (int i = 0; i < mergeRegions.size() && !isMerged; i++) {
CellRangeAddress cellRangeAddr = mergeRegions.get(i);
// 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元
if (cellRangeAddr.isInRange(curRowIndex - 1, curColIndex)) {
sheet.removeMergedRegion(i);
cellRangeAddr.setLastRow(curRowIndex);
sheet.addMergedRegion(cellRangeAddr);
isMerged = true;
}
}
// 若上一个单元格未被合并,则新增合并单元
if (!isMerged) {
CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex, curColIndex);
sheet.addMergedRegion(cellRangeAddress);
}
}
}
}
высота стола по умолчанию
public class CustomCellWriteHandler extends AbstractRowHeightStyleStrategy {
@Override
protected void setHeadColumnHeight(Row row, int i) {
if(i == 0){
row.setHeight((short) (1000));
}else if(i == 1){
row.setHeight((short) 300);
}else{
row.setHeight((short) 500);
}
}
@Override
protected void setContentColumnHeight(Row row, int i) {
row.setHeight((short) 500);
}
}
@Data
@Accessors(chain = true)
public class ExcelAnnotationValue {
/**
* 字段名称
*/
private String fieldName;
/**
* ExcelProperty注解 属性value数组
*/
private String[] values;
}
Параметры многолистового объекта экспорта
@Data
@Accessors(chain = true)
public class EasyExcelMoreSheetEntity {
/**
* 实体类
*/
private Class<?> clazz;
/**
* 数据
*/
private List<?> list;
/**
* sheet名称
*/
private String sheetName;
/**
* 样式
*/
private List<WriteHandler> handlers;
/**
* head 参数
*/
private Map<String,String> vars;
}
Параметры объекта многостраничного экспорта
@Data
@Accessors(chain = true)
public class EasyExcelMoreSheetMoreTableEntity {
/**
* 数据
*/
private List<EasyExcelMoreSheetEntity> list;
/**
* sheet名称
*/
private String sheetName;
/**
* 样式
*/
private List<WriteHandler> handlers;
/**
* head 参数
*/
private Map<String,String> vars;
}
Ниже приведен простейший экспорт
ComplexSubjectEasyExcel excel = new ComplexSubjectEasyExcel()
.setSubjectId("1001")
.setSubjectName("库存现金")
.setFirstBorrowMoney(BigDecimal.valueOf(100))
.setNowBorrowMoney(BigDecimal.valueOf(105))
.setNowCreditMoney(BigDecimal.valueOf(100))
.setYearBorrowMoney(BigDecimal.valueOf(200))
.setYearCreditMoney(BigDecimal.valueOf(205))
.setEndBorrowMoney(BigDecimal.valueOf(240));
List<ComplexSubjectEasyExcel> excels = new ArrayList<>();
excels.add(excel);
String url = EasyExcelUtils.defaultExportOssUrl(excels,ComplexSubjectEasyExcel.class,"科目余额表","科目余额表",new HashMap<>());
System.out.println(url);
Экспорт нескольких листов
public void moreSheetTest(){
ComplexSubjectEasyExcel excel = new ComplexSubjectEasyExcel()
.setSubjectId("1001")
.setSubjectName("库存现金")
.setFirstBorrowMoney(BigDecimal.valueOf(100))
.setNowBorrowMoney(BigDecimal.valueOf(105))
.setNowCreditMoney(BigDecimal.valueOf(100))
.setYearBorrowMoney(BigDecimal.valueOf(200))
.setYearCreditMoney(BigDecimal.valueOf(205))
.setEndBorrowMoney(BigDecimal.valueOf(240));
List<ComplexSubjectEasyExcel> excels = new ArrayList<>();
excels.add(excel);
List<EasyExcelMoreSheetEntity> entities = new ArrayList<>();
for(int i=0 ; i< 2; i++){
EasyExcelMoreSheetEntity entity = new EasyExcelMoreSheetEntity()
.setClazz(ComplexSubjectEasyExcel.class)
.setList(excels)
.setSheetName("科目余额表"+i);
entities.add(entity);
}
String url = EasyExcelUtils.moreSheetExportOssUrl(entities,"科目余额表");
System.out.println(url);
}
Многостраничный, многостраничный экспорт
public void moreSheetMoreTableTest(){
ComplexSubjectEasyExcel excel = new ComplexSubjectEasyExcel()
.setSubjectId("1001")
.setSubjectName("库存现金")
.setFirstBorrowMoney(BigDecimal.valueOf(100))
.setNowBorrowMoney(BigDecimal.valueOf(105))
.setNowCreditMoney(BigDecimal.valueOf(100))
.setYearBorrowMoney(BigDecimal.valueOf(200))
.setYearCreditMoney(BigDecimal.valueOf(205))
.setEndBorrowMoney(BigDecimal.valueOf(240));
List<ComplexSubjectEasyExcel> excels = new ArrayList<>();
excels.add(excel);
List<EasyExcelMoreSheetMoreTableEntity> entities = new ArrayList<>();
for(int i=0 ; i< 2; i++){
EasyExcelMoreSheetMoreTableEntity tableEntity = new EasyExcelMoreSheetMoreTableEntity()
.setSheetName("科目余额表"+i)
.setHandlers(Arrays.asList(new CustomCellWriteHandler()));
List<EasyExcelMoreSheetEntity> tables = new ArrayList<>();
EasyExcelMoreSheetEntity table = new EasyExcelMoreSheetEntity()
.setClazz(ComplexSubjectEasyExcel.class)
.setList(excels);
if(i== 1){
tables.add(table);
}
tables.add(table);
tableEntity.setList(tables);
entities.add(tableEntity);
}
String url = EasyExcelUtils.moreSheetMoreTableExportOssUrl(entities,"科目余额表");
System.out.println(url);
}
Уже! После его использования вы оцените удобство и мощь easyExcel.