
com.github.joekerouac.common.tools.poi.ExcelWriter Maven / Gradle / Ivy
The newest version!
// Generated by delombok at Fri Mar 14 11:41:38 CST 2025
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
* file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
* to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.github.joekerouac.common.tools.poi;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import com.github.joekerouac.common.tools.collection.CollectionUtil;
import com.github.joekerouac.common.tools.exception.ExcelClosedException;
import com.github.joekerouac.common.tools.poi.data.BooleanDataWriter;
import com.github.joekerouac.common.tools.poi.data.CalendarDataWriter;
import com.github.joekerouac.common.tools.poi.data.DateDataWriter;
import com.github.joekerouac.common.tools.poi.data.EnumDataWriter;
import com.github.joekerouac.common.tools.poi.data.NumberDataWriter;
import com.github.joekerouac.common.tools.poi.data.StringDataWriter;
import com.github.joekerouac.common.tools.reflect.ReflectUtil;
import com.github.joekerouac.common.tools.string.StringUtils;
/**
* Excel写出工具,将数据写入excel,用户可以注册自己的excel单元格数据类型处理器{@link ExcelDataWriter ExcelDataWriter}来做
* 一些特殊处理,例如添加单元格样式等,系统默认会注册Date、Calendar、String、Number、Boolean、Enum类型的数据处理器。
*
* @author JoeKerouac
* @date 2023-01-12 19:35
* @since 2.0.0
*/
public class ExcelWriter {
@java.lang.SuppressWarnings("all")
@lombok.Generated
private static final com.github.joekerouac.common.tools.log.Logger LOGGER = com.github.joekerouac.common.tools.log.LoggerFactory.getLogger(ExcelWriter.class.getName());
private static final String DEFAULT_SHEET_NAME = "Sheet0";
/**
* 默认内存中最多多少行单元格
*/
private static final int IN_MEMORY = 100;
/**
* 排序器
*/
private static final Comparator COMPARATOR;
/**
* 全局默认的Excel单元格数据类型
*/
private static final Map, ExcelDataWriter>> DEFAULT_WRITERS;
static {
Map, ExcelDataWriter>> writers = new HashMap<>();
writers.put(Boolean.class, new BooleanDataWriter());
writers.put(Calendar.class, new CalendarDataWriter());
writers.put(Date.class, new DateDataWriter());
writers.put(Enum.class, new EnumDataWriter());
writers.put(Number.class, new NumberDataWriter());
writers.put(String.class, new StringDataWriter());
DEFAULT_WRITERS = Collections.unmodifiableMap(writers);
COMPARATOR = (f1, f2) -> {
ExcelColumn c1 = f1.getAnnotation(ExcelColumn.class);
ExcelColumn c2 = f2.getAnnotation(ExcelColumn.class);
if (c1 == null && c2 == null) {
return f1.getName().compareTo(f2.getName());
}
if (c1 == null) {
return 1;
}
if (c2 == null) {
return -1;
}
return c1.sort() - c2.sort();
};
}
/**
* 所有的Excel单元格数据类型
*/
private final Map, ExcelDataWriter>> writers = new ConcurrentHashMap<>();
/**
* 是否写出标题,如果为true,则每个sheet写入第一行数据前都会先写出标题
*/
private final boolean hasTitle;
/**
* 是否横向写入(一列对应一个pojo,标题在第一列),默认false(一行一个pojo,标题在第一行)
*/
private final boolean transverse;
/**
* work book
*/
private final Workbook wb;
/**
* 标志位,false表示无法写出
*/
private boolean flag;
/**
* 构建器
*/
public ExcelWriter() {
this(IN_MEMORY, true, false);
}
/**
* 构建器
*
* @param inMemory
* 最多保留在内存中多少行
*/
public ExcelWriter(int inMemory) {
this(inMemory, true, false);
}
/**
* 构建器
*
* @param inMemory
* 最多保留在内存中多少行
* @param hasTitle
* 是否写出标题,全局配置,后续还可以分别指定
*/
public ExcelWriter(int inMemory, boolean hasTitle) {
this(inMemory, hasTitle, false);
}
/**
* 构建器
*
* @param inMemory
* 最多保留在内存中多少行
* @param hasTitle
* 是否写出标题,全局配置,后续还可以分别指定
* @param transverse
* 默认数据是按行写的,是否将数据按列写出,true表示按列写出(即竖着写)
*/
public ExcelWriter(int inMemory, boolean hasTitle, boolean transverse) {
this.hasTitle = hasTitle;
this.transverse = transverse;
this.wb = new SXSSFWorkbook(inMemory);
this.flag = true;
}
/**
* 将excel写出到指定输出流;
*
* 注意:写出后输出流将会被关闭,同时excel也会被关闭,无法再写入数据
*
* @param outputStream
* 输出流
* @throws IOException
* IO异常
*/
public synchronized void write(OutputStream outputStream) throws IOException {
if (!flag) {
throw new ExcelClosedException("excel closed, can not write");
}
flag = false;
wb.write(outputStream);
if (wb instanceof SXSSFWorkbook) {
((SXSSFWorkbook) wb).dispose();
}
wb.close();
}
/**
* 注册一个新的excel单元格DataWriter(如果原来存在那么将会覆盖原来的DataWriter)
*
* @param type
* DataWriter处理的数据类型的Class
* @param writer
* DataWriter
* @param
* DataWriter处理的数据类型
* @return 如果原来存在该类型的DataWriter那么返回原来的DataWriter
*/
public ExcelDataWriter> registerDataWriter(Class type, ExcelDataWriter writer) {
if (writer != null) {
return writers.put(type, writer);
} else {
return null;
}
}
/**
* 当前系统是否包含指定类型的DataWriter
*
* @param type
* DataWriter对应的数据类型的Class
* @param
* DataWriter对应的数据类型
* @return 返回true表示包含
*/
public boolean containsDataWriter(Class type) {
return writers.containsKey(type);
}
/**
* 将pojo集合写入excel
*
* @param dataList
* pojo集合,空元素将被忽略,集合中必须是都是同种对象
*/
public void writeToExcel(List dataList) {
writeToExcel(dataList, hasTitle, transverse, DEFAULT_SHEET_NAME);
}
/**
* 将pojo集合写入excel
*
* @param dataList
* pojo集合,空元素将被忽略,集合中必须是都是同种对象
* @param sheetName
* 数据要写入的sheet
*/
public void writeToExcel(List dataList, String sheetName) {
writeToExcel(dataList, hasTitle, transverse, sheetName);
}
/**
* 将pojo集合写入excel
*
* @param dataList
* pojo集合,空元素将被忽略,集合中必须是都是同种对象
* @param hasTitle
* 是否写入title
*/
public void writeToExcel(List dataList, boolean hasTitle) {
writeToExcel(dataList, hasTitle, transverse, DEFAULT_SHEET_NAME);
}
/**
* 将pojo集合写入excel
*
* @param dataList
* pojo集合,空元素将被忽略,集合中必须是都是同种对象
* @param hasTitle
* 是否写入title
* @param transverse
* 是否横向写入(一列对应一个pojo,标题在第一列),默认false(一行一个pojo,标题在第一行)
*/
public void writeToExcel(List dataList, boolean hasTitle, boolean transverse) {
writeToExcel(dataList, hasTitle, transverse, DEFAULT_SHEET_NAME);
}
/**
* 将pojo集合写入excel
*
* @param dataList
* pojo集合,空元素将被忽略,集合中必须是都是同种对象
* @param hasTitle
* 是否写入title
* @param transverse
* 是否横向写入(一列对应一个pojo,标题在第一列),默认false(一行一个pojo,标题在第一行)
* @param sheetName
* sheetName
*/
public void writeToExcel(List dataList, boolean hasTitle, boolean transverse, String sheetName) {
if (CollectionUtil.isEmpty(dataList)) {
LOGGER.warn("给定数据集合为空");
return;
}
List newDataList = dataList.parallelStream().filter(Objects::nonNull).collect(Collectors.toList());
if (newDataList.isEmpty()) {
LOGGER.warn("给定数据集合里的数据全是空");
return;
}
// 获取所有字段(包括父类的)
Field[] fields = ReflectUtil.getAllFields(newDataList.get(0).getClass());
// 过滤可以写入的字段
List writeFields = new ArrayList<>();
for (Field field : fields) {
String name = field.getName();
Class> type = field.getType();
if (decide(type) == null) {
LOGGER.debug("字段[{}]不能写入", name);
} else {
ExcelColumn column = field.getAnnotation(ExcelColumn.class);
if (column == null || !column.ignore()) {
writeFields.add(field);
}
}
}
LOGGER.debug("可写入excel的字段集合为:[{}],对可写入excel的字段集合排序", writeFields);
writeFields.sort(COMPARATOR);
List> titles = null;
if (hasTitle) {
LOGGER.debug("当前需要标题列表,构建...");
titles = new ArrayList<>(writeFields.size());
for (Field field : writeFields) {
ExcelColumn column = field.getAnnotation(ExcelColumn.class);
if (column == null || StringUtils.isBlank(column.value())) {
titles.add(build(field.getName()));
} else {
titles.add(build(column.value()));
}
}
}
List>> writeDatas = new ArrayList<>(newDataList.size());
for (Object dataValue : newDataList) {
// 构建一行数据
List> columnDatas = new ArrayList<>(writeFields.size());
// 加入
writeDatas.add(columnDatas);
for (Field field : writeFields) {
try {
Object value = field.get(dataValue);
columnDatas.add(build(value));
} catch (IllegalAccessException e) {
LOGGER.warn("[{}]中字段[{}]不能读取", dataValue, field.getName(), e);
columnDatas.add(null);
}
}
}
LOGGER.debug("要写入的数据为:[{}]", writeDatas);
writeToExcel(titles, writeDatas, hasTitle, transverse, sheetName);
}
/**
* 写出到excel,写出字段顺序按照数据列表中第一行数据的字段顺序,如果写出标题,并且某个字段的标题不存在,那么将使用字段名作为标题
*
* @param titleMap
* 标题,key是数据字段名,value是对应的标题名,为null表示不写出标题
* @param dataList
* 数据列表,每个map是一行数据,所有map结构应该一致,如果写出需要顺序,则需要使用有序map,key是字段名,value是字段值; sheetName
*/
public void writeToExcel(Map titleMap, List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy