1
2
首先,本實例是采用java語言的,需要進行xml操作,至於poi,docx4j這些,因為感覺不是文檔不太好,要嘛就是操作不方便,而且英文就算了,主要是注釋很少,讓人雲里霧里的。所以,嘿嘿,直接用java寫,這些操作包就不用了
首先,得明白docx解壓后的文件夾是什么,解壓后的東西如下
“----------------------------------------------------------------------------”
其中,_rels文件夾內容如下:
就一個文件,里面的內容如下:
里面標識了三個xml文件的位置,主要是指示鏈接了哪些文件。
“------------------------------------------------------------------------------”
docProps文件夾里面的東西如下:
app.xml指明了這個文檔的總體信息,比如多少行,多少空格,單詞,頁數之類的一些基礎信息。
core.xml也是如此,里面弄了創建人啊,時間啊,最后修改人之類的信息。
doc是最主要的文件夾,里面是我們的word文檔內容,挺多東西的
(1)其中這里的_rels文件夾下有個
這里面包含了你需要用到的下面的內容,比如說,下面的什么styles.xml啊等等等文件都需要在這里引入,包括需要用到的文件,比如說一個png圖片。
(2)media里面放的就是圖片,等等其它一些word支持的媒體文件。要使用這些文件,記得在document.xml.rels里引入
(3)theme文件夾下放的是word文檔的主題xml文件(包含了各種什么字體啊,顏色啊,等等一堆亂七八糟的東西)
(4)document.xml里放的就是我們的word文檔內容了,如果把docx文檔比喻成網頁,這里面放的就是html文件了。(等等主要詳細介紹)
(5)剩下的幾個xml文件就是注入腳注,樣式之類的xml文件,由於只是介紹蓋章,就不介紹了。一般docx批處理不會特意單獨去改單獨某個docx的樣式的。
最后,顧名思義,[Content Types]文件里放的是文件內容類型的信息,截圖如下,
Extension是擴展類型,如果在word文檔里用到了jpeg圖片,這里就要指明使用了image/jpeg的內容類型(其它格式模仿上面)。這里的jpeg可不是指修改了文件后綴就好了,是真正的jpeg格式,如果格式和后綴對不上的話(文件損壞),word要嘛會把格式轉換,要嘛就改后綴成格式后綴了。當然了,正常情況下,格式和后綴不會變的。
好了,現在回頭講講document.xml這個文件。
現在,弄了個word文檔,內容如下(隨便編的內容和章)
document.xml里面的內容摘要圖如下:
上面的內容,隨便幾個摘要圖隨便瀏覽一下就行了,下面我們分析一下。
這個 xml有<w:body>表示主體,w:p 表示一個段落
如:
畫線的部分都是一個段落,其中,圖片的左上角由於是和申請人在同一行上,所以算是同一段落的。
因此,這份document.xml(用谷歌瀏覽器打開xml,能合並標簽)有段落數量如下:
額,感覺還不錯,
現在,按順序點下來,圖片應該是在第四段落。也就是第四個w:p標簽里面(當然了,憑肉眼是這么看的,寫程序當然不是用看的了)。
該段落如下:
這里面有個w:drawing標簽,存放了章圖片的信息。
我們只需要,把這個dom弄出來。添加到到沒蓋章的word文檔自己需要的段落。注意,rels里一定要添加對該圖片的引用,且里面聲明的Id要與上圖的rId保持一致 ,且里面的rId是唯一的。
上圖的rId7在document.xml.rels里已經聲明引用了
還有,[Content_Types].xml也要聲明該圖片類型。
如果想要移動該圖片的位置,還是在<w:drawing>包裹的標簽里面,找到
里面的H是水平移動,也就是X軸的意思,V是垂直也就是y軸,改變這兩個數的數值就改變圖片的位置了。這里是是相對位置(其實也可以弄成絕對於頁面的位置的),也就是相對於這一一段開頭行的位置。包括上padding間隔。假如兩個都設置為0,圖片位置是變成如下:
當然,也可以設置成負數,現在將垂直值設置成-1000000,效果如下:
這樣就實現了圖片指定位置插入(圖片是襯於文字下方的,浮於上方也行,不然漂移圖片不了,不然得設置邊距,但是這樣就不妥了,蓋章需要在文字下方)
下面開始例子
首先,先寫解壓縮和壓縮的功能,畢竟word是個zip包。
package com.gmr.io;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import org.apache.commons.io.IOUtils;
import com.gmr.execption.DocxException;
import com.gmr.execption.GmrZipException;
public class DocxFile {
private ZipFile docxFile;
private String docxName;
private File file;
private Map<String, byte[]> updateEntryMap = new HashMap<String, byte[]>();
boolean tag = true;
/**
* 初始化(docx)
*
* @param docxName
* docx文件名及路徑 如 web-inf/a.docx
*
*/
public DocxFile(String docxName) {
this.docxName = docxName;
try {
file = new File(docxName);
docxFile = new ZipFile(file);
} catch (ZipException e) {
throw new GmrZipException(e);
} catch (IOException e) {
throw new DocxException(e);
}
}
/**
* 獲取docx壓縮體內容
*
* @param entryName
* 壓縮體名
*
* @return
*/
public InputStream getEntryInputStream(String entryName) {
ZipEntry entry = docxFile.getEntry(entryName);
try {
return entry == null ? null : docxFile.getInputStream(entry);
} catch (IOException e) {
throw new DocxException(e);
}
}
/**
* 獲取docx壓縮體內容
*
* @param entry
* 壓縮體
*
* @return
*/
public InputStream getEntryInputStream(ZipEntry entry) {
try {
return docxFile.getInputStream(entry);
} catch (IOException e) {
throw new DocxException(e);
}
}
/**
* 獲取指定文件夾下的ZipEntry
*
* @param directoryName
* @return
*/
public List<ZipEntry> getEntries(String directoryName) {
List<ZipEntry> list = new ArrayList<ZipEntry>();
Enumeration<? extends ZipEntry> entries = docxFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String name = entry.getName();
if (name.contains(directoryName)
|| name.contains(directoryName + "/")
|| name.contains(directoryName + "\\")) {
list.add(entry);
}
}
return list;
}
/**
* 放置修改后的Entry
*
* @param entryName
* @param bs
*/
public void putUpdateEntry(String entryName, byte[] bs) {
updateEntryMap.put(entryName, bs);
}
/**
* 修改當前的docx文件(這是文件名非空,也就是初始化時流的時候)
*
* @throws Exception
*/
public void updateZip() throws Exception {
String suffix = "" + System.currentTimeMillis() + docxFile.hashCode()
+ updateEntryMap.hashCode();
File tFile = new File(docxName + suffix);
OutputStream out;
try {
out = new FileOutputStream(tFile);
} catch (FileNotFoundException e) {
throw new DocxException(e);
}
ZipOutputStream docxOut = new ZipOutputStream(out);
Enumeration<? extends ZipEntry> zipEntrys = docxFile.entries();
try {
// 原有的部分,包括修改后的覆蓋原有的
while (zipEntrys.hasMoreElements()) {
ZipEntry zipEntry = zipEntrys.nextElement();
docxOut.putNextEntry(new ZipEntry(zipEntry.getName()));
if (updateEntryMap.containsKey(zipEntry.getName())) {
byte[] b = updateEntryMap.get(zipEntry.getName());
if (b != null && b.length > 0) {
docxOut.write(b);
}
updateEntryMap.remove(zipEntry.getName());
} else {
InputStream in = docxFile.getInputStream(zipEntry);
IOUtils.copy(in, docxOut);
}
}
// 表示新增的修改部分
for (Entry<String, byte[]> entry : updateEntryMap.entrySet()) {
docxOut.putNextEntry(new ZipEntry(entry.getKey()));
docxOut.write(entry.getValue());
}
docxOut.flush();
} finally {
docxOut.close();
tag = false;
docxFile.close();
}
this.file.delete();
tFile.renameTo(new File(docxName));
}
/**
* 關閉文件
*/
public void close() {
try {
if (tag) {
docxFile.close();
}
} catch (IOException e) {
throw new DocxException(e);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
/**
* docx圖片拷貝操作
*
* @author gmr
*
*/
public class DocxImageOperator {
private DocxFile docxTemplate;
/**
* 初始化圖片拷貝模板
*
* @param templateDocxName
*/
public DocxImageOperator(String templateDocxName) {
docxTemplate = new DocxFile(templateDocxName);
}
public static void main(String[] args) {
DocxImageOperator docxImageOperator = new DocxImageOperator(
"C:/Users/gmr/Desktop/xx/a.docx");
try {
//a.docx作為模板,將圖片拷貝入b.docx
docxImageOperator
.copyImageToDocxFromTemplate("C:/Users/gmr/Desktop/xx/b.docx");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 圖片復樣
*
* @param docxNames
*/
public void copyImageToDocxFromTemplate(String... docxNames) {
try {
for (String docxName : docxNames) {
copyImageToDocxFromTemplate(docxName);
}
} catch (Exception e) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
} else {
throw new DocxException(e);
}
}
}
/**
* 將模板里的圖片拷貝到docx中
*
* @param docxName
*/
private void copyImageToDocxFromTemplate(String docxName) throws Exception {
DocxFile docx = new DocxFile(docxName);
try {
DocumentBuilderFactory factory = DocumentBuilderFactory
.newInstance();
DocumentBuilder documentBuilder = factory.newDocumentBuilder();
TransformerFactory transformerFactory = TransformerFactory
.newInstance();
Transformer transformer = transformerFactory.newTransformer();
// 圖片數據切入(圖片media進入壓縮包)
Set<String> imageNames = new HashSet<String>();// 其實就只支持一張,當然了,可以自行改動代碼支持多張
addImage(imageNames, docx);
// 圖片引用導入(document.xml.rels添加對圖片的引用)
// 雖然寫着imageNames,但是為了偷懶,我只讓放一張,否則就亂序(因為打算直接replace,不管順序了)
String idName = documentRelsXmlCheck(transformer, imageNames,
documentBuilder, docx);
// 下面是contentType.xml的修改
// 修改[Content_Types].xml
contentTypesXmlCheck(transformer, imageNames, documentBuilder, docx);
/*
* 修改最后的內容,添加正文圖片修改document.xml
*/
documentXmlCheck(transformer, documentBuilder, docx, idName);
// 保存該word文檔
docx.updateZip();
} finally {
docx.close();
}
}
/**
* 拷貝模板的圖片正文內容到word文檔
*
* @param transformer
* @param documentBuilder
* @param docx
* @param idName
* @throws Exception
*/
public void documentXmlCheck(Transformer transformer,
DocumentBuilder documentBuilder, DocxFile docx, String idName)
throws Exception {
Document documentXml = documentBuilder.parse(docxTemplate
.getEntryInputStream("word/document.xml"));
NodeList blips = documentXml.getElementsByTagName("a:blip");
if (blips.getLength() > 0) {
Node node = blips.item(0);
node.getAttributes().getNamedItem("r:embed").setNodeValue(idName);
}
NodeList imageContents = documentXml.getElementsByTagName("w:drawing");
if (imageContents.getLength() > 0) {
Node node = null;
Node rNode = null;
Node pNode = null;
int imgLength = imageContents.getLength();
for (int i = 0; i < imgLength; i++) {
node = imageContents.item(i);
rNode = node.getParentNode();
pNode = rNode.getParentNode();
if (node.getParentNode().getNodeName().equals("w:r")
&& pNode.getNodeName().equals("w:p")) {
break;
}
}
NodeList pList = documentXml.getElementsByTagName("w:p");
int pLength = pList.getLength();
int position = -1;
for (int i = 0; i < pLength; i++) {
if (pList.item(i) == pNode) {
position = i;
}
}
Document toDocumentXml = documentBuilder.parse(docx
.getEntryInputStream("word/document.xml"));
Node n = toDocumentXml.importNode(rNode, true);
NodeList toNodeList = toDocumentXml.getElementsByTagName("w:p");
if (position != -1) {
if (position >= toNodeList.getLength()) {
position = toNodeList.getLength() - 1;
}
toDocumentXml.getElementsByTagName("w:p").item(position)
.appendChild(n);
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
transformer.transform(new DOMSource(toDocumentXml),
new StreamResult(out));
docx.putUpdateEntry("word/document.xml", out.toByteArray());
} finally {
if (out != null) {
out.close();
}
}
}
}
/**
* 檢查word文檔對圖片類型的拓展(如果沒有,則添加該拓展)
*
* @param transformer
* @param imageNames
* @param documentBuilder
* @param docx
* @throws Exception
*/
private void contentTypesXmlCheck(Transformer transformer,
Set<String> imageNames, DocumentBuilder documentBuilder,
DocxFile docx) throws Exception {
Document contentTypesXml = documentBuilder.parse(docx
.getEntryInputStream("[Content_Types].xml"));
NodeList nodeList = contentTypesXml.getElementsByTagName("Default");
int length = nodeList.getLength();
boolean tag = false;
String imgName = "";
for (String name : imageNames) {
imgName = name;
break;
}
String type = imgName.substring(imgName.lastIndexOf('.') + 1);
for (int i = 0; i < length; i++) {
Node node = nodeList.item(i);
String value = node.getAttributes().getNamedItem("Extension")
.getNodeValue();
if (value.equals(type)) {
tag = true;
break;
}
}
if (!tag) {
// 沒有該類型拓展則添加
Element defaultNode = contentTypesXml.createElement("Default");
defaultNode.setAttribute("Extension", type);
defaultNode.setAttribute("ContentType", "image/" + type);// 就不辨別其它非圖片類型了,直接加(不然很長很長的代碼)
contentTypesXml.getElementsByTagName("Types").item(0)
.appendChild(defaultNode);
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
transformer.transform(new DOMSource(contentTypesXml),
new StreamResult(out));
docx.putUpdateEntry("[Content_Types].xml", out.toByteArray());
} finally {
if (out != null) {
out.close();
}
}
}
}
/**
* 添加對圖片的引用
*
* @param transformer
* @param imageNames
* @param documentBuilder
* @param docx
* @return
* @throws Exception
*/
private String documentRelsXmlCheck(Transformer transformer,
Set<String> imageNames, DocumentBuilder documentBuilder,
DocxFile docx) throws Exception {
Document documentRelsXml = documentBuilder.parse(docx
.getEntryInputStream("word/_rels/document.xml.rels"));
String idName = "";
for (String imageName : imageNames) {
// 添加對圖片(暫時只支持圖片,其它什么亂七八糟非圖片格式也暫時不判斷過濾了)的引用
Element relTextNode = documentRelsXml.createElement("Relationship");
idName = "gmr" + imageName.hashCode();
relTextNode.setAttribute("Id", idName);
relTextNode
.setAttribute("Type",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image");
relTextNode.setAttribute("Target", imageName);
documentRelsXml.getElementsByTagName("Relationships").item(0)
.appendChild(relTextNode);
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
transformer.transform(new DOMSource(documentRelsXml),
new StreamResult(out));
docx.putUpdateEntry("word/_rels/document.xml.rels",
out.toByteArray());
} finally {
if (out != null) {
out.close();
}
}
return idName;
}
/**
* 添加圖片文件進word
*
* @param imageNames
* @param docx
* @throws Exception
*/
private void addImage(Set<String> imageNames, DocxFile docx)
throws Exception {
List<ZipEntry> list = docxTemplate.getEntries("word/media");
for (ZipEntry zipEntry : list) {
if (!zipEntry.isDirectory()) {
imageNames.add(zipEntry.getName().replace("word/", "")
.replace("word\\", ""));// 圖片名集合
docx.putUpdateEntry(zipEntry.getName(),
IOUtils.toByteArray(docxTemplate
.getEntryInputStream(zipEntry)));
}
}
}
public void close() {
docxTemplate.close();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
public class DocxFile {
private ZipFile docxFile;
private String docxName;
private File file;
private Map<String, byte[]> updateEntryMap = new HashMap<String, byte[]>();
boolean tag = true;
/**
* 初始化(docx)
*
* @param docxName
* docx文件名及路徑 如 web-inf/a.docx
*
*/
public DocxFile(String docxName) {
this.docxName = docxName;
try {
file = new File(docxName);
docxFile = new ZipFile(file);
} catch (ZipException e) {
throw new GmrZipException(e);
} catch (IOException e) {
throw new DocxException(e);
}
}
/**
* 獲取docx壓縮體內容
*
* @param entryName
* 壓縮體名
*
* @return
*/
public InputStream getEntryInputStream(String entryName) {
ZipEntry entry = docxFile.getEntry(entryName);
try {
return entry == null ? null : docxFile.getInputStream(entry);
} catch (IOException e) {
throw new DocxException(e);
}
}
/**
* 獲取docx壓縮體內容
*
* @param entry
* 壓縮體
*
* @return
*/
public InputStream getEntryInputStream(ZipEntry entry) {
try {
return docxFile.getInputStream(entry);
} catch (IOException e) {
throw new DocxException(e);
}
}
/**
* 獲取指定文件夾下的ZipEntry
*
* @param directoryName
* @return
*/
public List<ZipEntry> getEntries(String directoryName) {
List<ZipEntry> list = new ArrayList<ZipEntry>();
Enumeration<? extends ZipEntry> entries = docxFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String name = entry.getName();
if (name.contains(directoryName)
|| name.contains(directoryName + "/")
|| name.contains(directoryName + "\\")) {
list.add(entry);
}
}
return list;
}
/**
* 放置修改后的Entry
*
* @param entryName
* @param bs
*/
public void putUpdateEntry(String entryName, byte[] bs) {
updateEntryMap.put(entryName, bs);
}
/**
* 修改當前的docx文件(這是文件名非空,也就是初始化時流的時候)
*
* @throws Exception
*/
public void updateZip() throws Exception {
String suffix = "" + System.currentTimeMillis() + docxFile.hashCode()
+ updateEntryMap.hashCode();
File tFile = new File(docxName + suffix);
OutputStream out;
try {
out = new FileOutputStream(tFile);
} catch (FileNotFoundException e) {
throw new DocxException(e);
}
ZipOutputStream docxOut = new ZipOutputStream(out);
Enumeration<? extends ZipEntry> zipEntrys = docxFile.entries();
try {
// 原有的部分,包括修改后的覆蓋原有的
while (zipEntrys.hasMoreElements()) {
ZipEntry zipEntry = zipEntrys.nextElement();
docxOut.putNextEntry(new ZipEntry(zipEntry.getName()));
if (updateEntryMap.containsKey(zipEntry.getName())) {
byte[] b = updateEntryMap.get(zipEntry.getName());
if (b != null && b.length > 0) {
docxOut.write(b);
}
updateEntryMap.remove(zipEntry.getName());
} else {
InputStream in = docxFile.getInputStream(zipEntry);
IOUtils.copy(in, docxOut);
}
}
// 表示新增的修改部分
for (Entry<String, byte[]> entry : updateEntryMap.entrySet()) {
docxOut.putNextEntry(new ZipEntry(entry.getKey()));
docxOut.write(entry.getValue());
}
docxOut.flush();
} finally {
docxOut.close();
tag = false;
docxFile.close();
}
this.file.delete();
tFile.renameTo(new File(docxName));
}
/**
* 關閉文件
*/
public void close() {
try {
if (tag) {
docxFile.close();
}
} catch (IOException e) {
throw new DocxException(e);
}
}
}
————————————————
版權聲明:本文為CSDN博主「我叫睿」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qwe125698420/article/details/70622289