머리말
POI는 Apache에서 Microsoft의 문서를 읽고 쓰는 데 잘 알려진 도서관입니다. 많은 사람들이 보고서를 내보내고, 단어 문서를 작성하고, 읽을 때 POI를 사용해야했습니다. POI는 이러한 작업에 큰 편의를 제공합니다. 최근에 만든 도구 중 하나는 컴퓨터에서 Word and Excel 파일을 읽는 것입니다.
POI 구조 설명
패키지 이름 설명
HSSF는 Microsoft Excel XLS 형식 아카이브를 읽고 쓸 수있는 기능을 제공합니다.
XSSF는 Microsoft Excel OOXML XLSX 형식 아카이브를 읽고 쓸 수있는 기능을 제공합니다.
HWPF는 Microsoft Word Doc 형식 아카이브를 읽고 쓸 수있는 기능을 제공합니다.
HSLF는 Microsoft PowerPoint 형식 아카이브를 읽고 쓸 수있는 기능을 제공합니다.
HDGF는 Microsoft Visio Format Archives를 읽을 수있는 기능을 제공합니다.
HPBF는 Microsoft Publisher 형식의 아카이브를 읽을 수있는 기능을 제공합니다.
HSMF는 Microsoft Outlook 형식 아카이브를 읽는 기능을 제공합니다.
다음은 단어와 Excel에서 발생하는 함정 중 일부입니다.
단어
Word 파일의 경우 파일의 본문에서 텍스트를 추출하는 것입니다. 따라서 doc 또는 docx 파일을 읽는 메소드를 만들 수 있습니다.
private static string readdoc (String filepath, inputstream is) {문자열 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 추출기 = 새로운 xwpfwordextractor (DOC); text = extractor.getText (); extractor.close (); is.close (); }} catch (예외 e) {logger.error (filepath, e); } 마침내 {if (is! = null) {is.close (); }} 텍스트 리턴; }이론적 으로이 코드는 대부분의 DOC 또는 DOCX 파일을 읽는 데 유효해야합니다. 하지만!!! 이상한 문제를 발견했습니다. 즉, 코드가 특정 DOC 파일을 읽을 때 종종 그러한 예외가 제공됩니다.
org.apache.poi.poifs.filesystem.officexmlfileException : 제공된 데이터는 Office 2007+ XML에있는 것으로 보입니다. OLE2 사무실 문서를 다루는 POI의 일부를 호출하고 있습니다.
이 예외는 무엇을 의미합니까? 간단히 말해서, 열린 파일은 DOC 파일이 아니므로 DOCX를 읽는 방법을 사용하여 읽어야합니다. 그러나 우리가 명확하게 여는 것은 접미사 문서의 파일입니다!
실제로 DOC와 DOCX는 본질적으로 다릅니다. Doc은 OLE2 유형이고 DOCX는 OOXML 유형입니다. 압축 파일로 docx 파일을 열면 일부 폴더가 있습니다.
본질적으로 DOCX 파일은 일부 XML 파일을 포함하는 zip 파일입니다. 따라서 일부 DOCX 파일의 크기는 크지 않지만 내부의 XML 파일은 실제로 비교적 크기 때문에 크게 큰 DOCX 파일을 읽을 때 많은 메모리를 소비하는 이유입니다.
그런 다음 압축 파일을 사용 하여이 Doc 파일을 열었습니다. 예상 한 바와 같이, 내부는 위의 그림에 표시된대로, 본질적으로 우리는 그것을 docx 파일로 생각할 수 있습니다. 어쩌면 일부 호환 모드에서 저장되어 이러한 사기 문제로 이어질 수 있습니다. 이제 파일이 접미사 이름을 기반으로 DOC 또는 DOCX인지 판단 할 수 있습니다.
솔직히 말해서, 나는 이것이 드문 문제라고 생각하지 않습니다. 그러나 나는 이것에 대해 Google에서 찾지 못했습니다. 파일이 .docx인지 .docx 또는 .doc 형식인지 아는 방법 Apache POI이 예제는 ZipInputStream을 사용하여 파일이 docx 파일인지 확인하는 것입니다.
Boolean iszip = 새로운 ZipinputStream (filestream) .getNextEntry ()! = null;
그러나 나는 이것이 좋은 방법이라고 생각하지 않습니다. 왜냐하면 나는 zipinpustream을 만들어야하기 때문입니다. 또한이 작업은 InputStream에 영향을 미치는 것으로 보이므로 일반 DOC 파일을 읽는 데 문제가 있습니다. 또는 파일 개체를 사용하여 ZIP 파일인지 확인합니다. 그러나 압축 파일에서 Doc 또는 Docx 파일을 읽어야하므로 입력이 inputStream이어야 하므로이 옵션도 괜찮지 않기 때문에 이것은 좋은 방법이 아닙니다. 나는 대부분의 시간 동안 stackoverflow에서 외국인 그룹과 이야기를 나 ed습니다. 때때로 나는이 외국인들이 이해할 수있는 능력을 의심했지만 결국 큰 샷으로 인해 나에게 황홀하고 filemagic을 만들어주는 해결책을 주었다. 이것은 POI 3.17에 추가 된 새로운 기능입니다.
공개 ENUM FILEMAGIC { / ** OLE2 / BIFF8+ OFFICE 97 및 HEOR DOCUMENTS* / OLE2 (HeaderBlockConstants._Signature), / ** OOXML / ZIP 스트림* / OOXML (OOXML_FILE_HEADER), / ** XML 파일* / XML _BIFL _HEF2 FOR EXFILER) */ biff2 (new Byte [] {0x09, 0x00, // sid = 0x0009 0x04, 0x00, // size = 0x0004 0x00, 0x00, // 미사용 0x70, 0x00 // 0x70 = 다중 값}),/ ** Biff3 Raw 스트림 - Excel 3 */ biff3 (new Quete [] {0x09, /// sid = 0x0209 0x06, 0x00, // size = 0x0006 0x00, 0x00, // 미사용 0x70, 0x00 // 0x70 = 다중 값}), /** Biff4 Raw 스트림 - Excel 4* /biff4 (new Byte [] {0x09, 0x04, // sid = 0x0409 0x06, 0x06, 0x09 size = 0x0006 0x00, 0x00, // 미사용 0x70, 0x00 // 0x70 = 다중 값}, new Byte [] {0x09, 0x04, // sid = 0x0409 0x06, 0x00, // size = 0x0006 0x00, 0x00, // 0x00, 0x01}), 0x01}). mswrite (new byte [] {0x31, (byte) 0xbe, 0x00, 0x00}, new byte [] {0x32, (byte) 0xbe, 0x00, 0x00}),/** rtf 문서*/rtf ( "{// rtf"),/** pdf 문서*/pdf (pdf) 마지막 열거! / ** 알 수없는 마법*/ 미지 (New Byte [0]); 최종 바이트 [] [] 마술; fillemagic (긴 마법) {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 valuef (byte [] magic) {for (filemagic fm : values ()) {int i = 0; 부울 발견 = 참으로; for (byte [] ma : fm.magic) {for (byte m : ma) {byte d = magic [i ++]; if (! (d == m || (m == 0x70 && (d == 0x10 || d == 0x20 || d == 0x40))) {found = false; 부서지다; }} if (find) {return fm; }}} return Unknown; } / ** * 제공된 입력 스트림의 파일 마법 (마크 및 재설정을 지원해야합니다). <p> * * 입력 스트림이 Mark / Reset을 지원하는지 확실하지 않은 경우 * {@link #preparetocheckMagic (inputocheckMagic)}}}}}}}}}}}}}을 사용하여 항상 그것을 랩하고 * <p> * * * * * * * * * * * * * * * * * * * * * * * * * Zip Stream이 주요 정크 바이트 * * @param inp Mark/ Reset */ public static filemagic valuef (inputstream inp)를 지원하는 입력 스트림을 선도한다는 것을 의미 할 수 있습니다. ioexception {if (! inp.marksupported ()) {Throw New IoException ( "getfilemagic ())); } // 처음 8 바이트 바이트를 잡습니다 [] data = ioutils.peekfirst8bytes (INP); return filemagic.valueof (데이터); } / ** * {@link inputStream}을 재설정 할 수 있는지 확인하고 (즉, 헤더 마술 확인에 사용) * * @param 스트림 스트림 래핑을 확인하기 위해 * * @param 스트림 스트림 * @Return 마크 활성화 스트림 * / public inputStream repareCheckMagic (입력 스트림) {if (if.marksupported () } // 우리는 PushbackInputStream을 통해 데이터를 처리하는 데 사용했지만 사용자 코드는 너무 작은 것을 제공 할 수 있으므로 // 대신 BufferedInputStream을 사용하여 새로운 bufferedInputStream (stream)을 반환합니다. }}주로 입력 스트림의 첫 8 바이트를 기반으로 파일 유형을 결정하는 기본 코드는 다음과 같습니다. 이것이 가장 우아한 솔루션이라고 생각할 방법이 없습니다. 처음에, 나는 압축 파일의 처음 몇 바이트가 다른 Magicmumber에 의해 정의 된 것처럼 보였다. Filemagic의 종속성은 버전 3.16과 호환 되므로이 클래스를 추가하면됩니다. 따라서 Word 파일을 읽는 올바른 방법은 다음과 같습니다.
private static string readdoc (String filepath, inputstream is) {문자열 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 추출기 = 새로운 xwpfwordextractor (DOC); text = extractor.getText (); extractor.close (); }} catch (예외 e) {logger.error ( "파일 용" + filepath, e); } 마침내 {if (is! = null) {is.close (); }} 텍스트 리턴; } 뛰어나다
Excel 기사의 경우 이전 계획과 현재 계획을 비교하지 않을 것입니다. 지금 모범 사례를 드리겠습니다.
@SuppressWarnings ( "Dequrecation") 개인 정적 문자열 readexcel (String 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). // XLSX 파일의 입력 스트림 또는 파일 (필수)} sb = readsheet (wb, sb, filepath.endswith ( ". xls")); wb.close (); } catch (olo2NotOfficexmlFileException e) {logger.error (Filepath, e); } 마침내 {if (inp! = null) {inp.close (); }} return sb.toString (); } private static string readexcelbyfile (문자열 filepath, 파일 파일) {Workbook WB; StringBuilder sb = new StringBuilder (); try {if (filepath.endswith ( ". xls")) {wb = wordbookfactory.create (file); } else {wb = streamingReader.Builder () .rowCachesize (1000) // 메모리를 유지할 행 수 (기본값 10) .BuffSerize (4096) // inputStream에서 파일을 읽을 때 사용하는 버퍼 크기 (1024) .Open (파일); // XLSX 파일의 입력 스트림 또는 파일 (필수)} sb = readsheet (wb, sb, filepath.endswith ( ". xls")); wb.close (); } catch (예외 e) {logger.error (Filepath, e); } return sb.toString (); } private static stringbuilder readsheet (Workbook WB, StringBuilder SB, boolean isxls)는 예외를 {(시트 시트 : wb) {for (row r : sheet) {for (cell cell : r) {if (cell.getCellType () == cell.cell_type_string) 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 ( ""); }}}} return sb; }실제로 Excel 읽기의 경우, 내 공구가 직면 한 가장 큰 문제는 메모리 오버플로입니다. 종종 특히 큰 Excel 파일을 읽으면 메모리 오버플로 문제가 발생합니다. 나중에 마침내 XLSX 파일을 간소화하고 특히 큰 파일을 작은 파일로 나누어 읽을 수있는 우수한 도구 Excel-Streaming-Reader를 찾았습니다.
또 다른 최적화는 파일 객체를 사용할 수있는 시나리오에서 파일 객체를 사용하여 입력 스트림을 사용하여 파일을 읽는 대신 파일을 읽습니다. inputStream을 사용하려면 모든 것이 메모리에로드되어야하므로 매우 메모리 소모입니다.
마지막으로, 내 작은 속임수는 셀을 사용하여 데이터 양을 줄이기 위해 셀을 사용하는 것입니다. 텍스트와 숫자 문자열 내용 만 가져 가면됩니다.
위의 내용은 POI를 사용하여 파일을 읽을 때의 탐구와 발견 중 일부이며, 그것이 당신에게 도움이되기를 바랍니다. 위의 예제는 내 도구 중 하나에도 적용됩니다 (이 도구는 주로 컴퓨터에서 컨텐츠의 전체 텍스트를 검색하는 데 도움이 될 수 있습니다). 관심이 있으시면 살펴볼 수 있습니다. Star 또는 Pr에 오신 것을 환영합니다.
요약
위는이 기사의 전체 내용입니다. 이 기사의 내용에 모든 사람의 연구 나 작업에 대한 특정 참조 가치가 있기를 바랍니다. 궁금한 점이 있으면 의사 소통을 위해 메시지를 남길 수 있습니다. Wulin.com을 지원 해주셔서 감사합니다.