Исключение при чтении файла XLSB Apache POI java.io.CharConversionException
Я разрабатываю Java-приложение, которое читает файл Excel xlsb с использованием Apache POI, но я получил исключение при его чтении, мой код выглядит следующим образом:
import java.io.IOException;
import java.io.InputStream;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
import org.apache.poi.openxml4j.opc.Package;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;
import java.util.Iterator;
public class Prueba {
public static void main (String [] args){
String direccion = "C:/Documents and Settings/RSalasL/My Documents/New Folder/masstigeoct12.xlsb";
Package pkg;
try {
pkg = Package.open(direccion);
XSSFReader r = new XSSFReader(pkg);
SharedStringsTable sst = r.getSharedStringsTable();
XMLReader parser = fetchSheetParser(sst);
Iterator<InputStream> sheets = r.getSheetsData();
while(sheets.hasNext()) {
System.out.println("Processing new sheet:\n");
InputStream sheet = sheets.next();
InputSource sheetSource = new InputSource(sheet);
parser.parse(sheetSource);
sheet.close();
System.out.println("");
}
} catch (InvalidFormatException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (OpenXML4JException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void processAllSheets(String filename) throws Exception {
Package pkg = Package.open(filename);
XSSFReader r = new XSSFReader( pkg );
SharedStringsTable sst = r.getSharedStringsTable();
XMLReader parser = fetchSheetParser(sst);
Iterator<InputStream> sheets = r.getSheetsData();
while(sheets.hasNext()) {
System.out.println("Processing new sheet:\n");
InputStream sheet = sheets.next();
InputSource sheetSource = new InputSource(sheet);
parser.parse(sheetSource);
sheet.close();
System.out.println("");
}
}
public static XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException {
XMLReader parser =
XMLReaderFactory.createXMLReader(
"org.apache.xerces.parsers.SAXParser"
);
ContentHandler handler = new SheetHandler(sst);
parser.setContentHandler(handler);
return parser;
}
private static class SheetHandler extends DefaultHandler {
private SharedStringsTable sst;
private String lastContents;
private boolean nextIsString;
private SheetHandler(SharedStringsTable sst) {
this.sst = sst;
}
public void startElement(String uri, String localName, String name,
Attributes attributes) throws SAXException {
// c => cell
if(name.equals("c")) {
// Print the cell reference
System.out.print(attributes.getValue("r") + " - ");
// Figure out if the value is an index in the SST
String cellType = attributes.getValue("t");
if(cellType != null && cellType.equals("s")) {
nextIsString = true;
} else {
nextIsString = false;
}
}
// Clear contents cache
lastContents = "";
}
public void endElement(String uri, String localName, String name)
throws SAXException {
// Process the last contents as required.
// Do now, as characters() may be called more than once
if(nextIsString) {
int idx = Integer.parseInt(lastContents);
lastContents = new XSSFRichTextString(sst.getEntryAt(idx)).toString();
nextIsString = false;
}
// v => contents of a cell
// Output after we've seen the string contents
if(name.equals("v")) {
System.out.println(lastContents);
}
}
public void characters(char[] ch, int start, int length)
throws SAXException {
lastContents += new String(ch, start, length);
}
}
}
И исключение это:
java.io.CharConversionException: Characters larger than 4 bytes are not supported: byte 0x83 implies a length of more than 4 bytes
at org.apache.xmlbeans.impl.piccolo.xml.UTF8XMLDecoder.decode(UTF8XMLDecoder.java:162)
at org.apache.xmlbeans.impl.piccolo.xml.XMLStreamReader$FastStreamDecoder.read(XMLStreamReader.java:762)
at org.apache.xmlbeans.impl.piccolo.xml.XMLStreamReader.read(XMLStreamReader.java:162)
at org.apache.xmlbeans.impl.piccolo.xml.PiccoloLexer.yy_refill(PiccoloLexer.java:3474)
at org.apache.xmlbeans.impl.piccolo.xml.PiccoloLexer.yylex(PiccoloLexer.java:3958)
at org.apache.xmlbeans.impl.piccolo.xml.Piccolo.yylex(Piccolo.java:1290)
at org.apache.xmlbeans.impl.piccolo.xml.Piccolo.yyparse(Piccolo.java:1400)
at org.apache.xmlbeans.impl.piccolo.xml.Piccolo.parse(Piccolo.java:714)
at org.apache.xmlbeans.impl.store.Locale$SaxLoader.load(Locale.java:3439)
at org.apache.xmlbeans.impl.store.Locale.parseToXmlObject(Locale.java:1270)
at org.apache.xmlbeans.impl.store.Locale.parseToXmlObject(Locale.java:1257)
at org.apache.xmlbeans.impl.schema.SchemaTypeLoaderBase.parse(SchemaTypeLoaderBase.java:345)
at org.openxmlformats.schemas.spreadsheetml.x2006.main.WorkbookDocument$Factory.parse(Unknown Source)
at org.apache.poi.xssf.eventusermodel.XSSFReader$SheetIterator.<init>(XSSFReader.java:207)
at org.apache.poi.xssf.eventusermodel.XSSFReader$SheetIterator.<init>(XSSFReader.java:166)
at org.apache.poi.xssf.eventusermodel.XSSFReader.getSheetsData(XSSFReader.java:160)
at EDManager.Prueba.main(Prueba.java:36)
Файл имеет 2 листа, один с 329 строками и 3 столбцами, а другой с 566 строками и 3 столбцами, я просто хочу прочитать файл, чтобы найти значение на втором листе.
2 ответа
Apache POI не поддерживает формат файла.xlsb для чего-либо, кроме извлечения текста. Apache POI с радостью предоставит полную поддержку чтения и записи файлов.xls (через HSSF) и файлов.xlsx (через XSSF), или обоих (через общий интерфейс SS UserModel).
Тем не менее, формат.xlsb не поддерживается для генерирующих операций - это очень странный гибрид между ними, и большой объем работы означал, что никто не хотел добровольно предлагать / спонсировать требуемую работу.
То, что Apache POI предлагает для.xlsb, начиная с Apache POI 3.15 beta3 / 3.16, - это экстрактор текста для файлов.xlsb - XSSFBEventBasedExcelExtractor. Вы можете использовать это, чтобы получить текст из вашего файла, или с помощью нескольких настроек конвертировать его в что-то вроде CSV
Для полной поддержки чтения / записи вам необходимо преобразовать файл в формат.xls (если он не имеет большого количества строк / столбцов) или в формат.xlsx (если он есть). Если вы действительно хотите помочь, вы можете просмотреть исходный код XSSFBEventBasedExcelExtractor, а затем попробовать внести исправления, чтобы добавить для него полную поддержку POI!
(Кроме того, я думаю, за исключением того, что ваш конкретный файл.xlsb частично поврежден, но даже если бы он не был, он все равно не будет поддерживаться Apache POI для чего-либо, кроме извлечения текста, извините)
Я пробовал читать файл XLSB с помощью Apache POI, и это успешно. Ниже приведен фрагмент кода, который я использовал.
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.xssf.binary.XSSFBSharedStringsTable;
import org.apache.poi.xssf.binary.XSSFBSheetHandler;
import org.apache.poi.xssf.binary.XSSFBStylesTable;
import org.apache.poi.xssf.eventusermodel.XSSFBReader;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class ApachePoiXLSB {
public static void main (String [] args){
String xlsbFileName = "test.xlsb";
OPCPackage pkg;
try {
pkg = OPCPackage.open(xlsbFileName);
XSSFBReader r = new XSSFBReader(pkg);
XSSFBSharedStringsTable sst = new XSSFBSharedStringsTable(pkg);
XSSFBStylesTable xssfbStylesTable = r.getXSSFBStylesTable();
XSSFBReader.SheetIterator it = (XSSFBReader.SheetIterator) r.getSheetsData();
List<String> sheetTexts = new ArrayList<>();
while (it.hasNext()) {
InputStream is = it.next();
String name = it.getSheetName();
TestSheetHandler testSheetHandler = new TestSheetHandler();
testSheetHandler.startSheet(name);
XSSFBSheetHandler sheetHandler = new XSSFBSheetHandler(is,
xssfbStylesTable,
it.getXSSFBSheetComments(),
sst, testSheetHandler,
new DataFormatter(),
false);
sheetHandler.parse();
testSheetHandler.endSheet();
sheetTexts.add(testSheetHandler.toString());
}
System.out.println("output text:"+sheetTexts);
} catch (InvalidFormatException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (OpenXML4JException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
import org.apache.poi.xssf.usermodel.XSSFComment;
class TestSheetHandler implements XSSFSheetXMLHandler.SheetContentsHandler {
private final StringBuilder sb = new StringBuilder();
public void startSheet(String sheetName) {
sb.append("<sheet name=\"").append(sheetName).append(">");
}
public void endSheet() {
sb.append("</sheet>");
}
@Override
public void startRow(int rowNum) {
sb.append("\n<tr num=\"").append(rowNum).append(">");
}
@Override
public void endRow(int rowNum) {
sb.append("\n</tr num=\"").append(rowNum).append(">");
}
@Override
public void cell(String cellReference, String formattedValue, XSSFComment comment) {
formattedValue = (formattedValue == null) ? "" : formattedValue;
if (comment == null) {
sb.append("\n\t<td ref=\"").append(cellReference).append("\">").append(formattedValue).append("</td>");
} else {
sb.append("\n\t<td ref=\"").append(cellReference).append("\">")
.append(formattedValue)
.append("<span type=\"comment\" author=\"")
.append(comment.getAuthor()).append("\">")
.append(comment.getString().toString().trim()).append("</span>")
.append("</td>");
}
}
@Override
public void headerFooter(String text, boolean isHeader, String tagName) {
if (isHeader) {
sb.append("<header tagName=\"").append(tagName).append("\">").append(text).append("</header>");
} else {
sb.append("<footer tagName=\"").append(tagName).append("\">").append(text).append("</footer>");
}
}
@Override
public String toString() {
return sb.toString();
}
}
У меня есть реализация, использующая smartxls, и мой код сначала преобразует xlsb в xlsx и после может использовать ApachePoi. Следующий метод получает файл java.io.File и проверяет, является ли его расширение xlsb, преобразует его в xlsx и заменяет файл новым. Это работает для меня.
private void processXLSBFile(File file) {
WorkBook workBook = new WorkBook();
String filePath = file.getAbsolutePath();
if (FilenameUtils.getExtension(filePath).equalsIgnoreCase((Static.XLSB_EXT))) {
try {
workBook.readXLSB(new java.io.FileInputStream(filePath));
filePath = filePath.replaceAll("(?i)".concat(Static.XLSB),
Static.XLSX_EXT.toLowerCase());
workBook.writeXLSX(new java.io.FileOutputStream(filePath));
final File xlsb = new File(filePath);
file = xlsb;
} catch (Exception e) {
logger.error(e.getMessage(), e);
MensajesJSFUtil
.mostrarMensajeNegocio(new GTMException(e, ClaveMensaje.COMANDAS_ADJUNTAR_XLSBFILE_READERROR));
}
}
}