fix: 拦截非标准 XLSX 文件
- 在 POI 解析前识别旧版 XLS 与异常容器 - 返回可操作提示并补充回归测试
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user