Vorwort
POI ist eine bekannte Bibliothek zum Lesen und Schreiben von Microsoft-Dokumenten unter Apache. Viele Menschen sollten POI beim Exportieren von Berichten, zum Erstellen von Wortdokumenten und beim Lesen verwendet haben. Poi bringt diesen Operationen große Bequemlichkeit. Eines der Tools, die ich kürzlich gemacht habe, ist das Lesen von Wort- und Excel -Dateien in meinem Computer.
POI -Struktur Beschreibung
Paketname Beschreibung
HSSF bietet die Möglichkeit, Microsoft Excel XLS -Formatarchive zu lesen und zu schreiben.
XSSF bietet die Möglichkeit, Microsoft Excel OOXML XLSX -Formatarchive zu lesen und zu schreiben.
HWPF bietet die Möglichkeit, Microsoft Word DOC -Formatarchive zu lesen und zu schreiben.
HSLF bietet die Möglichkeit, Microsoft PowerPoint -Formatarchive zu lesen und zu schreiben.
HDGF bietet die Möglichkeit, Microsoft Visio -Formatarchive zu lesen.
HPBF bietet die Möglichkeit, Archive im Microsoft Publisher -Format zu lesen.
HSMF bietet die Funktion des Lesens von Microsoft Outlook -Formatarchiven.
Hier sind einige der Fallstricke sowohl in Wort als auch in Excel:
Wort
Für Wortdateien muss ich nur den Text im Haupttext in der Datei extrahieren. Sie können also eine Methode erstellen, um die DOC- oder DOCX -Datei zu lesen:
private statische Zeichenfolge readdoc (String filepath, InputStream) {String text = ""; try {if (filepath.endswith ("doc")) {WordExtractor ex = new WordExtractor (IS); text = ex.GetText (); ex.close (); IS.CLOSE (); } else if (filepath.endswith ("docx")) {xwpfdocument doc = new XWPFDocument (IS); XWPFWORDExtractor extractor = new XWPFWordExtractor (DOC); text = extractor.getText (); extractor.close (); IS.CLOSE (); }} catch (Ausnahme e) {logger.Error (filepath, e); } endlich {if (ist! = null) {is.close (); }} return text; }Theoretisch sollte dieser Code für das Lesen der meisten DOC- oder DOCX -Dateien gültig sein. Aber!!! Ich fand ein seltsames Problem, dh wenn mein Code bestimmte DOC -Dateien liest, gibt er oft eine solche Ausnahme:
org.apache.poi.poifs.Filesystem.officexmlFileException: Die gelieferten Daten scheinen im Büro 2007+ XML zu sein. Sie nennen den Teil des POI, der sich mit Ole2 -Office -Dokumenten befasst.
Was bedeutet diese Ausnahme? In einfachen Worten ist die von Ihnen geöffnete Datei keine DOC -Datei, und Sie sollten die Methode zum Lesen von DOCX verwenden, um sie zu lesen. Aber was wir eindeutig öffnen, ist eine Datei mit dem Suffix DOC!
Tatsächlich sind DOC und DOCX im Wesentlichen unterschiedlich. DOC ist ein Ole2 -Typ, während docx ooxml Typ ist. Wenn Sie eine DOCX -Datei mit einer komprimierten Datei öffnen, finden Sie einige Ordner:
Im Wesentlichen handelt es sich bei der DOCX -Datei um eine ZIP -Datei, die einige XML -Dateien enthält. Obwohl einige DOCX -Dateien nicht groß sind, sind die XML -Dateien in der Tat relativ groß, weshalb sie beim Lesen einiger Docx -Dateien, die nicht sehr groß zu sein scheinen, viel Speicher verbraucht.
Dann habe ich diese DOC -Datei mit einer komprimierten Datei geöffnet. Wie erwartet werden seine Interna wie im obigen Bild gezeigt, sodass wir es im Wesentlichen als DOCX -Datei betrachten können. Vielleicht liegt es daran, dass es in einem Kompatibilitätsmodus gespeichert wird, was zu einem solchen Betrugsproblem führt. Jetzt können wir nun beurteilen, ob eine Datei doc oder docx basierend auf dem Suffix -Namen ist, was unzuverlässig ist.
Um ehrlich zu sein, denke ich nicht, dass dies ein seltenes Problem ist. Aber ich habe bei Google nichts darüber gefunden. So wissen Sie, ob eine Datei .Docx- oder .doc -Format aus Apache POI ist. In diesem Beispiel wird ZipinputStream verwendet, um festzustellen, ob eine Datei eine DOCX -Datei ist:
boolean iSZIP = neu ZipinputStream (fileStream) .GetNextEntry ()! = NULL;
Aber ich denke nicht, dass dies ein guter Weg ist, weil ich einen Zipinpusteram bauen muss, was offensichtlich nicht gut ist. Darüber hinaus scheint sich dieser Vorgang auf den InputStream auswirken, sodass Sie Probleme haben, normale DOC -Dateien zu lesen. Sie verwenden das Dateiobjekt, um festzustellen, ob es sich um eine ZIP -Datei handelt. Dies ist aber auch kein guter Weg, da ich auch die DOC- oder DOCX -Datei in der komprimierten Datei lesen muss, sodass meine Eingabe eingabetream sein muss, sodass diese Option auch nicht in Ordnung ist. Ich habe die meiste Zeit mit einer Gruppe von Ausländern auf Stackoverflow gesprochen. Manchmal bezweifelte ich wirklich die Fähigkeit dieser Ausländer zu verstehen, aber am Ende gab mir ein großer Schuss eine Lösung, die mich ekstatisch und filemagisch machte. Dies ist eine neue Funktion, die zu POI 3.17 hinzugefügt wurde:
public enum fileMagic { / ** ole2 / biff8+ Stream, der für Office 97 und höhere Dokumente verwendet wird 2 */ biff2 (neues byte [] {0x09, 0x00, // sid = 0x0009 0x04, 0x00, // size = 0x0004 0x00, 0x00, // unbenutzt 0x70, 0x00 // 0x70 = Mehrfachwerte}),/ ** biff3 raw stream - für excel 3 */ b, für excel 3 */ b, für excel 3 */ b, für excel 3 */ b, für excel 3 */ b, für excel 3 */ b, New SoN. // sid=0x0209 0x06, 0x00, // size=0x0006 0x00, 0x00, // unused 0x70, 0x00 // 0x70 = multiple values }), /** BIFF4 raw stream - for Excel 4 */ BIFF4(new byte[]{ 0x09, 0x04, // sid=0x0409 0x06, 0x00, // size = 0x0006 0x00, 0x00, // unbenutzt 0x70, 0x00 // 0x70 = Mehrere Werte}, neues Byte [] {0x09, 0x04, // sid = 0x0409 0x06, 0x00, // size = 0x0006 0x00, 0x00, // // / / / / / / /ite 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00. MSWRITE (neuer Byte [] {0x31, (Byte) 0xbe, 0x00, 0x00}, neues Byte [] {0x32, (Byte) 0xbe, 0x00, 0x00}),/** Rtf -Dokument*/rtf ("{{// rtf"),/** pdf document Letzte Aufzählung! / ** unbekannte Magie*/ Unbekannt (neues Byte [0]); Final Byte [] [] Magie; Filemagic (lange Magie) {this.magic = new Byte [1] [8]; Littleendian.putlong (this.magic [0], 0, Magie); } Filemagic (byte [] ... Magic) {this.magic = Magic; } FileMagic (String Magic) {this (Magic.getBytes (localeutil.charset_1252)); } public static filemagic valueof (byte [] Magic) {für (filemagic fm: values ()) {int i = 0; boolean found = true; für (byte [] ma: fm.magic) {für (byte m: ma) {byte d = magic [i ++]; if (! (d == m || (m == 0x70 && (d == 0x10 || d == 0x20 || d == 0x40)))) {foffen = false; brechen; }} if (gefunden) {return fm; }}} return unbekannt; } / ** * Erhalten Sie die Dateimagie des gelieferten InputStream (der Mark und zurücksetzen muss). Mean, * dass der ZIP -Stream führende Junk -Bytes hat } // Die ersten 8 Bytes Byte [] data = ioutils.peekfirst8Bytes (INP) schnappen; return filemagic.Valueof (Daten); } / ** * prüft, ob ein {@link InputStream} zurückgesetzt werden kann (dh zur Überprüfung der Headermagie) und sie abschließt, wenn nicht * * @param Stream Stream auf das Wraping * @Return einen Mark -aktivierten Stream * / public static InputStream präparetochmagic (InputStram -Stream) {if (Stream.marksupported ()) {) {stream). } // Wir haben die Daten über einen RushbackInputStream verarbeitet, aber der Benutzercode kann einen zu kleinen // liefern, sodass wir einen BufferedInputStream verwenden, stattdessen geben wir jetzt einen neuen BufferedInputStream (Stream) zurück. }}Hier ist der Hauptcode, der den Dateityp basierend auf den ersten 8 Bytes von InputStream hauptsächlich bestimmt. Es gibt keine Möglichkeit zu glauben, dass dies die eleganteste Lösung ist. Zu Beginn dachte ich tatsächlich, dass die ersten Bytes der komprimierten Datei durch eine andere, MagicMumber definiert zu sein schienen. Da die Abhängigkeiten von Filemagic mit Version 3.16 kompatibel sind, muss ich diese Klasse nur hinzufügen. Die richtige Möglichkeit für uns, die Wortdatei jetzt zu lesen, ist jetzt:
private statische Zeichenfolge readdoc (String filepath, InputStream) {String text = ""; ist = filemagic.preparetocheckmagic (IS); try {if (fileMagic.Valueof (is) == fileMagic.ole2) {WordExtractor ex = new WordExtractor (IS); text = ex.GetText (); ex.close (); } else if (fileMagic.Valueof (is) == fileMagic.OOXML) {XWPFDocument doc = new XWPFDocument (IS); XWPFWORDExtractor extractor = new XWPFWordExtractor (DOC); text = extractor.getText (); extractor.close (); }} catch (Ausnahme e) {logger.Error ("für Datei" + filepath, e); } endlich {if (ist! = null) {is.close (); }} return text; } Excel
Für den Excel -Artikel werde ich nicht nach Vergleiche zwischen dem vorherigen Plan und dem aktuellen Plan suchen. Ich werde mir jetzt die besten Praktiken geben:
@SuppressWarnings ("Abschaltung") private statische String -ReadExcel (String Filepath, InputStream INP) löst eine Ausnahme aus {Workbook WB; StringBuilder sb = new StringBuilder (); try {if (filepath.endswith (". xls")) {wb = new HSSFWorkbook (INP); } else {wb = streamingReader.builder () .Rowcachesize (1000) // Anzahl der Zeilen, die im Speicher aufbewahrt werden sollen (Standardeinstellungen bis 10) .Buffersize (4096) // Puffergröße zum Lesen von InputStream zu Datei (Standards bis 1024) .Open (INP); // InputStream oder Datei für XLSX -Datei (erforderlich)} SB = LEaSheet (WB, SB, Filepath.endswith (". XLS")); wb.close (); } catch (ole2notOfficexmlFileException e) {logger.Error (filepath, e); } endlich {if (inp! = null) {inp.close (); }} return sb.toString (); } private statische Zeichenfolge ReadExcelByFile (String -Filepath, Dateidatei) {Arbeitsbuch WB; StringBuilder sb = new StringBuilder (); try {if (filepath.endswith (". xls")) {wb = workbookFactory.create (Datei); } else {wb = streamingReader.builder () .Rowcachesize (1000) // Anzahl der Zeilen, die im Speicher aufbewahrt werden sollen (Standardeinstellungen bis 10) .BufferSize (4096) // Puffergröße zum Lesen von InputStream zu Datei (standardmäßig 1024) .Open (Datei); // InputStream oder Datei für XLSX -Datei (erforderlich)} SB = LEaSheet (WB, SB, Filepath.endswith (". XLS")); wb.close (); } catch (Ausnahme e) {logger.Error (filepath, e); } return sb.toString (); } private static StringBuilder readSheet(Workbook wb, StringBuilder sb, boolean isXls) throws Exception { for (Sheet sheet: wb) { for (Row r: sheet) { for (Cell cell: r) { if (cell.getCellType() == Cell.CELL_TYPE_STRING) { sb.append(cell.getStringCellValue()); Sb.Append (""); } else if (cell.getCellType () == cell.cell_type_numeric) {if (isxls) {dataFormatter formatter = new DataFormatter (); sb.Append (formatcellValue (Zelle)); } else {sb.append (cell.getStringCellValue ()); } SB.Append (""); }}}} return sb; }Für Excel Read ist das größte Problem, mit dem mein Werkzeug ausgesetzt ist, der Speicherüberlauf. Oft verursacht das Lesen bestimmter besonders großer Excel -Dateien ein Problem mit dem Speicherüberlauf. Später fand ich schließlich ein exzellentes Tool Excel-Streaming-Reader, mit dem XLSX-Dateien optimiert und einige besonders große Dateien in kleine Dateien zum Lesen aufgeteilt werden können.
Eine weitere Optimierung besteht darin, dass ich im Szenario, in dem Dateiobjekte verwendet werden können, Dateiobjekte zum Lesen von Dateien verwende, anstatt InputStream zu lesen, um sie zu lesen, da die Verwendung von InputStream erforderlich ist, dass alle in den Speicher geladen werden.
Schließlich ist mein kleiner Trick, Cell.getCellType zu verwenden, um die Datenmenge zu reduzieren, da ich nur einen String -Inhalt von Text und Zahlen abrufen muss.
Die oben genannten sind einige meiner Erkundungen und Entdeckungen beim Lesen von Dateien mit POI, und ich hoffe, es wird Ihnen hilfreich sein. Die obigen Beispiele werden auch in einem meiner Tools überall angewendet (mit diesem Tool können Sie hauptsächlich den vollständigen Text des Inhalts auf Ihrem Computer durchsuchen). Wenn Sie interessiert sind, können Sie einen Blick darauf werfen. Willkommen bei Star oder PR.
Zusammenfassen
Das obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, dass der Inhalt dieses Artikels einen gewissen Referenzwert für das Studium oder die Arbeit eines jeden hat. Wenn Sie Fragen haben, können Sie eine Nachricht zur Kommunikation überlassen. Vielen Dank für Ihre Unterstützung bei Wulin.com.