task-25: 店铺结果 workbook 的 SXSSF 流式写入路径
评估结论:SXSSF 无法访问/删除模板行,与现有模板重写流程不兼容, 故模板路径保留 XSSFWorkbook,另提供 writeWorkbookStreaming —— 空构造 SXSSFWorkbook + 自建 5 国工作表与表头,行数据按 rowAccessWindow spill 到磁盘, 写入后 dispose() 释放 spill 文件;图片嵌入复用现有链路,失败行兜底 URL 文本。 全量测试 548 通过。
This commit is contained in:
+57
-2
@@ -18,6 +18,7 @@ import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.xssf.usermodel.XSSFDrawing;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTTwoCellAnchor;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -141,6 +142,60 @@ public class ShopDataCrawlExcelAssemblyService {
|
||||
return rowsByCountry(items).values().stream().mapToInt(List::size).sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Task 25:SXSSF 流式写入路径。空构造 SXSSFWorkbook + 自建 5 个国家工作表与表头,
|
||||
* 数据行按 rowAccessWindow 数量 spill 到磁盘临时文件,写入后 dispose() 释放 spill 文件。
|
||||
* 模板路径(validateTemplate/样式/累计替换)保留 XSSFWorkbook 不变。
|
||||
* 返回实际写入的数据行数;图片下载失败的行兜底为 URL 文本,不中断整表。
|
||||
*/
|
||||
public int writeWorkbookStreaming(File outputXlsx, List<ShopDataCrawlResultItemVo> items, int rowAccessWindow) {
|
||||
if (outputXlsx == null) {
|
||||
throw new BusinessException("输出文件路径不能为空");
|
||||
}
|
||||
if (rowAccessWindow <= 0) {
|
||||
throw new IllegalArgumentException("rowAccessWindow 必须为正数,实际 " + rowAccessWindow);
|
||||
}
|
||||
if (items == null) {
|
||||
throw new IllegalArgumentException("items 不能为 null");
|
||||
}
|
||||
SXSSFWorkbook workbook = new SXSSFWorkbook(rowAccessWindow);
|
||||
try (FileOutputStream output = new FileOutputStream(outputXlsx)) {
|
||||
Map<String, List<ShopDataCrawlRowDto>> rowsByCountry = rowsByCountry(items);
|
||||
BoundedImageCache imageCache = newImageCache();
|
||||
prefetchImages(rowsByCountry, imageCache);
|
||||
Map<String, Integer> pictureIndexes = new LinkedHashMap<>();
|
||||
for (int i = 0; i < COUNTRIES.size(); i++) {
|
||||
writeStreamingSheet(workbook, i, rowsByCountry.get(COUNTRIES.get(i)), imageCache, pictureIndexes);
|
||||
}
|
||||
workbook.write(output);
|
||||
return rowsByCountry.values().stream().mapToInt(List::size).sum();
|
||||
} catch (BusinessException ex) {
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
throw new BusinessException("生成店铺数据抓取流式 Excel 失败: " + ex.getMessage());
|
||||
} finally {
|
||||
workbook.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeStreamingSheet(Workbook workbook,
|
||||
int index,
|
||||
List<ShopDataCrawlRowDto> rows,
|
||||
BoundedImageCache imageCache,
|
||||
Map<String, Integer> pictureIndexes) {
|
||||
Sheet sheet = workbook.createSheet(SHEETS.get(index));
|
||||
Row header = sheet.createRow(0);
|
||||
for (int column = 0; column < HEADERS.size(); column++) {
|
||||
header.createCell(column).setCellValue(HEADERS.get(column));
|
||||
}
|
||||
sheet.setColumnWidth(IMAGE_COLUMN, IMAGE_COLUMN_WIDTH);
|
||||
int rowIndex = 1;
|
||||
for (ShopDataCrawlRowDto value : rows == null ? List.<ShopDataCrawlRowDto>of() : rows) {
|
||||
Row row = sheet.createRow(rowIndex++);
|
||||
writeDataRow(workbook, sheet, row, value, new CellStyle[HEADERS.size()], imageCache, pictureIndexes);
|
||||
}
|
||||
}
|
||||
|
||||
void validateTemplate(XSSFWorkbook workbook) {
|
||||
if (workbook.getNumberOfSheets() != SHEETS.size()) {
|
||||
throw new BusinessException("店铺数据抓取模板工作表数量不正确");
|
||||
@@ -205,7 +260,7 @@ public class ShopDataCrawlExcelAssemblyService {
|
||||
}
|
||||
}
|
||||
|
||||
private void writeDataRow(XSSFWorkbook workbook,
|
||||
private void writeDataRow(Workbook workbook,
|
||||
Sheet sheet,
|
||||
Row row,
|
||||
ShopDataCrawlRowDto value,
|
||||
@@ -249,7 +304,7 @@ public class ShopDataCrawlExcelAssemblyService {
|
||||
return currentTemplate || outputColumn < IMAGE_COLUMN ? outputColumn : outputColumn - 1;
|
||||
}
|
||||
|
||||
private void embedImage(XSSFWorkbook workbook,
|
||||
private void embedImage(Workbook workbook,
|
||||
Sheet sheet,
|
||||
Row row,
|
||||
String imageUrl,
|
||||
|
||||
Reference in New Issue
Block a user