Vorwort (Hintergrundeinführung):
Apache POI ist das nächste Open -Source -Projekt der Apache Foundation, das zur Verarbeitung von Dokumenten in der Office -Serie verwendet wird und Dokumente in Word-, Excel- und PPT -Formaten erstellen und analysieren kann.
Es gibt zwei Technologien zur Verarbeitung von Wortdokumenten, nämlich HWPF (.DOC) und XWPF (.docx). Wenn Sie mit diesen beiden Technologien vertraut sind, sollten Sie in der Lage sein, den Schmerz zu verstehen, Java zu verwenden, um Wortdokumente zu analysieren.
Zwei der größten Probleme sind:
Das erste ist, dass diese beiden Klassen keine einheitliche Elternklasse und Schnittstelle (XSSF und HSSF nebenan ihre verächtlichen Augen werfen), sodass sie keine Schnittstellenprogrammierung im selben Format durchführen können.
Die zweite ist, dass es keine Schnittstelle für die relative Position der Bilder im Dokument in der offiziellen API gibt, was dazu führt, dass Sie zwar alle Bilder im Dokument erhalten können, Sie jedoch nicht wissen, wo sich diese Bilder befinden. In Zukunft können Sie die Bilder nicht in die richtige Position einfügen.
Für den ersten Punkt habe ich keine andere Wahl, als andere verwandte Technologien wie Jacob, Doc4j usw. zu untersuchen, um festzustellen, ob es andere Lösungen gibt, aber Doc4j scheint in der Lage zu sein, 2007 Dokumente (.docx) zu verarbeiten.
Für den zweiten Punkt gibt mir dieser Artikel die Lösung des Autors. Tatsächlich ist dies auch der Zweck meines Schreibens dieses Artikels.
Hinweis: Schauen Sie sich einfach Kapitel 2 und Kapitel 3 an, wenn Sie einfach nur nach Geschwindigkeit bitten.
1. Vorbereitungswissen
1. Die beiden Formate von Wortdokumenten entsprechen zwei verschiedenen Speichermethoden
Wie wir alle wissen, haben Word -Dokumente zwei Speicherformate: doc und docx
DOC: Es wird allgemein Word2003 bezeichnet, das Binärspeicherdaten verwendet. Dies ist nicht der Schwerpunkt unserer heutigen Diskussion.
DOCX: Word2007, verwendet XML , um Daten und Format zu speichern.
Vielleicht werden Sie fragen, warum es ein XML -Format ist, das offensichtlich ein Dokument ist, das bei DOCX endet?
Es ist sehr einfach: Sie können einfach eine DOCX-Datei auswählen, mit der rechten Maustaste klicken, um sie mit dem Komprimierungstool zu öffnen, und Sie können eine Verzeichnisstruktur wie folgt erhalten:
Sie glauben also, dass docx ein vollständiges Dokument ist, aber tatsächlich nur eine komprimierte Datei. (docx:? _?)
2. Das Definitionsformat von XML in Wortdokumenten:
Aus dem vorherigen Beispiel haben wir erfahren, dass DOCX -Dokumente komprimierte Dateien verwenden, dh XML, um Daten zu beschreiben. Wie werden die Daten in Word -Dokumenten speziell definiert?
Aus dem Raum wird das gesamte komprimierte Dokument hier nicht ausführlich beschrieben. Ich werde nur kurz zwei Dateien/Ordner vorstellen:
Zunächst ist die document.xml -Datei im Word -Verzeichnis, das die Definition des gesamten Dokumentinhalts ist.
Der zweite ist der Medienordner im Wortverzeichnis. Sie können den Multimedia -Inhalt im Dokument erraten, indem Sie sich den Namen ansehen:
Abbildung 3: word/document.xml (Dokumentinhalt definieren)
Abbildung 4: Inhalt im Wort/Medienordner
Im Folgenden finden Sie einige Schlüsselinhalte des Dokuments.xml -Dokuments:
A: Dokument Gesamtstrukturdefinition:
<W: Dokument MC: Ignoring = "W14 W15 WP14" XMLNS: m = "http://schemas.openxmlFormats.org/officedocument/2006/math" xmlns: mc = "http://schemas.openxmlformats.org/Markup-Compatibility/2006" xmlns: o = "Urn: Schemas-microsoft-com: Büro: Büro" xmlns: r = "http://schemas.openxmlFormats.org/officedocument/2006/relationships" xmlns: v = "Urn: Schemas-microsoft-com: vml" xmlns: w = "http://schemas.openxmlFormats.org/wordprocessingml/2006/main" xmlns: w10 = "Urn: Schemas-microsoft-com: Office: Word" xmlns: w14 = "http://Schemas.microsoft.mic2/210//Schemas.microsoft.com/word/20//Schemas. 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/2010/wordprocessingdrawing" xmlns: wpg = "http://schemas.microsoft.com/office/word/2010/wordprocessingcanvas" xmlns: wpg = "http://schemas.microsoft.com/office/2010/wordprocessinggroup" xmlns: wpi = "http://schemas.microsoft.com/office/word/2010/wordprocessing" xmlns: wps = "http://schemas.microsoft.com/office/word/2010/wordprocessingsshape" xmlns: wpscustomdata = "http://www.wps.cn/officedocument/2013/wpscustomdata"> <w: body> <w: p> <w: ppr> <w: pstyle w: val = "2"> </w: pstyle> <w: style <w: W: W: W: W: W: W: W: W: W: W: W: W: W: W: val = "0" 0 "> </w: pstyle> <w: w: w: w: w: w: val =" 0 "> </w: pstyle> <w: w: w: w: b: val =" 0 "0> </w: pstyle> <w: W: W: W: W: W: W: W. <W: Keepines w: val = "0"> </w: Keepline> <w: WitowControl> </w: WitowControl> <w: SuppressLinEnumbers W: val = "0"> </w: SuppressLinumbers> <w: pbdr> <w: top w: color = "auto" w: space = "0" 0 "0" 0 "0" 0 "0" 0 " <w: links w: color = "auto" w: space = "0" w: val = "none"> </w: bohrung> <w: rechts w: color = "auto" w: space = "0" w: sz = "0" w: val = "none"> </w: rechts> </w: pbdr>
B: Dokumentabsatzinhalt:
<w: p> <w: ppr> <w: pstyle w: val = "2"> </w: pStyle> <w: keepnext w: val = "0"> </w: keeNext> <w: keepline w: val = "0"> </w: keepines> <w: widowcontrol> </w: witowcontrol> <w: widowcontrol> </w: widowcontrol> <w: sugenlinienw: widowcontrol> </w: widowcontrol> <w: sugenlinienw: widowcontrol> </w: widowcontrol> <w: sugenlinungen w: wh: widowcontrol> <W: widowcontrol> </w: widowcontrol> <w: sugenlinungen </w: SuppressLinEnumber> <w: pbdr> <w: top w: color = "auto" w: space = "0" w: sz = "0"> </w: top> <w: links w: color = "auto" w: space = "0" w: sz = "0"> w: val = "> </w: b: bd: b: b: bd: b: b: bd: b: b: b: bd: b: b: b: b: b: b: b: b: b: b: b: b: b: bo: w: b: bd: bd: b: b: bust W: sz = "0" W: val = "none"> </w: boden> <w: rechts W: color = "Auto" W: space = "0" W: sz = "0" w: val = "keine"> </w: rechts> </w: pbdr> <w: shd w: fill = "fafafa" w: val = "> </w: SHD> <W: spacing w:" W: Afterautospacing = "0" W: vor = "150" W: BeforeAtospacing = "0" W: Line = "378" W: Linerule = "mindestens"> </w: Abstand> <w: ind w: firstline = "0" W: links = "0" W: rechts = "0"> </w: ind> <w: rprikte <w: rf. 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:color w:val="404040"> </w:color> <w:spacing w:val="0"> </w: Abstand> <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: rfonts w: ascii = "w: cs =" w: cs = "w: cs" "" W: cs "" "" W: CSA " W: hansi = "Verdana" W: Hint = "default"> </w: rfonts> <w: iw: val = "0"> </w: i> <w: caps w: val = "0"> </w: Caps> <w: color w: val = "404040"> </w: color> <w: sal W: val = "21"> </w: sz> <w: szcs w: val = "21"> </w: szcs> <w: bdr w: color = "auto" w: space = "0" w: sz = "0" w: val = "keine" </w: rpr> <w: t> Autor: Brian Lieber </w: t> </w: r> </w: p>
C: Bildinhaltsdefinition:
<w: r> <w: rpr> <w: rfonts w: ascii = "Verdana" W: cs = "Verdana" W: hansi = "Verdana" W: Hint = "Standard"> </w: rfonts> <w: iw: val = "0"> </w: i> <w: caps w: val = "404040"> </w: color> <w: Abstand W: val = "0"> </w: Abstand> <w: sz w: val = "21"> </w: sz> <w: szcs w: val = "21"> </w: szcs> <w: bddr w: color = " w: val = "none"> </w: BDR> <w: shd w: fill = "fafafa" w: val = "clear"> </w: shd> </w: rpr> <w: zeichnen> <wp: inline distb = "0" distl = "114300" dist = "114300" distb = "0"> <"<"> <WP: Ausgeschwindigkeit CX = "554300" Cy = "5543550"> </wp: Ausdehnung> <wp: effectextent b = "0" l = "0" r = "0" t = "0"> </wp: effectextent> <wp: docpr descr = "img_256" id = "1" name = "picture 1"> </wp. NoChaNeAsPep = "1" xmlns: a = "http://schemas.openxmlFormats.org/drawingml/2006/main"> </a: graphicframelocks> </wp: cnvgraphicFramePr> <a: Graphicics> </wp: cnvgraphicFramePr> <a: graphicics xmlns: a = "http://schemas.openxmlFormats.org/drawingml/2006/main"> <a: graphicData uri = "http://schemas.openxmlformats.org/drawingml/2006/picture"> < xmlns: pic = "http://schemas.openxmlFormats.org/drawingml/2006/picture"> <pic: nvpicpr> <pic: cnvppr descr = "img_256" id = "1" name = "piction 1"> </pic: cnvvPr> <pic: NoChaNeasspect = "1"> </a: piclocks> </pic: cnvpicPr> </pic: nvpicpr> <pic: blipfill> <a: blip r: embed = "rid4"> </a: blip> <a: striet> <a: fillRect> </a: fillrect> </a: · a: a: · a. <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:nofill> </a:nofill> <a: ln w = "9525"> <a: nofill> </a: nofill> </a: ln> </pic: sppr> </pic: pic> </a: graphicData> </a: graphic> </wp: inline> </w: Zeichnung> </w: r>
Wenn Sie interessiert sind, können Sie sich die oben genannten drei XML -Codes ansehen. Ich werde die Schlussfolgerung hier direkt zu ziehen:
Word -Dokument Shema -Datei: xmlns: w = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
Dokument Root Node: <W: Dokument> Definiert den Beginn des gesamten Dokuments
<W: Body> ist der untergeordnete Knoten des Dokuments und der Hauptinhalt des Dokuments
<W: P> Körperkörperknoten, ein Absatz, ist der Absatz im Word -Dokument
<w: r> untergeordneter Knoten von P -Element, ein Lauf definiert einen Absatz mit demselben Format im Absatz
<W: T> Der untergeordnete Knoten des Run -Element -Knotens ist der Inhalt des Dokuments.
<W: Zeichnen> Kinderknoten des Laufelements definiert ein Bild:
<W: Inline> Kinderknoten zeichnen, es wurde keine eingehende Forschung an der spezifischen Anwendung durchgeführt.
<A: Grafik> Bildinhalt definieren
<pic: blopfill> Dies ist ein untergeordneter Knoten des Grafikdokuments, der den Index des Bildinhalts definiert. Insbesondere kann POI die entsprechenden Ressourcen des Bildes basierend auf diesem Namen erhalten, und der Schlüssel zum Erhalten der Position des Dokumentbildes ist hier.
Insgesamt soll XWPF -DOCX -Dokumente das XML -Dokument analysieren, alle Knoten speichern und sie dann in nützlichere Eigenschaften umwandeln, wodurch die Nutzer API bereitgestellt werden können.
So können wir die von POI zur Verfügung gestellte Schnittstelle verwenden, um den Dokumentinhalt zu erhalten, die Daten im Dokument selbst analysieren und in welchem Absatz das Bild befindet. Natürlich können Sie auch wissen, in welchem Laufelement sich das Bild befindet.
2. Realisierung
Paket com.szdfhx.reportStatistic.util; import com.microsoft.schemas.vml.ctshape; import org.apache.poi.xwpf.usermodel.xwpf -paragraph; import org. org.apache.poi.xwpf.usermodel.xwpfrun; import org.apache.xmlbeans.xmlcursor; import org.apache.xmlbeans.xmlobject; org.openxmlFormats.schemas.drawingml.x2006.picture.ctpicture; import org.openxmlformats.schemas.drawingml.x2006.main.ctdrawing; org.openxmlFormats.schemas.wordprocessingml.x2006.main.ctr; import Java.util.ArrayList; import Java.util.list Java.util.list ;list importieren java.util.map; öffentliche Klasse xWPFutil. ReadImageInTrapriph (XWPFTrateRAGHAPH -Absatz) {// Bildindexliste <string> ImageBundLelist = new ArrayList <string> (); // Alle XWPFrun -Liste <xwpfrun> in Absatzrunlist = Absatz.getruns (); für (xwpfrun run: runlist) {// xwpfrun ist ein eigenes Attribut, das nach dem Parsen des XML -Elements erzeugt wird. Es kann nicht durch XML analysiert werden. Es muss in ctr ctr = run.getCtr () konvertiert werden; // Transaktion von untergeordneten Elementen xmlCursor c = ctr.newcursor (); // Dies ist, um alle Kinderelemente zu erhalten: C.SelectPath ("./*"); while (c.tonextSelection ()) {xmlobject o = c.GetObject (); // Wenn sich das untergeordnete Element in Form von <w: Drawing> befindet, speichern Sie das Bild, um das Bild zu speichern, wenn (o Instanz von ctdrawing) {ctdrawing Drawing = (ctdrawing) o; Ctinline [] ctinlines = Drawing.getInlineArray (); für (ctinline ctinline: ctinlines) {ctgraphicalObject graphic = ctinline.getGraphic (); // xmlCursor cursor = graphic.getGraphicData (). NewCursor (); Cursor.SelectPath ("./*"); while (cursor.tonextSelection ()) {xmlobject xmlobject = cursor.getObject (); // Wenn sich das untergeordnete Element in der Form <pic: pic> if (xmlobjectinstance von ctpicture) {org.openxMlFormats.schemas.DrawingMl.x2006.Picture.Ctpicture picture = (org.openxmlFormats.Schemas.drawingml.x2006.Picture.Chemas.Chemas.Pict.X2006.Picture.Chemas.Chemas.Pict.X2006.Picture.Ctemas.Chemas.Pict.x2006.Picture.Pict.Picture) -Picts.Picture.Pict.Picture.Picture.Picture) -Pict -. // Erhalten Sie das Attribut des Elements imageBundLelist.add (picture.getBlipfill (). GetBlip (). Getembed ()); }}}}} // Verwenden Sie CTObject, um das Bild zu speichern // <w: Objekt> Form if (o Instanceof ctObject) {ctObject Object = (ctObject) o; System.out.println (Objekt); XmlCursor w = Object.newCursor (); W.SelectPath ("./*"); while (w.tonextSelection ()) {xmlobject xmlobject = w.getObject (); if (xmlobjectinstance von ctShape) {CTSHAPE SHAPE = (CTSHAPE) XMLObject; ImageBundLelist.Add (Shape.GetImagedatArray () [0] .GetID2 ()); }}}}} return ImageBundLelist; }}Zunächst müssen wir die Einkapselung von XML -Elementen von XWPF vorschlagen:
<W: Dokument> entspricht der XWPFDocument -Klasse
<W: Run> entspricht der XWPFRun -Klasse
Grundsätzlich entspricht es nur der Laufschicht. Da es viele Kinderelemente gibt, gibt es auf der folgenden Ebene keine Einkapselung und Definition mehr.
So können wir nur alle XWPFrun -Objekte in seine XML -Definition konvertieren lassen: CTR -Objekte. Verwenden Sie schließlich CTR, um den Inhalt des Laufelements zu lesen und analysieren und den Index des Bildes erhalten.
Die zweite Sache, über die Sie sprechen sollten, ist die Definition des gesamten XML -Elements:
Wir können sehen, dass POI XML von der XMLBeans -Technologie unter Apache verwendet. Wenn Sie die verwandten Technologien nicht eingehend diskutieren, müssen Sie zwei wichtige Punkte verstehen:
1: Alle Elemente im XML -Dokument werden von der XMLBean und einer XMLObject -Schnittstelle erben, sodass diese Klasse verwendet werden kann, um die erworbenen untergeordneten Elemente zu erhalten.
2: Element -Traversal erfolgt durch XMLCursor. Die spezifische Erfassung von untergeordneten Elementen wird basierend auf dem SelectPath -Attribut des XMLCursor -Objekts kontrolliert. Wenn der SelectPath "./*" ist, wird er als durchquerende Kinderelemente definiert.
Es ist also wie folgt geschrieben: Es kann die untergeordneten Elemente des aktuellen Elements durchqueren und den Typ des untergeordneten Elements überprüfen:
Ctr ctr = run.getCtr (); // transulieren die untergeordneten Elemente xmlcursor c = ctr.newcursor (); // Dies soll alle untergeordneten Elemente erhalten: C.SelectPath ("./*"); while (c.tonextSelection ()) {xmlobject o = C.GetObject (); // Wenn sich das untergeordnete Element in der Form <w: Zeichnung> befindet, speichern Sie das Bild, wenn (o Instanz von ctdrawing) {ctdrawing Drawing = (ctdrawing) o;Schließlich haben Sie möglicherweise Fragen, hat dieses Element <W: Draw> ein Bild nicht definiert?
Also
if (o Instance von ctObject) {ctObject Object = (ctObject) o; ...}Wofür ist die zweite Beurteilungsbedingung?
Sie hätten es erraten sollen
Das stimmt! Zusätzlich zu <W: Drawing> kann das XML im DOCX -Dokument auch zum Definieren des Bildes verwendet werden.
Warum gibt es nur diese beiden?
Da ich nur die erste Methode verwendet habe, um analysiert zu werden, fand ich, dass einige Bilder verloren gingen, also fand ich die zweite Methode ... vielleicht gibt es mehr als zwei? Ich weiß sowieso nicht, es gibt im Moment kein Problem für mich.
Vielleicht haben Sie, die schlau sind, in der Praxis mehr Situationen gestoßen?
Wenn Sie dann die oben erwähnte XML -Parsenmethode verwenden, können Sie sie richtig lesen und den gewünschten Indexwert erhalten.
Erweitern Sie es ein wenig. Wenn es andere APIs gibt, die von POI nicht bereitgestellt werden, können wir sie dann auch durch XML -Parsing -Technologie implementieren? Dies erfordert, dass wir in der Praxis erkunden. Ich glaube, diese Zeit wird uns die Antwort geben.
Okay, jetzt haben wir den Indexwert, also wie erhalten wir die Bildressourcen?
POI bietet fertige Methoden:
In der XWPFDocument -Klasse gibt es GetPicturedatabyid (String -Bild).
Die Methode kann das XWPFpiceTruedate -Objekt erhalten, das die Bildressource ist.
Für bestimmte Operationen finden Sie in den entsprechenden Blog -Posts und APIs, die hier nicht ausführlich eingeführt werden.
3. Test:
Code zum Testen mit JUNIT4:
Paket com.szdfhx.ReportStatistic.util; import org.apache.commons.collections.collectionutils; import org.apache.commons.lang.stringutils; import org.apache.poi.xwpf.usermodel.xwpfdocument; org.apache.poi.xwpf.usermodel.xwpfparagraph; import org.apache.poi.xwpf.usmodel.xwpfpicturedata; import org.junit.test; java.util.collections; import java.util.list; static org.junit.assert. document // example.docx "); XWPFDocument XWPFDocument = new XWPFDocument (in); Liste <xWPFTRAGRAPH> Absatzelist = xwpfdocument.getSparagraphs (); System.out.println ("Bildindex/t | Bildname/t | Inhalt eines Textabsatzes auf dem Bild/t"); System.out.pringln ("---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- if (collectionUtils.isnotEmpty (ImageBundLelist)) {für (String pictureId: ImageBundLelist) {xwpfPicturedata abgebildet Absatzelist.get (I-1) .GetSparagrapHText ();Ergebnisse zeigen:
Die Verwendung von Bildnamen hier bedeutet, dass ich die entsprechenden Ressourcen erhalten habe. Wenn Sie mit dem Inhalt des vorherigen Artikels vertraut sind, werden Sie feststellen, dass der Name des Bildes tatsächlich der vollständige Name aller Bilder im Ordner Word/Media ist.
In dem entsprechenden XWPFPicturedata -Objekt können die Binärdaten des Bildes über die Eigenschaft getData () erhalten werden, damit Sie sie in der Datenbank oder in Ihrem lokalen Ordner speichern können!
4. Andere:
Apropos, das zu Beginn erwähnte zweite Problem wurde hier gelöst.
Also, was soll ich mit der ersten Frage tun?
Wenn Ihr System keine hohe Geschwindigkeit erfordert, ist es mein Rat, das DOC -Dokument in das DOCX -Dokument umzuwandeln, um analysiert zu werden - POI hat eine ausgereifte API zu tun
Wenn Sie die Leistung berücksichtigen möchten, müssen Sie zwei Methodensätze schreiben, um das Dokument zu analysieren.
Also ... wie kann man die relative Position des Bildes in einem Word-Dokument vom Typ DOC erhalten?
Ich weiß nicht ... oder komm und sag es mir?
Im obigen Java -Parsingwort ist die Methode, um den Ort des Bildes im Dokument zu erhalten, der gesamte Inhalt, den ich mit Ihnen teile. Ich hoffe, Sie können Ihnen eine Referenz geben und ich hoffe, Sie können wulin.com mehr unterstützen.