refactor(system): 重构系统模块中的导出功能实现
This commit is contained in:
+101
@@ -0,0 +1,101 @@
|
||||
package cn.odboy.util.xlsx;
|
||||
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.idev.excel.EasyExcel;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* EasyExcel 导出增强工具类 (支持普通/分页模式)
|
||||
*
|
||||
* @author https://mp.weixin.qq.com/s/gnrKCUzHg24sBSDp4IyAvg
|
||||
*/
|
||||
public class KitExcelExporter {
|
||||
|
||||
private static int calculateOptimalPageSize(Class<?> clazz) {
|
||||
// 1. 估算单条数据大小 (字节) - 根据业务模型调整逻辑
|
||||
// 保守估计500字节/行
|
||||
long approxBytesPerRow = 500;
|
||||
// 2. 获取当前JVM可用内存 (转成字节)
|
||||
long freeMemoryBytes = Runtime.getRuntime().freeMemory();
|
||||
// 3. 【安全策略】仅使用一部分可用内存 (例如 40%)
|
||||
long safeMemoryToUse = (long) (freeMemoryBytes * 0.4);
|
||||
// 4. 计算建议分页条数
|
||||
int suggestedPageSize = (int) (safeMemoryToUse / approxBytesPerRow);
|
||||
// 5. 设置合理范围 (防止太大或太小)
|
||||
// 限制在1000~10000条/页
|
||||
return Math.max(1000, Math.min(suggestedPageSize, 10000));
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页写入 (大数据量首选)
|
||||
*
|
||||
* @param response /
|
||||
* @param fileName 下载文件名
|
||||
* @param dataModel 数据类 (User.class)
|
||||
* @param totalCount 总条数
|
||||
* @param pageSupplier 分页查询逻辑
|
||||
*/
|
||||
public static <T> void exportByPage(HttpServletResponse response, String fileName, Class<T> dataModel, long totalCount, PageQuerySupplier<T> pageSupplier) {
|
||||
int pageSize = calculateOptimalPageSize(dataModel);
|
||||
String sheetName = DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN);
|
||||
setupResponse(response, fileName, sheetName);
|
||||
try (OutputStream out = response.getOutputStream()) {
|
||||
KitExcelPageWriteHelper.writeByPage(out, dataModel, sheetName, pageSize, totalCount, pageSupplier);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("导出失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 普通导出 (小数据量)
|
||||
*
|
||||
* @param response /
|
||||
* @param fileName 下载文件名
|
||||
* @param dataModel 数据类 (User.class)
|
||||
* @param dataList 全量数据List
|
||||
*/
|
||||
public static <T> void exportSimple(HttpServletResponse response, String fileName, Class<T> dataModel, List<T> dataList) {
|
||||
String sheetName = DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN);
|
||||
setupResponse(response, fileName, sheetName);
|
||||
try (OutputStream out = response.getOutputStream()) {
|
||||
EasyExcel.write(out, dataModel)
|
||||
.sheet(sheetName)
|
||||
// 全量写入
|
||||
.doWrite(dataList);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("导出失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置响应头
|
||||
*/
|
||||
private static void setupResponse(HttpServletResponse response, String title, String sheetName) {
|
||||
try {
|
||||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
response.setCharacterEncoding("utf-8");
|
||||
String fileName = URLEncoder.encode(sheetName + "_" + title, StandardCharsets.UTF_8);
|
||||
response.setHeader("Content-disposition",
|
||||
"attachment;filename*=utf-8''" + fileName + ".xlsx");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("设置响应头失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询供应商
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface PageQuerySupplier<T> {
|
||||
|
||||
List<T> getPage(long pageNum, long pageSize); // 函数式接口
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package cn.odboy.util.xlsx;
|
||||
|
||||
import cn.idev.excel.EasyExcel;
|
||||
import cn.idev.excel.ExcelWriter;
|
||||
import cn.idev.excel.write.metadata.WriteSheet;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 分页写入Excel工具 - 专治各种不服(OOM)
|
||||
*/
|
||||
public class KitExcelPageWriteHelper {
|
||||
|
||||
/**
|
||||
* 执行分页写入
|
||||
*
|
||||
* @param outputStream 输出流 (响应OutputStream)
|
||||
* @param head 数据模型Class (如 User.class)
|
||||
* @param sheetName 表格名称
|
||||
* @param pageSize 【重要】每批次处理条数 (建议 1000~5000)
|
||||
* @param totalCount 总数据量 (用于计算总页数)
|
||||
* @param supplier 分页数据提供器 (你的业务查询逻辑)
|
||||
*/
|
||||
public static <T> void writeByPage(OutputStream outputStream, Class<T> head, String sheetName, long pageSize, long totalCount, KitExcelExporter.PageQuerySupplier<T> supplier) {
|
||||
try (ExcelWriter excelWriter = EasyExcel.write(outputStream, head).build()) {
|
||||
// 默认Sheet
|
||||
WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();
|
||||
int totalPage = totalCount > 0 ? (int) Math.ceil((double) totalCount / pageSize) : 1;
|
||||
for (int pageNum = 1; pageNum <= totalPage; pageNum++) {
|
||||
List<T> pageData = supplier.getPage(pageNum, pageSize);
|
||||
excelWriter.write(pageData, writeSheet);
|
||||
pageData.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -6,13 +6,13 @@ import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.idev.excel.FastExcel;
|
||||
import cn.odboy.framework.exception.BadRequestException;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user