Préface (introduction en arrière-plan):
Apache POI est le prochain projet open source de la Fondation Apache, utilisé pour traiter les documents dans la série Office et peut créer et analyser des documents dans les formats Word, Excel et PPT.
Il existe deux technologies pour le traitement des documents de mots, à savoir HWPF (.DOC) et XWPF (.DOCX). Si vous connaissez ces deux technologies, vous devriez être en mesure de comprendre la douleur de l'utilisation de Java pour analyser les documents Word.
Deux des plus gros problèmes sont:
La première est que ces deux classes n'ont pas de classe et d'interface de parent unifiées (XSSF et HSSF à côté jettent leurs yeux méprisants), ils ne peuvent donc pas effectuer une programmation d'interface dans le même format;
La seconde est qu'il n'y a pas d'interface pour la position relative des images dans le document de l'API officielle, ce qui mène au fait que bien que vous puissiez obtenir toutes les images du document, vous ne pouvez pas savoir où se trouvent ces images. À l'avenir, vous ne pourrez pas insérer les images dans la bonne position.
Pour le premier point, je n'ai pas d'autre choix que d'étudier d'autres technologies connexes, telles que Jacob, DOC4J, etc., pour voir s'il existe d'autres solutions, mais DOC4J semble être en mesure de traiter les documents 2007 (.docx).
Pour le deuxième point, cet article me donnera la solution de l'auteur. En fait, c'est aussi le but de mon écriture cet article.
Remarque: il suffit de regarder le chapitre 2 et le chapitre 3 si vous demandez simplement la vitesse;
1. Connaissances de préparation
1. Les deux formats de documents de mots correspondent à deux méthodes de stockage différentes
Comme nous le savons tous, les documents Word ont deux formats de stockage: Doc et Docx
DOC: Il est communément appelé Word2003, qui utilise des données de stockage binaires ; Ce n'est pas l'objectif de notre discussion aujourd'hui.
DOCX: Word2007, utilise XML pour stocker les données et le format.
Peut-être que vous demanderez, pourquoi est-ce un format XML qui est évidemment un document se terminant à Docx?
C'est très simple: vous pouvez simplement sélectionner un fichier DOCX, cliquez avec le bouton droit pour l'ouvrir avec l'outil de compression, et vous pouvez obtenir une structure de répertoire comme ceci:
Vous pensez donc que DOCX est un document complet, mais en fait, ce n'est qu'un fichier compressé. (docx :? _?)
2. Le format de définition de XML dans les documents Word:
D'après l'exemple précédent, nous avons appris que les documents DOCX utilisent des fichiers compressés, c'est-à-dire XML pour décrire les données. Alors, comment les données des documents de mots sont-elles définies spécifiquement?
En raison de l'espace, l'ensemble du document comprimé ne sera pas décrit en détail ici. Je ne présenterai que brièvement deux fichiers / dossiers:
Tout d'abord, le fichier document.xml dans le répertoire Word, qui est la définition de l'ensemble du contenu du document;
Le second est le dossier multimédia dans le répertoire des mots. Vous pouvez deviner le contenu multimédia du document en regardant le nom:
Figure 3: Word / Document.xml (Définissez le contenu du document)
Figure 4: Contenu dans le dossier Word / Media
Voici un contenu clé du document Document.xml:
R: Définition de la structure globale du document:
<W: Document MC: ignorable = "W14 W15 WP14" xmlns: m = "http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns: mc = "http://schemas.openxmlformats.org/markup-compatibilité/2006" xmlns: o = "urn: schémas-microsoft-Com: Office: Office" xmlns: r = "http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns: w = "http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns: w10 = "urn: schémas-microsoft-com: bureau: word" xmlns: w14 = "http://schemas.microzoft.com/office/word/2010" xmlns: w15 = "http://schemas.microsoft.com/office/word/2012/wordml" xmlns: wne = "http://schemas.microsoft.com/office/word/2006/wordml" xmlns: wp = "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns: wp14 = "http://schemas.microsoft.com/office/word/20/wordprocessedrawing" xmlns: wpg = "http://schemas.microsoft.com/office/word/2010/wordprocessingcanvas" xmlns: wpg = "http://schemas.microsoft.com/office/word/2010/wordprocessggroup" xmlns: wpi = "http://schemas.microsoft.com/office/word/2010/wordprocessingink" xmlns: wps = "http://schemas.microsoft.com/office/word/2010/wordprocessshape" xmlns: wpsCustomData = "http://www.wps.cn/officedocument/2013/wpscustomdata"> <w: body> <w: p> <w: ppr> <w: pstyle w: Val = "2"> </ w: pstyle> <w: keepnext w: val = "0"> </ w: gardien> <w: keepnext w: val = "0"> </ w: gardien> w: val = "0"> </ w: keeplines> <w: widowControl> </ w: widowControl> <w: suppresslineNumbers w: Val = "0"> </ w: supresslineNumbers> <w: pbdr> <w: top w: colore = "auto" w: spatial = "0" w: sz = "0" w: Val = "nul"> </ w: <w: left: Left ' w: color = "auto" w: spatial = "0" w: val = "non"> </ w: inférieur> <w: droit w: color = "auto" w: spatial = "0" w: sz = "0" w: val = "non"> </ w: droite> </ w: pbdr>
B: Contenu du paragraphe de documents:
<w: p> <w: ppr> <w: pstyle w: val = "2"> </ w: pstyle> <w: keepNext w: val = "0"> </ w: keepnext> <w: keeplines w: val = "0"> </ w: keeplines> <w: widowControl> </ w: widowControl> <W: supprimelineninembers </ w: suppresslineNumbers> <w: pbdr> <w: top w: colore = "auto" w: space = "0" w: sz = "0"> </ w: top> <w: gauche w: colore = "auto" w: spatial = "0" w: sz = "0"> w: val = "nothe"> </ w: gauche> <w: bott w: sz = "0" w: val = "non"> </ w: inférieur> <w: droit w: color = "auto" w: space = "0" w: sz = "0" w: val = "non"> </ w: droit> </ w: pbdr> <w: shd w: fill = "fafafa" w: Val = "clear"> </ w: shd> <w: SPAFIC w: afterAutoSpacte = "0" w: avant = "150" w: beforeAutoSpacing = "0" w: line = "378" w: linerule = "atleast"> </ w: espacement> <w: ind w: firstline = "0" w: Left = "0" w: droite = "0"> </ w: inde <w: rpr> <w: rfonts w: Ascii = " w: cs = "verdana" w: hansi = "verdana" w: hint = "default"> </ w: rfonts> <w: iw: val = "0"> </ w: i> <w: caps w: val = "0"> </ w: caps> <w: couleur w: val = "4040"> </ w: colore> <w: spaçage w: val = "0"> </ w: espacement> <w: sz w: val = "21"> </ w: sz> <w: szcs w: val = "21"> </ w: szcs> </ w: rpr> </ w: ppr> <w: rpr> <w: rpr> <w: rpr> <w: rfont w: ascii = "verdana" w: cs = "verdana" W: hansi = "verdana" w: hint = "default"> </ w: rfonts> <w: iw: val = "0"> </ w: i> <w: caps w: val = "0"> </ w: caps> <w: couleur w: val = "404040"> </ w: colore> <w: espacement w: Val = "0"> </ w: <w: S: Espace w: Val = "0"> </ w: Spacing> <w: Sz w: val = "21"> </ w: sz> <w: szcs w: val = "21"> </ w: szcs> <w: bdr w: colore = "auto" w: space = "0" w: sz = "0" w: val = "nothe"> </ w: bdr> <w: shd w: flow = "fafa" w: val = "clear"> <w: shd " </ w: rpr> <w: t> Auteur: Brian cher </ w: t> </ w: r> </ w: p>
C: Définition du contenu de l'image:
<w: r> <w: rpr> <w: rfonts w: ascii = "verdana" w: cs = "verdana" w: hansi = "verdana" w: hint = "default"> </ w: rfonts> <w: iw: val = "0"> </ w: i> <w: caps w: val = "0"> </ w: caps> W: val = "404040"> </ w: couleur> <w: espacement w: val = "0"> </ w: espacement> <w: sz w: val = "21"> </ w: sz> <w: szcs w: val = "21"> </ w: szcs> <w: bdr w: colore = "auto" w: space = "0" w: sz = "0" "" w: val = "non"> </ w: bdr> <w: shd w: fill = "fafafa" w: val = "clear"> </ w: shd> </ w: rpr> <w: drawing> <wp: inline Distb = "0" Distl = "114300" Dist = "114300" Dist = "0"> <wp: Extent Cx = "5543550" cy = "5543550"> </ wp: extension> <wp: effextent b = "0" l = "0" r = "0" t = "0"> </ wp: effextent> <wp: docpr descr = "IMG_256" id = "1" name = "image 1"> </p: docpr> <wp: cnvgra <A: GraphicFramelocks nochangeSpect = "1" xmlns: a = "http://schemas.openxmlformats.org/drawingml/2006/main"> </ a: graphicframelocks> xmlns: a = "http://schemas.openxmlformats.org/drawingml/2006/main"> <a: graphicdata uri = "http://schemas.openxmlformats.org/drawingml/2006/picture"> <pic: pic: Pic: xmlns: pic = "http://schemas.openxmlformats.org/drawingml/2006/picture"> <pic: nvpicpr> <pic: cnvpr descr = "img_256" id = "1" name = "image 1"> </ pic: cnvpr> <pic: cnvpicpr> nochangeaspect = "1"> </ a: piclocks> </ pic: cnvpicpr> </ pic: nvpicpr> <pic: blipfill> <a: blip r: embed = "rid4"> </ a: blip> <a: stretch> <a: fillrect> </s: fillrect> </s: stretch> </ pic: blipfill> </ a: xfr> <a: stretch> </ing <a: off x = "0" y = "0"> </ a: off> <a: ext cx = "5543550" cy = "5543550"> </ a: ext> </ a: xfrm> <a: prstgeom prst = "rect"> <a: avlst> </a: avlst> </a: prstgeom> <a: ln w = "9525"> <a: nofill> </ a: nofill> </ a: ln> </ pic: sppr> </ pic: pic> </ a: graphicData> </ a: graphic> </wp: inline> </ w: drawing> </w: r> r>
Si vous êtes intéressé, vous pouvez consulter les trois codes XML ci-dessus. Je donnerai la conclusion directement ici:
Document Word Fichier Shema: xmlns: w = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
Node racine du document: <W: Document> Définit le début de l'ensemble du document
<w: body> est le nœud enfant du document et le contenu principal du document
<w: p> nœud d'enfant du corps, un paragraphe, est le paragraphe du document Word
<w: r> nœud enfant de l'élément p, une course définit un paragraphe avec le même format dans le paragraphe
<W: T> Le nœud enfant du nœud d'élément RUN est le contenu du document.
<w: dessin> nœud enfant de l'élément de course, définit une image:
<W: en ligne> dessin des nœuds enfants, aucune recherche approfondie n'a été effectuée sur l'application spécifique.
<a: graphique> Définir le contenu de l'image
<pic: blipfill> Il s'agit d'un nœud enfant du document graphique, qui définit l'index du contenu de l'image. Plus précisément, POI peut obtenir les ressources correspondantes de l'image en fonction de ce nom, et la clé pour obtenir l'emplacement de l'image du document est ici.
Dans l'ensemble, les documents DOCX d'analyse XWPF consistent à analyser le document XML, à enregistrer tous les nœuds, puis à les convertir en propriétés plus utiles, à fournir une API à utiliser.
Nous pouvons donc utiliser l'interface fournie par POI pour obtenir le contenu du document, analyser les données du document vous-même et obtenir dans quel paragraphe l'image se trouve. Bien sûr, vous pouvez également savoir quel élément d'exécution est situé.
2. Réalisation
package com.szdfhx.reportstatistic.util; import com.microsoft.schemas.vml.ctshape; import org.apache.poi.xwpf.usermodel.xwpfparagraph; import org.apache.poi.xwpf.usermodel.xwpfpicturedata; Importer; org.apache.poi.xwpf.usermodel.xwpfrun; import org.apache.xmlbeans.xmlcursor; import org.apache.xmlbeans.xmlobject; import org.openxmlformats.schemas.drawingml.x2006.main.ctgraphicalobject; import; org.openxmlformats.schemas.drawingml.x2006.picture.ctpicture; import org.openxmlformats.schemas.drawingml.x2006.main.ctdrawing; import org.openxmlformats.schemas.wordprocessingml.x2006.main.ctobject; org.openxmlformats.schemas.wordprocessingml.x2006.main.ctr; import java.util.arraylist; import java.util.list; import java.util.list; import java.util.map; public class xwpfutils {// Obtenir tous les indices d'image dans un certain paragraph LeImageInParagraph (paragraphe xwpfParagraph) {// INDEX INDEX LISTE <string> ImageBundlelist = new ArrayList <string> (); // Tous les xwpfrun liste <xwpfrun> dans paragraph runlist = paragraph.getRuns (); pour (xwpfrun run: runlist) {// xwpfrun est son propre attribut généré par POI après l'analyse de l'élément XML. Il ne peut pas être analysé à travers XML. Il doit être converti en Ctr Ctr = run.getCtr (); // Transaction des éléments enfants xmlcursor c = ctr.newCursor (); // Ceci est pour obtenir tous les éléments enfants: C.SelectPath ("./*"); while (c.tonextSelection ()) {xmlobject o = c.getObject (); // Si l'élément enfant est sous la forme de <W: Drawing>, utilisez CTDRAWING pour enregistrer l'image if (o instanceof ctdrawing) {ctdrawing drawing = (ctdrawing) o; Ctinline [] ctinlines = drawing.getInLineArray (); pour (ctinline ctinline: ctinlines) {ctgraphicalObject graphic = ctinline.getGraphic (); // xmlcursor cursor = graphic.getGraphicData (). NewCursor (); Cursor.SelectPath ("./*"); while (cursor.tonextSelection ()) {xmlobject xmlobject = cursor.getObject (); // Si l'élément enfant est dans la forme <pic: pic> if (xmlobject instanceof ctpicture) {org.openxmlformats.schemas.drawingml.x2006.picture.ctpicture image = (org.openxmlformats.schemas.Drawingml.x2006.picture.cTpitture) // Obtenez l'attribut de l'élément imagebundlelist.add (image.getBlipFill (). GetBlip (). GetEmbed ()); }}}}} // Utilisez ctObject pour enregistrer l'image // <w: objet> formulaire if (o instanceof ctObject) {ctObject object = (ctObject) o; System.out.println (objet); XmlCursor w = object.newCursor (); W.SelectPath ("./*"); while (w.tonextSelection ()) {xmlobject xmlobject = w.getObject (); if (xmlobject instanceof CTShape) {CTShape Shape = (ctShape) xmloBject; ImageBundleList.Add (Shape.GetImagedataArray () [0] .getId2 ()); }}}}} return ImageBundleList; }}Tout d'abord, nous devons proposer l'encapsulation des éléments XML par XWPF:
<W: Document> correspondant à la classe XWPFDocument
<w: run> correspondant à la classe xwpfrun
Fondamentalement, il ne correspond qu'à la couche de course. Parce qu'il existe de nombreux éléments enfants de l'exécution, il n'y a plus d'encapsulation et de définition au niveau suivant.
Nous ne pouvons donc obtenir que tous les objets XWPFRUN convertis dans sa définition XML: CTR objets. Enfin, utilisez CTR pour lire et analyser le contenu de l'élément d'exécution et obtenir l'index de l'image.
La deuxième chose à dire est la définition de l'élément XML entier:
Nous pouvons voir que POI utilise XML analysé par la technologie XMLBeans sous Apache. Si vous ne discutez pas en profondeur des technologies connexes, vous devez comprendre deux points clés:
1: Tous les éléments du document XML sont encapsulés par le XMLBean et héritent d'une interface XMLObject, afin que cette classe puisse être utilisée pour recevoir les éléments enfants acquis;
2: La traversée des éléments se fait via XMLCursor. L'acquisition spécifique des éléments enfants est contrôlée en fonction de l'attribut SelectPath de l'objet XMLCursor. Lorsque le SelectPath est "./*", il est défini comme traversant des éléments enfants;
Il est donc écrit comme suit: il peut traverser les éléments enfants de l'élément actuel et vérifier le type de l'élément enfant:
Ctr Ctr = run.getctr (); // transsulez les éléments de l'enfant xmlcursor c = ctr.newcursor (); // c'est pour obtenir tous les éléments enfants: C.SelectPath ("./*"); while (c.tonextSelection ()) {xmlobject o = c.getObject (); // si l'élément enfant est dans la forme <w: dessin>, utilisez ctdrawing pour enregistrer l'image if (o instanceof ctdrawing) {ctdrawing drawing = (ctdrawing) o;Enfin, vous avez peut-être des questions, cet élément <W: Drawing> Définit une image?
Donc
if (o instanceof ctObject) {ctObject object = (ctObject) o; ...}À quoi sert la deuxième condition de jugement?
Tu aurais dû le deviendre
C'est exact! En plus de <W: Drawing>, le XML dans le document DOCX peut également être utilisé pour définir l'image.
Pourquoi n'y a-t-il que ces deux?
Parce que je n'ai utilisé que la première méthode pour analyser, j'ai trouvé que certaines photos étaient perdues, alors j'ai trouvé la deuxième méthode ... peut-être qu'il y en a plus de deux? Je ne sais pas, de toute façon, il n'y a aucun problème pour moi en ce moment.
Peut-être que vous, qui êtes intelligent, avez rencontré plus de situations dans la pratique?
Ensuite, en utilisant la méthode d'analyse XML mentionnée ci-dessus, je pense que vous pouvez le lire correctement et obtenir la valeur d'index souhaitée.
Élargissez-le un peu. S'il existe d'autres API qui ne sont pas fournies par POI, pouvons-nous également les implémenter via la technologie d'analyse XML? Cela nous oblige à explorer dans la pratique. Je crois que le temps nous donnera la réponse.
D'accord, maintenant nous avons la valeur d'index, alors comment obtenir les ressources d'image?
POI fournit des méthodes prêtes à l'emploi:
Il y a getPictureDatabyId (image de chaîne) dans la classe XWPFDocument;
La méthode peut obtenir l'objet XWPFPICTRUEDate, qui est la ressource d'image.
Pour des opérations spécifiques, veuillez vous référer aux articles de blog et API concernés, qui ne seront pas introduits en détail ici.
3. Test:
Code à tester à l'aide de JUnit4:
package com.szdfhx.reportstatistic.util; import org.apache.comons.collections.collectionutils; import org.apache.commons.lang.stringutils; import org.apache.poi.xwpf.usermodel.xwpfdocument; import; org.apache.poi.xwpf.usermodel.xwpfparagraph; import org.apache.poi.xwpf.usermodel.xwpfpicturedata; import org.junit.test; import java.io.fileInputStream; import java.io.ioexect; import java.io.inputStream; importation; java.util.collections; import java.util.list; import static org.junit.assert. *; public class xwpfutilstest {@test public void releImageInParagraph () lance ioException {inputStream dans = new FileInputStream ("d: // document // mon blog // java paring word, get the Image Lieu document // example.docx "); XwpfDocument xwpfDocument = new xwpfDocument (in); List <xwpfParagraph> paragraphList = xwpfDocument.getParagraphs (); System.out.println ("Image index / t | Nom d'image / T | Contenu d'un paragraphe de texte sur l'image / t"); System.out.pringln("------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ if (CollectionUtils.isnotempty (ImageBundleList)) {pour (String PictureId: ImageBundlelist) {xwpfpiCturedata pictureData = xwpfDocument.getPictureDataDid (pictureId); System.out.println (PictureId + "/ T |" + ImageName + "/ T |" + LastParagraphText);Afficher les résultats:
L'utilisation de noms d'images ici signifie que j'ai obtenu les ressources correspondantes. En fait, si vous connaissez le contenu de l'article précédent, vous constaterez que le nom de l'image est en fait le nom complet de toutes les images du dossier Word / Media.
Dans l'objet XWPFPictureData correspondant, les données binaires de l'image peuvent être obtenues via la propriété GetData (), afin que vous puissiez l'enregistrer dans la base de données ou votre dossier local!
4. Autres:
En parlant de cela, le deuxième problème mentionné au début a été résolu ici.
Alors, que dois-je faire avec la première question?
Si votre système ne nécessite pas de vitesse élevée, mon conseil est de convertir le document DOC en document DOCX en Parse - POI a une API mature à faire
Si vous souhaitez envisager des performances, vous devez écrire deux ensembles de méthodes pour analyser le document.
Alors ... comment obtenir la position relative de l'image dans un document Word de type DOC?
Je ne sais pas ... ou, venez me dire?
Dans le mot d'analyse Java ci-dessus, la méthode pour obtenir l'emplacement de l'image dans le document est tout le contenu que je partage avec vous. J'espère que vous pourrez vous faire référence et j'espère que vous pourrez soutenir Wulin.com plus.