pom引用
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
package com.manager.util;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.manager.dto.productnetvalue.excel.ProductNetvalueExcelExportDto;
import com.manager.dto.productnetvalue.req.ProductNetvalueExportReq;
import com.manager.exception.ServiceException;
import com.manager.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFCell;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* @author
* @date 2025/03/27 15:11
**/
@Slf4j
public class ProductNetValueExportExcelUtil {
private static final String PATTERN = "yyyyMMddHHmmss";
static DateTimeFormatter dtf = DateTimeFormatter.ofPattern(PATTERN);
public static void exportExcel(List<ProductNetvalueExportReq.exportField> exportFields, HttpServletResponse response
, List<ProductNetvalueExcelExportDto> results,String name) throws IOException {
String format = dtf.format(LocalDateTime.now());
String fileName = ProductNetvalueExcelExportDto.SHEET_NAME.concat("-").concat(format);
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
// 导出 Excel 文件
try (SXSSFWorkbook wb = new SXSSFWorkbook()){
buildExcel(exportFields,results,wb);
wb.write(response.getOutputStream());
}
}
public static void buildExcel(List<ProductNetvalueExportReq.exportField> exportFields
, List<ProductNetvalueExcelExportDto> results, SXSSFWorkbook wb) throws IOException {
SXSSFSheet sheet = wb.createSheet(ProductNetvalueExcelExportDto.SHEET_NAME);
//获取要展示的列名
if (CollectionUtils.isEmpty(exportFields)) {
throw new ServiceException("没有设置展示的列!");
}
// 创建表头样式
CellStyle headerStyle = createHeaderStyle(wb);
// 创建表头行
SXSSFRow headerRow = sheet.createRow(0);
//列宽
Map<Integer, Integer> maxWidth = new HashMap<>();
int colIndex = 0;
List<String> columns = new ArrayList<>();
for (ProductNetvalueExportReq.exportField field : exportFields) {
SXSSFCell cell = headerRow.createCell(colIndex);
cell.setCellValue(field.getFieldName());
cell.setCellStyle(headerStyle);
columns.add(field.getFieldKey());
// 初始化列宽(表头宽度)
int headerWidth = field.getFieldName().getBytes().length * 256 + 200;
maxWidth.put(colIndex, headerWidth);
colIndex++;
}
// 填充表格数据
for (int rowIndex = 0; rowIndex < results.size(); rowIndex++) {
SXSSFRow row = sheet.createRow(rowIndex + 1);
ProductNetvalueExcelExportDto dto = results.get(rowIndex);
for (int j = 0; j < columns.size(); j++) {
String fieldKey = columns.get(j);
Object value = getFieldValue(dto, fieldKey); // 通过反射或Getter方法获取值
SXSSFCell cell = row.createCell(j);
if (value != null) {
String cellValue = formatValue(fieldKey, value); // 格式化特定字段(如日期)
cell.setCellValue(cellValue);
// 计算列宽(按字符数估算)
int cellWidth = cellValue.length() * 320;
if (cellWidth > maxWidth.get(j)) {
maxWidth.put(j, cellWidth);
}
}
}
}
for (int j = 0; j < columns.size(); j++) {
int colWidth = maxWidth.get(j) * 2;
if (colWidth < 255 * 256) {
sheet.setColumnWidth(j, (maxWidth.get(j)) < 2000 ? 2000 : colWidth);
} else {
sheet.setColumnWidth(j, 4000);
}
}
}
private static CellStyle createHeaderStyle(SXSSFWorkbook wb) {
Font titleFont = wb.createFont();
titleFont.setBold(true);
titleFont.setFontHeightInPoints((short) 12);
titleFont.setColor(IndexedColors.WHITE.getIndex());
titleFont.setFontName("微软雅黑");
CellStyle headerStyle = wb.createCellStyle();
headerStyle.setFont(titleFont);
headerStyle.setAlignment(HorizontalAlignment.CENTER);
headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
headerStyle.setFillForegroundColor(IndexedColors.SKY_BLUE.getIndex());
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
return headerStyle;
}
// 辅助方法:通过反射获取字段值
private static Object getFieldValue(ProductNetvalueExcelExportDto dto, String fieldKey) {
try {
Field field = ProductNetvalueExcelExportDto.class.getDeclaredField(fieldKey);
field.setAccessible(true);
return field.get(dto);
} catch (Exception e) {
throw new ServiceException("字段访问失败: " + fieldKey, e);
}
}
// 辅助方法:格式化值(如日期)
private static String formatValue(String fieldKey, Object value) {
if ("netDate".equals(fieldKey)) {
return DateUtils.format(((Date) value), DateUtils.FORMAT_LONG2);
}
return value.toString();
}
public static void exportExcelZip(List<ProductNetvalueExportReq.exportField> exportFields
, HttpServletResponse response,Map<String, List<ProductNetvalueExcelExportDto>> map) throws IOException{
// 用于存放文件路径
List<File> excelFiles = new ArrayList<>();
// 设置ZIP响应格式
response.setContentType("application/zip");
String format = dtf.format(LocalDateTime.now());
String fileName = URLEncoder.encode(ProductNetvalueExcelExportDto.SHEET_NAME.concat("-").concat(format), "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + fileName + ".zip");
try {
for (String productName : map.keySet()) {
// 1. 根据产品ID获取数据
List<ProductNetvalueExcelExportDto> productData = map.get(productName);
if (CollectionUtils.isEmpty(productData)){
return;
}
// 生成文件名
String excelName = productName
.concat("-").concat("净值信息")
.concat("-").concat(format)
.concat(ExcelTypeEnum.XLSX.getValue());
File excelFile = creatFile(excelName);
// 保留100行在内存
try (SXSSFWorkbook wb = new SXSSFWorkbook(100);
FileOutputStream fos = new FileOutputStream(excelFile)) {
buildExcel(exportFields, productData, wb);
wb.write(fos);
excelFiles.add(excelFile);
// 清理临时文件(SXSSF生成的后备存储)
wb.dispose();
}catch (IOException e){
log.error("文件导出打包出错", e);
// 异常时清理临时文件
cleanupTempFiles(excelFiles);
}
}
try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
for (File excelFile : excelFiles) {
ZipEntry entry = new ZipEntry(excelFile.getName());
zipOut.putNextEntry(entry);
Files.copy(excelFile.toPath(), zipOut);
zipOut.closeEntry();
}
}
}catch (IOException e) {
log.error("文件下载出错", e);
// 异常时清理临时文件
cleanupTempFiles(excelFiles);
}finally {
// 确保最终清理
cleanupTempFiles(excelFiles);
}
}
//创建文件File对象
private static File creatFile(String filePath) {
return new File(filePath);
}
// 清理方法
private static void cleanupTempFiles(List<File> files) {
for (File file : files) {
if (file.exists() && !file.delete()) {
log.warn("临时文件删除失败: {}", file.getAbsolutePath());
}
}
// 清空列表引用
files.clear();
}
}
这里表述一个生产遇到的问题,最开始使用的时XSSFWorkbook,在生产遇到了当用户导出10W数据时,把服务器打挂了,后来排查发现数据量太大,导致OOM了。
问题:大内存占用风险(非内存泄漏,但可能导致OOM)
XSSFWorkbook会将整个Excel数据加载到内存,若map中的产品数据量极大,生成多个Excel时可能触发OutOfMemoryError。
修复方案:
改用流式Excel库(如SXSSFWorkbook),逐行写入数据,减少内存占用.
扩展知识:
几种Workbook格式
POI中提供了很多种WorkBook API来操作Excel,有的适合大文件读写,有的
不适合。
SSFWorkbook
用于处理 Excel的 .xls格式(即 Excel 97-2003)。
XSSFWorkbook
用于处理 Excel的.xlsx 格式(即 Excel 2007 及以后版本的)。
支持更大的数据集和更多的功能,如更好的样式和公式支持。但是相对于
HSSFWorkbook,它在处理大数据集时可能占用更多内存。
SXSSFWorkbook
用于处理 .xlsx 格式。它是 XSSFWorkbook 的流式版本,专门设计用于
处理大数据集。通过将数据写入临时文件而非全部保留在内存中,显著减
少内存消耗。特别适合用于创建大型数据集的 Excel 文件。
【代码】10W+ 数据导出excel,根据表头动态导出单个excel,多个excel到处压缩包
于 2025-04-10 16:59:40 首次发布
465

被折叠的 条评论
为什么被折叠?



