fix: 拦截非标准 XLSX 文件

- 在 POI 解析前识别旧版 XLS 与异常容器

- 返回可操作提示并补充回归测试
This commit is contained in:
2026-09-02 19:15:00 +08:00
parent 93e0ff2204
commit 4bc68ec7f7
2 changed files with 87 additions and 0 deletions

View File

@@ -14,6 +14,7 @@ import com.easyagents.document.core.entity.ParseRequest;
import com.easyagents.document.core.entity.ParseResponse;
import com.easyagents.document.core.entity.ParseResult;
import com.easyagents.document.core.entity.XlsxParseRequest;
import com.easyagents.document.core.exception.DocumentParseException;
import com.easyagents.document.core.support.AbstractAsyncDocumentParseService;
import com.easyagents.document.xlsx.XlsxDocumentProvider;
import com.easyagents.document.xlsx.model.XlsxCellArtifact;
@@ -52,6 +53,10 @@ import java.util.concurrent.Executors;
public class MineruXlsxDocumentParseService extends AbstractAsyncDocumentParseService<XlsxParseRequest> implements XlsxDocumentProvider {
public static final String PROVIDER_NAME = "mineru";
private static final byte[] OLE2_SIGNATURE = new byte[] {
(byte) 0xd0, (byte) 0xcf, 0x11, (byte) 0xe0,
(byte) 0xa1, (byte) 0xb1, 0x1a, (byte) 0xe1
};
private final MineruProperties properties;
private final MineruClient client;
@@ -145,6 +150,9 @@ public class MineruXlsxDocumentParseService extends AbstractAsyncDocumentParseSe
@Override
protected ParseResponse doParse(XlsxParseRequest request, DocumentAsyncTaskUpdater updater) {
for (ParseFile file : request.getFiles()) {
validateXlsxContent(file);
}
ParseResponse response = new ParseResponse();
List<ParseResult> results = new ArrayList<ParseResult>();
String backend = null;
@@ -211,6 +219,50 @@ public class MineruXlsxDocumentParseService extends AbstractAsyncDocumentParseSe
return aggregate;
}
/**
* 在 POI 打开工作簿前校验 XLSX 容器签名,避免向用户暴露底层格式异常。
*
* @param file 待解析文件
*/
private void validateXlsxContent(ParseFile file) {
byte[] content = file == null ? null : file.getContent();
if (hasZipSignature(content)) {
return;
}
String fileName = file == null || !StringUtil.hasText(file.getFileName())
? "当前文件"
: "文件“" + file.getFileName() + "";
String reason = startsWith(content, OLE2_SIGNATURE)
? "可能是旧版 XLS 或已加密文件"
: "文件内容与 .xlsx 扩展名不一致或文件已损坏";
throw new DocumentParseException(
fileName + "不是标准 XLSX" + reason
+ "。请解除保护后用 Excel/WPS 另存为 XLSX修改文件后缀无效"
);
}
private boolean hasZipSignature(byte[] content) {
return content != null
&& content.length >= 4
&& content[0] == 'P'
&& content[1] == 'K'
&& ((content[2] == 3 && content[3] == 4)
|| (content[2] == 5 && content[3] == 6)
|| (content[2] == 7 && content[3] == 8));
}
private boolean startsWith(byte[] content, byte[] signature) {
if (content == null || content.length < signature.length) {
return false;
}
for (int index = 0; index < signature.length; index++) {
if (content[index] != signature[index]) {
return false;
}
}
return true;
}
private SheetExtraction extractSheet(XSSFSheet sheet,
int sheetIndex,
DataFormatter formatter,