task-8: WorkbookFactory 输入解析改为受控读取,验证超大 Excel 失败提示
解析前用 ZipFile 中央目录探测 xlsx(zip) 条目数与解压总字节(不读压缩 内容),超过 maxWorkbookZipEntries(20000)/maxWorkbookUncompressedBytes (512MB) 立即拒绝并给出可识别提示;非 zip/损坏文件跳过探测交给 WorkbookFactory 兜底转业务异常。0/负值配置回退默认。新增 8 个测试覆盖 正常路径、多文件、幂等、空输入、单行、解压超限、条目超限、损坏文件、 依赖失败恢复。
This commit is contained in:
@@ -154,6 +154,17 @@ public class SimilarAsinProperties {
|
||||
*/
|
||||
private int maxFieldLength = 2000;
|
||||
|
||||
/**
|
||||
* Task 8:xlsx(zip) 最大条目数。受控读取在 WorkBookFactory 打开前探测,
|
||||
* 超过则拒绝,防止 zip bomb / 超大工作簿拖垮内存。
|
||||
*/
|
||||
private int maxWorkbookZipEntries = 20000;
|
||||
|
||||
/**
|
||||
* Task 8:xlsx(zip) 解压后总字节数上限。同样在打开前探测,超过则拒绝。
|
||||
*/
|
||||
private long maxWorkbookUncompressedBytes = 512L * 1024L * 1024L;
|
||||
|
||||
/**
|
||||
* P0-4:抢 Coze 提交锁失败后下次重试间隔(毫秒)。
|
||||
* 原硬编码 500ms,会在指数退避算法中作为基础值(500/1000/2000/4000ms 上限 4000)。
|
||||
|
||||
+61
-3
@@ -180,7 +180,7 @@ public class SimilarAsinTaskService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Task 7:单字段最大长度(字符)。0/负值回退默认 2000。
|
||||
* Task 8:单字段最大长度(字符)。0/负值回退默认 2000。
|
||||
*/
|
||||
private int resolveMaxFieldLength() {
|
||||
Integer configured = properties.getMaxFieldLength();
|
||||
@@ -189,6 +189,56 @@ public class SimilarAsinTaskService {
|
||||
}
|
||||
return configured;
|
||||
}
|
||||
|
||||
/**
|
||||
* Task 8:xlsx(zip) 最大条目数。0/负值回退默认 20000。
|
||||
*/
|
||||
private int resolveMaxWorkbookZipEntries() {
|
||||
Integer configured = properties.getMaxWorkbookZipEntries();
|
||||
if (configured == null || configured <= 0) {
|
||||
return 20000;
|
||||
}
|
||||
return configured;
|
||||
}
|
||||
|
||||
/**
|
||||
* Task 8:xlsx(zip) 解压后总字节数上限。0/负值回退默认 512MB。
|
||||
*/
|
||||
private long resolveMaxWorkbookUncompressedBytes() {
|
||||
Long configured = properties.getMaxWorkbookUncompressedBytes();
|
||||
if (configured == null || configured <= 0) {
|
||||
return 512L * 1024L * 1024L;
|
||||
}
|
||||
return configured;
|
||||
}
|
||||
|
||||
/**
|
||||
* Task 8:WorkbookFactory 打开前的受控读取。xlsx 本质是 zip,
|
||||
* 先用 ZipFile 中央目录探测条目数与解压总字节(不读取压缩内容),
|
||||
* 超限立即拒绝,避免 zip bomb / 超大工作簿直接进入全量加载。
|
||||
* 非 zip 文件(或损坏文件)由调用方 catch 转业务异常。
|
||||
*/
|
||||
private void probeWorkbookZipBounds(File input, String sourceName) throws Exception {
|
||||
int maxEntries = resolveMaxWorkbookZipEntries();
|
||||
long maxBytes = resolveMaxWorkbookUncompressedBytes();
|
||||
try (java.util.zip.ZipFile zipFile = new java.util.zip.ZipFile(input)) {
|
||||
if (zipFile.size() > maxEntries) {
|
||||
throw new BusinessException("Excel 条目数超过上限: " + zipFile.size() + " entries > " + maxEntries + " entries");
|
||||
}
|
||||
long total = 0;
|
||||
var entries = zipFile.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
var entry = entries.nextElement();
|
||||
long size = entry.getSize();
|
||||
if (size >= 0) {
|
||||
total += size;
|
||||
if (total > maxBytes) {
|
||||
throw new BusinessException("Excel 解压体积超过上限: " + total + " bytes > " + maxBytes + " bytes");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* P0-2 最小风险变体:poll 调度阶段并发预取 Coze HTTP 结果时,
|
||||
* 控制对单个 Coze 后端的并发度。8 与 cozeTaskExecutor 的 12 并发上限对齐留 4 余量,
|
||||
@@ -5268,8 +5318,16 @@ public class SimilarAsinTaskService {
|
||||
|
||||
private ParsedWorkbook parseWorkbook(File input, SimilarAsinSourceFileDto source) {
|
||||
DataFormatter formatter = new DataFormatter();
|
||||
try (FileInputStream fis = new FileInputStream(input); Workbook workbook = WorkbookFactory.create(fis)) {
|
||||
Sheet sheet = workbook.getSheetAt(0);
|
||||
try {
|
||||
// Task 8:受控读取——先探测 zip 条目数与解压体积,超限拒绝
|
||||
probeWorkbookZipBounds(input, source.getOriginalFilename());
|
||||
} catch (BusinessException ex) {
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
// 非 zip 或损坏文件:留给 WorkbookFactory 尝试后由下方 catch 转业务异常
|
||||
log.debug("[similar-asin] workbook zip probe skipped file={} err={}", input, ex.getMessage());
|
||||
}
|
||||
try (FileInputStream fis = new FileInputStream(input); Workbook workbook = WorkbookFactory.create(fis)) { Sheet sheet = workbook.getSheetAt(0);
|
||||
Row header = sheet.getRow(0);
|
||||
if (header == null) {
|
||||
throw new BusinessException("Excel 表头为空");
|
||||
|
||||
Reference in New Issue
Block a user