使用poi读取一些excel失败,遇到这样的错误
Caused by: org.xml.sax.SAXParseException; systemId: file://; lineNumber: 105; columnNumber: 147342; An invalid XML character (Unicode: 0xffff) was found in the element content of the document.
at java.xml/com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:204)
at java.xml/com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.fatalError(ErrorHandlerWrapper.java:178)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:400)
从xl/sharedStrings.xml
来看,存在导致该问题的<ffff>
。
怎么能成功地读取它而忽略这些无效字符呢?例如
aaa <ffff> bbb ==> aaa bbb
这些无效字符不应该在XML中,Excel本身也不会将它们放入其中。因此,可能有人在使用Excel以外的其他工具创建该文件时出错了。应该避免这种错误,而不是试图忽视症状。
但我知道在遥远的未来,如果有的话,对其他人的工作感到不快是什么感觉。所以一个人需要即兴发挥。但在这种情况下,只有使用丑陋的低级别方法才能做到这一点。因为XML无效,所以无法解析XML。因此,只有字符串替换是可能的。
在APACHE POI EXCEL XmlException中:是一个无效的XML字符,有什么方法可以预处理EXCEL文件吗?我已经学会了。在这种情况下,要替换在XML中也无效的UTF-16代理对空数字字符引用。
在下面的代码中,我将展示一个更灵活的代码,可以在必要时向/xl/sharedStrings.xml
添加多个其他修复操作。
其原理是使用OPCPackage
,即*.xlsx
ZIP包,来获取作为文本字符串的/xl/sharedStrings.xml
。然后进行所需的更换,并将修复后的/xl/sharedStrings.xml
放回OPCPackage
中。然后从修复的OPCPackage
而不是从损坏的文件创建XSSFWorkbook
。
import java.io.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.openxml4j.opc.*;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
class RepairSharedStringsTable {
static String removeInvalidXmlCharacters(String string) {
String xml10pattern = "[^"
+ "u0009rn"
+ "u0020-uD7FF"
+ "uE000-uFFFD"
+ "ud800udc00-udbffudfff"
+ "]";
string = string.replaceAll(xml10pattern, "");
return string;
}
static void repairSharedStringsTable(OPCPackage opcPackage) {
for (PackagePart packagePart : opcPackage.getPartsByName(Pattern.compile("/xl/sharedStrings.xml"))) {
String sharedStrings = "";
try (BufferedInputStream inputStream = new BufferedInputStream(packagePart.getInputStream());
ByteArrayOutputStream sharedStringsBytes = new ByteArrayOutputStream() ) {
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) != -1) {
sharedStringsBytes.write(buffer, 0, length);
}
sharedStrings = sharedStringsBytes.toString("UTF-8");
} catch (Exception ex) {
ex.printStackTrace();
}
System.out.println(sharedStrings);
//sharedStrings = replaceUTF16SurrogatePairs(sharedStrings);
sharedStrings = removeInvalidXmlCharacters(sharedStrings);
//sharedStrings = doSomethingElse(sharedStrings);
System.out.println(sharedStrings);
try (BufferedOutputStream outputStream = new BufferedOutputStream(packagePart.getOutputStream()) ) {
outputStream.write(sharedStrings.getBytes("UTF-8"));
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
public static void main(String[] args) throws Exception {
try (XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream("./Excel.xlsx"))) {
System.out.println("success");
} catch (Exception ex) {
System.out.println("failed");
ex.printStackTrace();
}
OPCPackage opcPackage = OPCPackage.open(new FileInputStream("./Excel.xlsx"));
repairSharedStringsTable(opcPackage);
opcPackage.flush();
try (XSSFWorkbook workbook = new XSSFWorkbook(opcPackage);
FileOutputStream out = new FileOutputStream("./ExcelRepaired.xlsx");) {
workbook.write(out);
System.out.println("success");
} catch (Exception ex) {
System.out.println("failed");
ex.printStackTrace();
}
}
}
在我的情况下,下面的文件都有无效字符
xl/sharedStrings.xml
xl/worksheets/sheet1.xml
xl/worksheets/sheet8.xml
所有这些xml都应该进行处理
opcPackage.getPartsByName(Pattern.compile("(/xl/sharedStrings.xml)|(/xl/worksheets/.+\.xml)"))