參考文章:http://dh.swzhinan.com/post/185.html
引入的jar包
1 <dependency> 2 <groupId>org.docx4j</groupId> 3 <artifactId>docx4j</artifactId> 4 <version>6.0.1</version> 5 </dependency> 6 <dependency> 7 <groupId>org.apache.commons</groupId> 8 <artifactId>commons-compress</artifactId> 9 <version>1.8.1</version> 10 </dependency> 11 <dependency> 12 <groupId>com.alibaba</groupId> 13 <artifactId>fastjson</artifactId> 14 <version>1.2.58</version> 15 </dependency> 16 17 <dependency> 18 <groupId>org.apache.poi</groupId> 19 <artifactId>poi</artifactId> 20 <version>3.10-FINAL</version> 21 </dependency> 22 <dependency> 23 <groupId>org.apache.poi</groupId> 24 <artifactId>poi-ooxml</artifactId> 25 <version>3.10-FINAL</version> 26 </dependency> 27 <dependency> 28 <groupId>org.apache.xmlbeans</groupId> 29 <artifactId>xmlbeans</artifactId> 30 <version>2.5.0</version> 31 </dependency> 32 <dependency> 33 <groupId>org.apache.xmlgraphics</groupId> 34 <artifactId>xmlgraphics-commons</artifactId> 35 <version>1.3.1</version> 36 </dependency>
代碼
1 package com.htsoft.oa.action.sjrh.tool; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileOutputStream; 6 import java.io.IOException; 7 import java.io.InputStream; 8 import java.io.OutputStream; 9 import java.io.RandomAccessFile; 10 import java.nio.MappedByteBuffer; 11 import java.nio.channels.FileChannel; 12 import java.nio.channels.FileChannel.MapMode; 13 import java.text.SimpleDateFormat; 14 import java.util.ArrayList; 15 import java.util.Date; 16 import java.util.Iterator; 17 import java.util.List; 18 19 import org.apache.commons.io.IOUtils; 20 import org.docx4j.dml.wordprocessingDrawing.Inline; 21 import org.docx4j.jaxb.Context; 22 import org.docx4j.openpackaging.exceptions.Docx4JException; 23 import org.docx4j.openpackaging.packages.WordprocessingMLPackage; 24 import org.docx4j.openpackaging.parts.PartName; 25 import org.docx4j.openpackaging.parts.WordprocessingML.AlternativeFormatInputPart; 26 import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage; 27 import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart; 28 import org.docx4j.relationships.Relationship; 29 import org.docx4j.wml.Br; 30 import org.docx4j.wml.CTAltChunk; 31 import org.docx4j.wml.Drawing; 32 import org.docx4j.wml.ObjectFactory; 33 import org.docx4j.wml.P; 34 import org.docx4j.wml.R; 35 import org.docx4j.wml.STBrType; 36 37 import com.alibaba.fastjson.JSONObject; 38 import com.htsoft.oa.action.sjrh.pojo.MergeResult; 39 40 public class WordMergeUtils { 41 private static ObjectFactory factory = new ObjectFactory(); 42 43 /** 44 * 合並docx 45 * 46 * @param streams 47 * 要合並的word文件的輸入流 48 * @param path 49 * 合並后的文件的路徑 50 * @return 51 * @throws Docx4JException 52 * @throws IOException 53 */ 54 public static File mergeDocx(final List<InputStream> streams, String path) throws Docx4JException, IOException { 55 56 WordprocessingMLPackage target = null; 57 final File generated = new File(path); 58 59 int chunkId = 0; 60 Iterator<InputStream> it = streams.iterator(); 61 while (it.hasNext()) { 62 InputStream is = it.next(); 63 if (is != null) { 64 try { 65 if (target == null) { 66 // Copy first (master) document 67 OutputStream os = new FileOutputStream(generated); 68 os.write(IOUtils.toByteArray(is)); 69 os.close(); 70 71 target = WordprocessingMLPackage.load(generated); 72 } else { 73 MainDocumentPart documentPart = target.getMainDocumentPart(); 74 75 // addPageBreak(documentPart); // 另起一頁,換頁 76 77 insertDocx(documentPart, IOUtils.toByteArray(is), chunkId++); 78 } 79 } catch (Exception e) { 80 e.printStackTrace(); 81 } finally { 82 is.close(); 83 } 84 } 85 } 86 87 if (target != null) { 88 target.save(generated); 89 // Docx4J.save(target, generated, Docx4J.FLAG_NONE); 90 return generated; 91 } else { 92 return null; 93 } 94 } 95 96 // 插入文檔 97 private static void insertDocx(MainDocumentPart main, byte[] bytes, int chunkId) { 98 try { 99 AlternativeFormatInputPart afiPart = new AlternativeFormatInputPart( 100 new PartName("/part" + chunkId + ".docx")); 101 // afiPart.setContentType(new ContentType(CONTENT_TYPE)); 102 afiPart.setBinaryData(bytes); 103 Relationship altChunkRel = main.addTargetPart(afiPart); 104 105 CTAltChunk chunk = Context.getWmlObjectFactory().createCTAltChunk(); 106 chunk.setId(altChunkRel.getId()); 107 108 main.addObject(chunk); 109 } catch (Exception e) { 110 e.printStackTrace(); 111 } 112 } 113 114 /** 115 * wordML轉word,原文件不變,返回轉換完成的word文件對象。 116 * 117 * @param file 118 * @return 119 * @throws Docx4JException 120 * @throws IOException 121 */ 122 public static File wordMLToWord(File file) throws Docx4JException, IOException { 123 WordprocessingMLPackage target = WordprocessingMLPackage.load(file); 124 File temp = File.createTempFile(file.getName(), ".doc"); 125 target.save(temp); 126 return temp; 127 } 128 129 /** 130 * xml轉docx,原文件不變,返回轉換完成的word文件對象。 131 * 132 * @param file 133 * @return 134 * @throws Docx4JException 135 * @throws IOException 136 */ 137 public static File xmlToWord(File file) throws Docx4JException, IOException { 138 WordprocessingMLPackage target = WordprocessingMLPackage.load(file); 139 File temp = File.createTempFile(file.getName(), ".doc"); 140 target.save(temp); 141 return temp; 142 } 143 144 /** 145 * 合並wordML文檔 146 * 147 * @param list 148 * @param path 149 * @throws Docx4JException 150 * @throws IOException 151 */ 152 public static File mergeWordML(List<File> list, String path) throws Docx4JException, IOException { 153 final List<InputStream> streams = new ArrayList<InputStream>(); 154 for (int i = 0; i < list.size(); i++) { 155 File file = list.get(i); 156 // file = WordMLUtil.wordMLToWord(file); // wordML轉word 157 streams.add(new FileInputStream(file)); 158 } 159 return WordMergeUtils.mergeDocx(streams, path); 160 } 161 162 /** 163 * 把文件轉換成Byte[] Mapped File way MappedByteBuffer 可以在處理大文件時,提升性能 164 * 165 * @param filename 166 * @return 167 * @throws IOException 168 */ 169 public static byte[] fileToByteArray(String filename) throws IOException { 170 171 RandomAccessFile raf = null; 172 FileChannel fc = null; 173 try { 174 raf = new RandomAccessFile(filename, "r"); 175 fc = raf.getChannel(); 176 MappedByteBuffer byteBuffer = fc.map(MapMode.READ_ONLY, 0, fc.size()).load(); 177 System.out.println(byteBuffer.isLoaded()); 178 byte[] result = new byte[(int) fc.size()]; 179 if (byteBuffer.remaining() > 0) { 180 byteBuffer.get(result, 0, byteBuffer.remaining()); 181 } 182 return result; 183 } catch (IOException e) { 184 e.printStackTrace(); 185 throw e; 186 } finally { 187 try { 188 fc.close(); 189 raf.close(); 190 } catch (IOException e) { 191 e.printStackTrace(); 192 } 193 } 194 } 195 196 /** 197 * Docx4j擁有一個由字節數組創建圖片部件的工具方法, 隨后將其添加到給定的包中. 為了能將圖片添加 到一個段落中, 198 * 我們需要將圖片轉換成內聯對象. 這也有一個方法, 方法需要文件名提示, 替換文本, 兩個id標識符和一個是嵌入還是鏈接到的指示作為參數. 199 * 一個id用於文檔中繪圖對象不可見的屬性, 另一個id用於圖片本身不可見的繪制屬性. 最后我們將內聯 對象添加到段落中並將段落添加到包的主文檔部件. 200 * 201 * @param word 202 * 需要編輯的文件 203 * @param imageList 204 * 圖片對象集合( 圖片對象屬性: url 圖片文件路徑 keyword 文檔中的圖片占位符 name 圖片文件名 ) 205 * @throws Exception 206 * 不幸的createImageInline方法拋出一個異常(沒有更多具體的異常類型) 207 */ 208 public static void addImageToPackage(File word, List<JSONObject> imageList) throws Exception { 209 210 WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(word); 211 212 for (int i = 0; i < imageList.size(); i++) { 213 JSONObject image = imageList.get(i); 214 215 byte[] bytes = fileToByteArray(image.getString("url")); 216 217 BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, bytes); 218 219 int docPrId = 1; 220 int cNvPrId = 2; 221 Inline inline = imagePart.createImageInline(image.getString("name"), image.getString("keyword"), docPrId, 222 cNvPrId, false); 223 224 P paragraph = addInlineImageToParagraph(inline); 225 226 wordMLPackage.getMainDocumentPart().addObject(paragraph); 227 } 228 229 wordMLPackage.save(word); 230 } 231 232 /** 233 * Docx4j擁有一個由字節數組創建圖片部件的工具方法, 隨后將其添加到給定的包中. 為了能將圖片添加 到一個段落中, 234 * 我們需要將圖片轉換成內聯對象. 這也有一個方法, 方法需要文件名提示, 替換文本, 兩個id標識符和一個是嵌入還是鏈接到的指示作為參數. 235 * 一個id用於文檔中繪圖對象不可見的屬性, 另一個id用於圖片本身不可見的繪制屬性. 最后我們將內聯 對象添加到段落中並將段落添加到包的主文檔部件. 236 * 237 * @param wordFilePath 238 * 文件路徑 239 * @param imageList 240 * 圖片對象集合( 圖片對象屬性: url 圖片文件路徑 keyword 文檔中的圖片占位符 name 圖片文件名 ) 241 * @throws Exception 242 * 不幸的createImageInline方法拋出一個異常(沒有更多具體的異常類型) 243 */ 244 public static void addImageToPackage(String wordFilePath, List<JSONObject> imageList) throws Exception { 245 addImageToPackage(new File(wordFilePath), imageList); 246 } 247 248 /** 249 * 創建一個對象工廠並用它創建一個段落和一個可運行塊R. 然后將可運行塊添加到段落中. 接下來創建一個圖畫並將其添加到可運行塊R中. 最后我們將內聯 250 * 對象添加到圖畫中並返回段落對象. 251 * 252 * @param inline 253 * 包含圖片的內聯對象. 254 * @return 包含圖片的段落 255 */ 256 private static P addInlineImageToParagraph(Inline inline) { 257 // 添加內聯對象到一個段落中 258 P paragraph = factory.createP(); 259 R run = factory.createR(); 260 paragraph.getContent().add(run); 261 Drawing drawing = factory.createDrawing(); 262 run.getContent().add(drawing); 263 drawing.getAnchorOrInline().add(inline); 264 return paragraph; 265 } 266 267 /** 268 * 文檔結尾添加一個空白頁 269 * 270 * @throws Docx4JException 271 */ 272 public static void addPageBreak(File word) throws Docx4JException { 273 274 WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(word); 275 276 MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart(); 277 278 Br breakObj = new Br(); 279 breakObj.setType(STBrType.PAGE); 280 281 P paragraph = factory.createP(); 282 paragraph.getContent().add(breakObj); 283 documentPart.getJaxbElement().getBody().getContent().add(paragraph); 284 wordMLPackage.save(word); 285 } 286 287 /** 288 * 文檔結尾添加一個空白頁 289 * 290 * @throws Docx4JException 291 */ 292 public static void addPageBreak(MainDocumentPart documentPart) { 293 Br breakObj = new Br(); 294 breakObj.setType(STBrType.PAGE); 295 296 P paragraph = factory.createP(); 297 paragraph.getContent().add(breakObj); 298 documentPart.getJaxbElement().getBody().getContent().add(paragraph); 299 } 300 301 /** 302 * 文檔結尾添加一個空白頁 303 * 304 * @throws Docx4JException 305 */ 306 public static void addPageBreak(String wordFilePath) throws Docx4JException { 307 addPageBreak(new File(wordFilePath)); 308 } 309 310 /** 311 * 合並word文檔 接口方法 312 * 313 * @param sourceFiles待合並文件 314 * @param mergedFileName合並后的文件名稱 315 * @throws Exception 316 */ 317 public static MergeResult merge(String djxh, List<String> sourceFiles, String mergedFileName) { 318 319 if (djxh == null || djxh.isEmpty()) { 320 return new MergeResult(-1, null, "登記序號為空!", null); 321 } else if (sourceFiles == null || sourceFiles.size() <= 0) { 322 return new MergeResult(-1, null, "待合並文件路徑為空!", null); 323 } 324 325 try { 326 List<File> files = new ArrayList<File>(); 327 for (String filePath : sourceFiles) { 328 File file = new File(filePath); 329 files.add(file); 330 } 331 332 // 保存基礎路徑 333 String path = ""; 334 if ("1".equals(WordStaticFileds.open_Fixed_path)) { 335 // 創建固定路徑 336 path = WordStaticFileds.create_word_path + "word/fixed/" + djxh; 337 } else { 338 // 創建不固定路徑 339 path = WordStaticFileds.create_word_path + "word/notFixed/" 340 + new SimpleDateFormat("yyyyMMdd").format(new Date()) + "/" + djxh; 341 } 342 343 if (mergedFileName == null || mergedFileName.isEmpty()) { 344 if (files.size() > 0) { 345 String oldName = files.get(0).getName(); 346 int lastIndexOf = oldName.lastIndexOf("."); 347 if (lastIndexOf > 0) { 348 mergedFileName = oldName.substring(0, lastIndexOf) + "-合並后.docx"; 349 } 350 } 351 } 352 353 File mergedfile = new File(path); 354 355 if (!mergedfile.exists()) { 356 mergedfile.mkdirs(); 357 } 358 359 String mergedFullPath = path + "/" + mergedFileName; 360 File mergeWordML = WordMergeUtils.mergeWordML(files, mergedFullPath); 361 362 363 return new MergeResult(0, mergeWordML, "合並word文件成功!", mergeWordML.getAbsolutePath()); 364 } catch (Exception e) { 365 return new MergeResult(-1, null, "合並word文件出錯!錯誤信息:" + e.getMessage(), null); 366 } 367 368 } 369 }