Préface
POI est une bibliothèque bien connue pour lire et écrire les documents de Microsoft sous Apache. Beaucoup de gens auraient dû utiliser POI lors de l'exportation de rapports, de la création de documents de mots et de la lecture. POI apporte une grande commodité à ces opérations. L'un des outils que j'ai fait récemment est de lire des fichiers Word et Excel dans mon ordinateur.
Description de la structure POI
Nom du pack Description
HSSF offre la possibilité de lire et d'écrire des archives de format Microsoft Excel XLS.
XSSF offre la possibilité de lire et d'écrire des archives de format Microsoft Excel OOXML XLSX.
HWPF offre la possibilité de lire et d'écrire des archives de format Microsoft Word Doc.
HSLF offre la possibilité de lire et d'écrire des archives au format Microsoft PowerPoint.
HDGF offre la possibilité de lire les archives du format Microsoft Visio.
HPBF offre la possibilité de lire des archives au format Microsoft Publisher.
HSMF offre la fonction de lire les archives du format Microsoft Outlook.
Voici quelques-uns des pièges rencontrés dans Word et Excel:
Mot
Pour les fichiers Word, tout ce dont j'ai besoin est d'extraire le texte dans le texte principal du fichier. Ainsi, vous pouvez créer une méthode pour lire le fichier DOC ou DOCX:
String statique privé ReadDoc (String filepath, inputStream is) {String text = ""; essayez {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 extracteur = new xwpfwordextractor (doc); text = extracteur.getText (); extracteur.close (); is.close (); }} catch (exception e) {logger.error (filepath, e); } enfin {if (is! = null) {is.close (); }} RETOUR Texte; }En théorie, ce code doit être valable pour lire la plupart des fichiers DOC ou DOCX. Mais!!! J'ai trouvé un problème étrange, c'est-à-dire lorsque mon code lit certains fichiers DOC, il donne souvent une telle exception:
org.apache.poi.poifs.filesystem.officexmlfileException: les données fournies semblent être dans l'Office 2007+ xml. Vous appelez la partie de POI qui traite des documents de bureau OLE2.
Que signifie cette exception? En termes simples, le fichier que vous avez ouvert n'est pas un fichier DOC, et vous devez utiliser la méthode de lecture de Docx pour le lire. Mais ce que nous ouvrons clairement, c'est un fichier avec le suffixe Doc!
En fait, Doc et Docx sont essentiellement différents. DOC est de type OLE2, tandis que DOCX est de type OOXML. Si vous ouvrez un fichier docx avec un fichier compressé, vous trouverez des dossiers:
Essentiellement, le fichier DOCX est un fichier zip qui contient certains fichiers XML. Par conséquent, bien que certains fichiers DOCX ne soient pas de grande taille, les fichiers XML à l'intérieur sont en effet relativement grands, c'est pourquoi il consomme beaucoup de mémoire lors de la lecture de certains fichiers DOCX qui ne semblent pas très grands.
Ensuite, j'ai ouvert ce fichier doc à l'aide d'un fichier compressé. Comme prévu, ses internes sont comme indiqué dans l'image ci-dessus, donc essentiellement nous pouvons le considérer comme un fichier DOCX. C'est peut-être parce qu'il est enregistré dans un mode de compatibilité, ce qui conduit à un tel problème d'arnaque. Ainsi, nous pouvons maintenant juger si un fichier est DOC ou DOCX basé sur le nom du suffixe, ce qui n'est pas fiable.
Pour être honnête, je ne pense pas que ce soit un problème rare. Mais je n'ai rien trouvé à ce sujet sur Google. Comment savoir si un fichier est .docx ou .doc Format à partir d'Apache POI Cet exemple est d'utiliser ZipinputStream pour déterminer si un fichier est un fichier docx:
booléan iszip = new ZipinputStream (filestream) .getNextStry ()! = null;
Mais je ne pense pas que ce soit un bon moyen, car je dois construire une zipinpustream, ce qui n'est évidemment pas bon. De plus, cette opération semble affecter InputStream, vous aurez donc des problèmes de lecture des fichiers DOC normaux. Ou vous utilisez l'objet de fichier pour déterminer s'il s'agit d'un fichier zip. Mais ce n'est pas un bon moyen non plus, car j'ai également besoin de lire le fichier DOC ou DOCX dans le fichier compressé, donc mon entrée doit être ENPUTSTREAM, donc cette option n'est pas OK non plus. J'ai parlé avec un groupe d'étrangers sur Stackoverflow pendant la plupart du temps. Parfois, je doutais vraiment de la capacité de ces étrangers à comprendre, mais en fin de compte, un gros coup m'a donné une solution qui m'a rendu extatique, filemagic. Ceci est une nouvelle fonctionnalité ajoutée à POI 3.17:
Public Enum fileMagic {/ ** ole2 / biff8 + Stream utilisé pour les documents Office 97 et supérieurs * / ole2 (HeaderblockConstants._Signature), / ** ooxml / zip stream * / ooxml (ooxml_file_hener), / ** xml Fichier * / xml (ROW_XML_FILE_HEADER), / ** BIFF-FORSUL_XML_FILE_HEADER), / ** BIFF-FORSUL_FUCL_FUL_ 2 */ BIFF2(new byte[]{ 0x09, 0x00, // sid=0x0009 0x04, 0x00, // size=0x0004 0x00, 0x00, // unused 0x70, 0x00 // 0x70 = multiple values }), /** BIFF3 raw stream - for Excel 3 */ BIFF3(new byte[]{ 0x09, 0x02, // 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, // inutilisé 0x70, 0x00 // 0x70 = plusieurs valeurs}, nouveau octet [] {0x09, 0x04, // sid = 0x0409 0x06, 0x00, // size = 0x0006 0x00, 0x00, // UNUSED 0x00, 0x01}), / ** old stream * used 0x00, 0x01}), / ** old stream * used 0x00, 0x01}), / ** old stream * used 0x00, 0x01}), / ** old stream * inutilisé 0x00, 0x01}), / *= MSWRITE( new byte[]{0x31, (byte)0xbe, 0x00, 0x00 }, new byte[]{0x32, (byte)0xbe, 0x00, 0x00 }), /** RTF document */ RTF("{//rtf"), /** PDF document */ PDF("%PDF"), // keep UNKNOWN always as Dernière énumération! / ** Magic inconnu * / inconnu (nouvel octet [0]); octet final [] [] magie; FileMagic (Long Magic) {this.magic = nouveau octet [1] [8]; Littleendian.putlong (this.magic [0], 0, magie); } FileMagic (byte [] ... magie) {this.magic = magic; } FileMagic (String Magic) {this (magic.getBytes (localeutil.charset_1252)); } public static fileMagic ValueOf (byte [] magique) {for (fileMagic fm: valeurs ()) {int i = 0; booléen trouvé = vrai; for (byte [] ma: fm.magic) {for (byte m: ma) {byte d = magic [i ++]; if (! (d == m || (m == 0x70 && (d == 0x10 || d == 0x20 || d == 0x40)))) {fondé = false; casser; }} if (trouvé) {return fm; }}} return inconnu; } / ** * Obtenez le fichier magique du fournit de l'entrée fourni (qui doit * prendre en charge la marque et la réinitialisation). <p> * * Si vous ne savez pas si votre entréestream prend en charge la marque / réinitialisation, * utilisez {@link #preparetocheckmagic (inputStream)} pour l'envelopper et vous assurer de toujours utiliser cela, et non l'original! potentiellement signifie, * que le flux zip a des octets indésirables principaux * * @param inp un entréestream qui prend en charge la marque / réinitialisation * / public static fileMagic Value of (inputStream inp) lance ioException {if (! inp.markupported ()) {lancement ioException ("getFileMagic () uniquement exploite sur les flux sur les flux qui prennent en charge la marque (int)"); } // Saisissez les 8 premiers octets octets [] data = ioutils.PeekFirst8Bytes (inp); return fileMagic.Valueof (data); } / ** * Vérifie si un {@Link InputStream} peut être réinitialisé (c'est-à-dire utilisé pour vérifier la magie de l'en-tête) et l'enveloppe s'il n'est pas * * @param stream stream à vérifier pour l'enveloppe * @return un stream activé de marque * / public static inputStream prepareTeTocheckMagic (Retour Stream) {if (stream.MarkSupporTed ()) {retour } // Nous avons utilisé pour traiter les données via un pushbackInputStream, mais le code utilisateur pourrait fournir un One // trop petit, donc nous utilisons un tampon de tampon à la place à la place, renvoyez-vous de nouvelles tampons de tampon (Stream); }}Voici le code principal, qui détermine principalement le type de fichier basé sur les 8 premiers octets de InputStream. Il n'y a aucun moyen de penser que c'est la solution la plus élégante. Au début, je pensais en fait que les premiers octets du fichier compressé semblaient être définis par un autre, MagicMumber. Étant donné que les dépendances de FileMagic sont compatibles avec la version 3.16, j'ai juste besoin d'ajouter cette classe, donc la bonne façon pour nous de lire le fichier Word maintenant est:
String statique privé 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 extracteur = new xwpfwordextractor (doc); text = extracteur.getText (); extracteur.close (); }} catch (exception e) {logger.error ("pour le fichier" + filepath, e); } enfin {if (is! = null) {is.close (); }} RETOUR Texte; } Exceller
Pour l'article Excel, je ne chercherai pas de comparaisons entre le plan précédent et le plan actuel. Je vais me donner les meilleures pratiques maintenant:
@SuppressWarnings ("Deprécation") String statique privé ReadExcel (String filepath, inputStream inp) lève une exception {Workbook WB; StringBuilder sb = new StringBuilder (); essayez {if (filepath.endswith (". xls")) {wb = new hssfworkbook (inp); } else {wb = streamingReader.builder () .RowCachesize (1000) // Nombre de lignes à garder en mémoire (par défaut à 10) .BuffeSize (4096) // Taille de tampon à utiliser lors de la lecture de l'entrée dans le fichier (par défaut à 1024) .Open (Inp); // inputStream ou fichier pour le fichier xlsx (requis)} sb = readsheet (wb, sb, filepath.endswith (". Xls")); wb.close (); } catch (ole2NotOfficexmlFileException e) {logger.error (filepath, e); } enfin {if (inp! = null) {inp.close (); }} return sb.toString (); } chaîne statique privée readExcelByFile (chaîne filepath, fichier de fichier) {workbook wb; StringBuilder sb = new StringBuilder (); essayez {if (filepath.endswith (". xls")) {wb = workbookfactory.create (file); } else {wb = streamingReader.builder () .RowCachesize (1000) // Nombre de lignes à conserver en mémoire (par défaut à 10) .BuffeSize (4096) // Taille de tampon à utiliser lors de la lecture de l'entrée dans le fichier (par défaut à 1024) .Open (fichier); // inputStream ou fichier pour le fichier xlsx (requis)} sb = readsheet (wb, sb, filepath.endswith (". Xls")); wb.close (); } catch (exception e) {logger.error (filepath, e); } return sb.toString (); } Private Static StringBuilder ReadSheet (Workbook WB, StringBuilder SB, Boolean ISXLS) lève une exception {pour (feuille de feuille: wb) {pour (row r: sheet) {for (Cell Cell: r) {if (cell.getCellType () == Cell.Cell_Type_String) {SB.Append (Cellule.GetStRringSther 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; }En fait, pour Excel Read, le plus gros problème auquel mon outil est confronté est le débordement de la mémoire. Souvent, la lecture de certains fichiers Excel particulièrement importants entraînera un problème de débordement de mémoire. Plus tard, j'ai finalement trouvé un excellent outil d'excellent-lisant-leader, qui peut rationaliser les fichiers XLSX et diviser certains fichiers particulièrement volumineux en petits fichiers à lire.
Une autre optimisation est que dans le scénario où les objets de fichier peuvent être utilisés, j'utilise des objets de fichier pour lire des fichiers au lieu d'utiliser InputStream pour les lire, car l'utilisation de l'entrée nécessite que toutes soient chargées en mémoire, donc cela prend beaucoup de mémoire.
Enfin, ma petite astuce consiste à utiliser Cell.getCellType pour réduire la quantité de données, car j'ai seulement besoin d'obtenir du contenu de chaîne de texte et de nombres.
Ce qui précède est quelques-unes de mes explorations et découvertes lors de la lecture de fichiers en utilisant POI, et j'espère que cela vous sera utile. Les exemples ci-dessus sont également appliqués dans l'un de mes outils partout (cet outil peut principalement vous aider à rechercher le texte intégral du contenu de votre ordinateur). Si vous êtes intéressé, vous pouvez jeter un œil. Bienvenue dans Star ou PR.
Résumer
Ce qui précède est l'intégralité du contenu de cet article. J'espère que le contenu de cet article a une certaine valeur de référence pour l'étude ou le travail de chacun. Si vous avez des questions, vous pouvez laisser un message pour communiquer. Merci pour votre soutien à wulin.com.