198 lines
7.8 KiB
Java
198 lines
7.8 KiB
Java
package com.ruoyi.cms.util;
|
||
|
||
import com.alibaba.excel.EasyExcel;
|
||
import com.alibaba.excel.context.AnalysisContext;
|
||
import com.alibaba.excel.event.AnalysisEventListener;
|
||
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
|
||
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
|
||
import com.alibaba.excel.write.metadata.style.WriteFont;
|
||
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
|
||
import org.apache.poi.ss.usermodel.HorizontalAlignment;
|
||
import org.apache.poi.ss.usermodel.VerticalAlignment;
|
||
|
||
import javax.servlet.http.HttpServletResponse;
|
||
import java.io.*;
|
||
import java.net.URLEncoder;
|
||
import java.util.List;
|
||
import java.util.Map;
|
||
import java.util.Objects;
|
||
import java.util.function.Consumer;
|
||
|
||
/**
|
||
* EasyExcel 工具类(基于 alibaba easyexcel 3.x+)
|
||
*/
|
||
public class EasyExcelUtils {
|
||
|
||
/**
|
||
* 读取 Excel 文件(一次性读取所有数据)
|
||
*
|
||
* @param file 上传的 Excel 文件
|
||
* @param head 实体类字节码(需使用 @ExcelProperty 注解映射表头)
|
||
* @param <T> 实体类泛型
|
||
* @return 解析后的数据集
|
||
*/
|
||
public static <T> List<T> readExcel(File file, Class<T> head) {
|
||
return EasyExcel.read(file)
|
||
.head(head)
|
||
.sheet()
|
||
.doReadSync();
|
||
}
|
||
|
||
/**
|
||
* 读取 Excel 输入流(一次性读取所有数据)
|
||
*
|
||
* @param inputStream Excel 输入流(如 MultipartFile 的 getInputStream())
|
||
* @param head 实体类字节码
|
||
* @param <T> 实体类泛型
|
||
* @return 解析后的数据集
|
||
*/
|
||
public static <T> List<T> readExcel(InputStream inputStream, Class<T> head) {
|
||
return EasyExcel.read(inputStream)
|
||
.head(head)
|
||
.sheet()
|
||
.doReadSync();
|
||
}
|
||
|
||
/**
|
||
* 分批读取 Excel(适用于大数据量,避免内存溢出)
|
||
*
|
||
* @param inputStream Excel 输入流
|
||
* @param head 实体类字节码
|
||
* @param batchSize 每批处理的数据量
|
||
* @param consumer 数据处理函数(如批量保存到数据库)
|
||
* @param <T> 实体类泛型
|
||
*/
|
||
public static <T> void readExcelByBatch(InputStream inputStream, Class<T> head, int batchSize, Consumer<List<T>> consumer) {
|
||
EasyExcel.read(inputStream)
|
||
.head(head)
|
||
.sheet()
|
||
.registerReadListener(new AnalysisEventListener<T>() {
|
||
private List<T> batchList; // 临时存储批数据
|
||
|
||
@Override
|
||
public void invoke(T data, AnalysisContext context) {
|
||
if (batchList == null) {
|
||
batchList = new java.util.ArrayList<>(batchSize);
|
||
}
|
||
batchList.add(data);
|
||
// 达到批处理量时执行消费逻辑
|
||
if (batchList.size() >= batchSize) {
|
||
consumer.accept(batchList);
|
||
batchList.clear(); // 清空集合,释放内存
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void doAfterAllAnalysed(AnalysisContext context) {
|
||
// 处理剩余不足一批的数据
|
||
if (batchList != null && !batchList.isEmpty()) {
|
||
consumer.accept(batchList);
|
||
}
|
||
}
|
||
})
|
||
.doRead();
|
||
}
|
||
|
||
/**
|
||
* 生成 Excel 并写入到输出流(通用样式)
|
||
*
|
||
* @param outputStream 输出流(如 HttpServletResponse 的 getOutputStream())
|
||
* @param data 数据集
|
||
* @param head 实体类字节码
|
||
* @param sheetName 工作表名称
|
||
* @param <T> 实体类泛型
|
||
*/
|
||
public static <T> void writeExcel(OutputStream outputStream, List<T> data, Class<T> head, String sheetName) {
|
||
// 构建通用样式策略(表头居中加粗,内容居中)
|
||
HorizontalCellStyleStrategy styleStrategy = getDefaultStyleStrategy();
|
||
|
||
ExcelWriterSheetBuilder writerBuilder = EasyExcel.write(outputStream, head)
|
||
.sheet(sheetName)
|
||
.registerWriteHandler(styleStrategy);
|
||
|
||
writerBuilder.doWrite(data);
|
||
}
|
||
|
||
/**
|
||
* 生成 Excel 并通过 HttpServletResponse 下载(前端直接触发下载)
|
||
*
|
||
* @param response HttpServletResponse
|
||
* @param data 数据集
|
||
* @param head 实体类字节码
|
||
* @param sheetName 工作表名称
|
||
* @param fileName 下载的文件名(不带后缀)
|
||
* @param <T> 实体类泛型
|
||
* @throws IOException IO异常
|
||
*/
|
||
public static <T> void downloadExcel(HttpServletResponse response, List<T> data, Class<T> head,
|
||
String sheetName, String fileName) throws IOException {
|
||
// 设置响应头,触发前端下载
|
||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||
response.setCharacterEncoding("UTF-8");
|
||
// 文件名编码,避免中文乱码
|
||
String encodedFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
|
||
response.setHeader("Content-disposition", "attachment;filename*=UTF-8''" + encodedFileName + ".xlsx");
|
||
|
||
// 写入数据到响应流
|
||
writeExcel(response.getOutputStream(), data, head, sheetName);
|
||
}
|
||
|
||
/**
|
||
* 填充 Excel 模板(适用于带固定格式的模板文件)
|
||
*
|
||
* @param templateInputStream 模板文件输入流
|
||
* @param outputStream 输出流(如响应流或文件流)
|
||
* @param dataMap 填充数据(key为模板中的占位符,value为填充值)
|
||
* @param sheetName 工作表名称
|
||
*/
|
||
public static void fillTemplate(InputStream templateInputStream, OutputStream outputStream,
|
||
Map<String, Object> dataMap, String sheetName) {
|
||
EasyExcel.write(outputStream)
|
||
.withTemplate(templateInputStream)
|
||
.sheet(sheetName)
|
||
.doFill(dataMap);
|
||
}
|
||
|
||
/**
|
||
* 获取默认单元格样式策略(表头加粗居中,内容居中)
|
||
*/
|
||
private static HorizontalCellStyleStrategy getDefaultStyleStrategy() {
|
||
// 表头样式
|
||
WriteCellStyle headStyle = new WriteCellStyle();
|
||
WriteFont headFont = new WriteFont();
|
||
headFont.setBold(true); // 加粗
|
||
headFont.setFontHeightInPoints((short) 11);
|
||
headStyle.setWriteFont(headFont);
|
||
headStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); // 水平居中
|
||
headStyle.setVerticalAlignment(VerticalAlignment.CENTER); // 垂直居中
|
||
|
||
// 内容样式
|
||
WriteCellStyle contentStyle = new WriteCellStyle();
|
||
WriteFont contentFont = new WriteFont();
|
||
contentFont.setFontHeightInPoints((short) 11);
|
||
contentStyle.setWriteFont(contentFont);
|
||
contentStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
|
||
contentStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
||
|
||
// 返回样式策略
|
||
return new HorizontalCellStyleStrategy(headStyle, contentStyle);
|
||
}
|
||
|
||
/**
|
||
* 关闭流(工具类内部使用)
|
||
*/
|
||
private static void closeStream(Closeable... closeables) {
|
||
if (closeables != null) {
|
||
for (Closeable closeable : closeables) {
|
||
if (Objects.nonNull(closeable)) {
|
||
try {
|
||
closeable.close();
|
||
} catch (IOException e) {
|
||
// 日志记录(建议替换为实际项目的日志框架)
|
||
e.printStackTrace();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} |