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();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|