Предисловие
POI-это известная библиотека для чтения и написания документов Microsoft под Apache. Многие люди должны были использовать POI при экспорте отчетов, создания документов Word и чтения их. POI действительно приносит удобство для этих операций. Один из инструментов, которые я сделал недавно, - прочитать файлы Word и Excel на моем компьютере.
Описание структуры POI
Имя пакета Описание
HSSF предоставляет возможность читать и писать Microsoft Excel XLS Формата Архива.
XSSF предоставляет возможность читать и писать Microsoft Excel OOXML XLSX Формат.
HWPF предоставляет возможность читать и писать архивы формата DOC Microsoft Word.
HSLF предоставляет возможность читать и писать архивы формата Microsoft PowerPoint.
HDGF предоставляет возможность читать архивы формата Microsoft Visio.
HPBF предоставляет возможность чтения архивов в формате Microsoft Publisher.
HSMF обеспечивает функцию чтения архивов формата Microsoft Outlook.
Вот некоторые из ошибок, встречающихся как в Word, так и в Excel:
Слово
Для файлов слов все, что мне нужно, это извлечь текст в основной текст в файле. Таким образом, вы можете создать метод для чтения файла DOC или DOCX:
Private Static String Readdoc (String filePath, InputStream IS) {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 (Exception e) {logger.error (filePath, e); } наконец {if (is! = null) {is.close (); }} возвратный текст; }Теоретически, этот код должен быть действительным для чтения большинства файлов DOC или DOCX. Но!!! Я нашел странную проблему, то есть, когда мой код считывает определенные файлы DOC, он часто дает такое исключение:
org.apache.poi.poifs.filesystem.officexmlfileException: Поставляемые данные, по -видимому, находятся в Office 2007+ XML. Вы называете часть POI, которая занимается офисными документами OLE2.
Что означает это исключение? Проще говоря, открытый вами файл не является файлом DOC, и вы должны использовать метод чтения DOCX для его прочтения. Но то, что мы явно открываем, - это файл с Doc Doc!
На самом деле, DOC и DOCX по существу разные. DOC - тип OLE2, а DOCX - тип OOXML. Если вы откроете файл docx с помощью сжатого файла, вы найдете несколько папок:
По сути, файл DOCX - это zip -файл, который содержит некоторые файлы XML. Следовательно, хотя некоторые файлы DOCX не имеют больших по размеру, файлы XML внутри действительно относительно большие, поэтому он потребляет много памяти при чтении некоторых файлов DOCX, которые не кажутся очень большими.
Затем я открыл этот файл DOC, используя сжатый файл. Как и ожидалось, его внутренние внутренности, как показано на рисунке выше, поэтому по сути мы можем думать об этом как о файле DOCX. Может быть, это потому, что он сохранен в каком -то режиме совместимости, что приводит к такой проблеме мошенничества. Итак, теперь мы можем судить, является ли файл DOC или DOCX на основе имени суффикса, что ненадежно.
Честно говоря, я не думаю, что это редкая проблема. Но я ничего об этом не нашел в Google. Как узнать, является ли файл .docx или .doc format из Apache Poi Этот пример - использовать ZipinputStream, чтобы определить, является ли файл файл docx:
Boolean Iszip = new ZipinputStream (fileStream) .getNextEntry ()! = NULL;
Но я не думаю, что это хороший способ, потому что я должен построить ZipinPustream, что, очевидно, не очень хорошо. Кроме того, эта операция, по -видимому, влияет на inputStream, поэтому у вас будут проблемы с чтением обычных файлов DOC. Или вы используете объект файла, чтобы определить, является ли это файлом ZIP. Но это также не хороший способ, потому что мне также нужно прочитать файл DOC или DOCX в сжатом файле, поэтому мой вход должен быть входным потоком, поэтому эта опция тоже не в порядке. Я разговаривал с группой иностранцев на Stackoverflow большую часть времени. Иногда я действительно сомневался в способности этих иностранцев понимать, но, в конце концов, большой выстрел дал мне решение, которое сделало меня в восторге от Filemagic. Это новая функция, добавленная в POI 3.17:
public enum filemagic { / ** ole2 / biff8+ поток, используемый для Office 97 и более высоких документов* / ole2 (headerblockconstants._signature), / ** ooxml / zip -поток* / ooxml (ooxml_file_header), / ** xml file* / xml (row_xml_file_header), / ** xml fore* / xml (row_xml_file_header), / ** xml fore* / xml (row_xml_file_header), / ** xml fore* / xml (ooxml_file_header), / ** xml fore* / xml (ooxml_file_header) 2 */ biff2 (новый байт [] {0x09, 0x00, // sid = 0x0009 0x04, 0x00, // size = 0x0004 0x00, 0x00, // не используется 0x70, 0x00 // 0x70 = несколько значений}),/ ** biff3 Raw -Stream - for Excel 3 */ biff3 (New Byte},/ ** biff3 Raw - для Excel 3 */ Biff3 (New Byte}},/ ** Biff3 Raw - для Excel 3 */ Biff3 (New Byte},/ ** Biff3 Raw - для Excel 3 */ biff3 (New Byte}),/ ** Biff3 Raw -Stream // sid = 0x0209 0x06, 0x00, // size = 0x0006 0x00, 0x00, // неиспользованный 0x70, 0x00 // 0x70 = несколько значений}), /** Biff4 Raw Stream - для Excel 4* /Biff4 (New Byte [] {0x09, 0x04, // SID = 0x040040606060060 /0x060606060 /0x06060 /0x06060 /0x0. size = 0x0006 0x00, 0x00, // неиспользованный 0x70, 0x00 // 0x70 = несколько значений}, новая байт [] {0x09, 0x04, // sid = 0x0409 0x06, 0x00, // size = 0x0006 0x00, 0x00, / /unazed 0x00, 0x01}), /*********************************** MSWRITE (New Byte [] {0x31, (byte) 0xbe, 0x00, 0x00}, новый байт [] {0x32, (byte) 0xbe, 0x00, 0x00}),/** rtf документ*/rtf ("// rtf"),/** Документ PDF*/pdf ("// rtf"),/** Документ PDF*PDF ("// rtf"), ** PDF ** PDF ("// rtf"),/** Документ PDF ("// rtf"). Последний разум! / ** Неизвестная магия*/ Неизвестно (новый байт [0]); Финальный байт [] [] магия; Filemagic (длинная магия) {this.magic = new Byte [1] [8]; Littleendian.putlong (this.magic [0], 0, Magic); } FileMagic (byte [] ... Magic) {this.magic = Magic; } FileMagic (String Magic) {this (Magic.getBytes (localeutil.charset_1252)); } public static filemagic valueof (byte [] Magic) {for (filemagic fm: values ()) {int i = 0; Boolean найдено = true; for (byte [] ma: fm.magic) {for (byte m: ma) {byte d = магия [i ++]; if (! (d == m || (m == 0x70 && (d == 0x10 || d == 0x20 || d == 0x40)))) {sud = false; перерыв; }} if (найдено) {return fm; }}} return неизвестно; } / ** * Получите магию файла поставляемого InputStream (который должен * поддержать марку и сброс). <p> * * Если неуверенность в том, поддерживает ли ваш inputstream mark / reset, * Используйте {@link #preparetocheckmagic (inputstream)}, чтобы завершить его и не допустить, чтобы всегда использовал это, а не оригинал! Потенциально означает, что * что zip -поток имеет ведущие нежелательные байты * * @param inp inputstream, который поддерживает либо Mark/ reset */ public static filemagic value (inputstream inp), бросает ioexception {if (! inp.marksupported ()) {бросает новое ioException ("getFilemagic (), которые отмечают только потоки); } // Возьмите первые 8 байт -байт [] data = ioutils.peekfirst8bytes (inp); return filemagic.valueof (data); } / ** * Проверяет, может ли {@link inputstream} сбросить (т.е. используется для проверки магии заголовка) и завершает его, если не * * @param по потоку потока для проверки для обертывания * @return aemplable inabled Stream * / public static outpareStream PrepareTocheckmagic (inputstream stream) {if.marksuppord () strabort (strive -stream) {if wream.marks } // Мы использовали для обработки данных с помощью DefbackInputStream, но код пользователя может предоставить слишком маленький //, поэтому мы используем BufferedInputStream вместо этого теперь возвращаем новый BufferedInptream (поток); }}Вот основной код, который в основном определяет тип файла на основе первых 8 байтов inputstream. Невозможно думать, что это самое элегантное решение. Вначале я на самом деле думал, что первые несколько байтов сжатого файла, казалось, определялись другим, Magicmumber. Поскольку зависимости FileMagic совместимы с версией 3.16, мне просто нужно добавить этот класс, поэтому нам сейчас правильный способ прочитать слово «файл»:
Private Static String Readdoc (String filePath, InputStream IS) {String Text = ""; is = 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 (Exception e) {logger.error ("для файла" + filePath, e); } наконец {if (is! = null) {is.close (); }} возвратный текст; } Экстр
Для статьи Excel я не буду искать сравнения между предыдущим планом и текущим планом. Я дам мне лучшие практики сейчас:
@Suppresswarnings («Унимок») частная статическая строка readexcel (строка FilePath, Inputstream Inp) бросает исключение {Workbook WB; StringBuilder SB = new StringBuilder (); try {if (filepath.endswith (". xls")) {wb = new hssfworkbook (inp); } else {wb = streamingReader.builder () .RowCachesize (1000) // Количество строк для поддержания в памяти (по умолчанию до 10) .buffersize (4096) // Размер буфера для использования при чтении ввода в файл (по умолчанию на 1024). Open (inp); // inputStream или файл для файла xlsx (обязательно)} sb = readsheet (wb, sb, filepath.endswith (". Xls")); wb.close (); } catch (ole2notofficexmlfileexception e) {logger.error (filePath, e); } наконец {if (inp! = null) {inp.close (); }} return sb.toString (); } частная статическая строка readexcelbyfile (String filePath, файл файла) {Workbook WB; StringBuilder SB = new StringBuilder (); try {if (filepath.endswith (". xls")) {wb = workbookfactory.create (file); } else {wb = streamingReader.builder () .RowCachesize (1000) // Количество строк для поддержания в памяти (по умолчанию до 10) .buffersize (4096) // Размер буфера для использования при чтении ввода по умолчанию (по умолчанию на 1024). // inputStream или файл для файла xlsx (обязательно)} sb = readsheet (wb, sb, filepath.endswith (". Xls")); wb.close (); } catch (Exception e) {logger.error (filePath, e); } вернуть sb.toString (); } Частный статический stringBuilder ReadSheet (Workbook WB, StringBuilder SB, Boolean ISXLS) Throws Exception {для (лист (лист: wb) {for (row r: лист) {для (ячейка: r) {if (cell.getcelltype () == cell.cell_type_string) {sb.pl SB.Append ("" "); } else if (cell.getCelltype () == cell.cell_type_numeric) {if (isxls) {dataformatter formatter = new DataFormatter (); SB.Append (FormatCellValue (Cell)); } else {sb.append (cell.getStringCellValue ()); } sb.append (""); }}}} вернуть Sb; }На самом деле, для чтения Excel, самая большая проблема, с которой сталкивается мой инструмент, - это переполнение памяти. Часто чтение определенных особенно больших файлов Excel вызовет проблему переполнения памяти. Позже я наконец нашел отличный инструмент Excel-Streaming-reader, который может оптимизировать файлы XLSX и разделять некоторые особенно большие файлы на небольшие файлы для чтения.
Другая оптимизация заключается в том, что в сценарии, в котором можно использовать объекты файлов, я использую файловые объекты для чтения файлов вместо использования InputStream для их чтения, потому что использование InputStream требует, чтобы все они были загружены в память, так что это очень напоминает память.
Наконец, мой маленький трюк состоит в том, чтобы использовать cell.getCelltype, чтобы уменьшить объем данных, потому что мне нужно только получить некоторое содержимое строки текста и числа.
Вышеуказанное - некоторые из моих исследований и открытий при чтении файлов с использованием POI, и я надеюсь, что это будет полезно для вас. Приведенные выше примеры также применяются в одном из моих инструментов повсюду (этот инструмент может в основном помочь вам найти полный текст контента на вашем компьютере). Если вам интересно, вы можете посмотреть. Добро пожаловать в звезду или PR.
Суммировать
Вышеуказанное - все содержание этой статьи. Я надеюсь, что содержание этой статьи имеет определенную справочную ценность для каждого обучения или работы. Если у вас есть какие -либо вопросы, вы можете оставить сообщение для общения. Спасибо за поддержку Wulin.com.