Springboot如何根據(jù)docx填充生成word文件并導(dǎo)出pdf
在項(xiàng)目中碰見(jiàn)一個(gè)需求,需要將.doc
的合同,轉(zhuǎn)換為pdf
實(shí)現(xiàn)打印與預(yù)覽功能。
將docx模板填充數(shù)據(jù)生成doc文件
1、依賴引入
填充docx模板,只需要引入一個(gè)pom依賴即可實(shí)現(xiàn)。
<dependency> <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifactId> <version>1.5.0</version> </dependency>
2、doc文件轉(zhuǎn)換docx,并標(biāo)注別名
用office或者wps,創(chuàng)建一個(gè)001.doc
文件,繪制表格,保存。
更改后綴為.docx
,確定后,在指定的位置,表示數(shù)據(jù)接受變量名稱。
如下圖所示:
3、編寫(xiě)java代碼實(shí)現(xiàn)數(shù)據(jù)填充
import com.deepoove.poi.XWPFTemplate; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.HashMap; import java.util.Map; /** * word 填充測(cè)試 */ public class TestWord { public static void main(String[] args) throws IOException { Map<String, Object> params = new HashMap<>(); params.put("username","xiangjiao1"); params.put("password","******"); params.put("age",22); params.put("email","專注寫(xiě)bug測(cè)試中文"); Resource resource = new ClassPathResource("templates_report/001.docx"); File file = resource.getFile(); // 數(shù)據(jù)填充 XWPFTemplate template = XWPFTemplate.compile(file).render(params); String docOutPath = System.getProperty("user.dir")+File.separator+"springboot-poi"+File.separator+"pdf"+File.separator+ "1.doc"; OutputStream outputStream = new FileOutputStream(docOutPath); template.write(outputStream); } }
運(yùn)行程序,查看結(jié)果。
測(cè)試項(xiàng)目結(jié)構(gòu)如下:
docx文件填充數(shù)據(jù)導(dǎo)出pdf(web)
1、依賴引入
向docx
模板中填充數(shù)據(jù),并導(dǎo)出pdf
類型的文件,除了上面的pom依賴之外,還需要引入其他的依賴信息,完整依賴如下所示:
<!-- docx 數(shù)據(jù)填充生成 doc文件 這個(gè)是主要 --> <dependency> <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifactId> <version>1.5.0</version> </dependency> <!-- doc 轉(zhuǎn) pdf --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.13</version> </dependency> <!-- docx4j docx2pdf --> <dependency> <groupId>org.docx4j</groupId> <artifactId>docx4j</artifactId> <version>6.1.2</version> </dependency> <dependency> <groupId>org.docx4j</groupId> <artifactId>docx4j-export-fo</artifactId> <version>6.0.0</version> </dependency>
2、字體文件
在src\main\resources
下創(chuàng)建一個(gè)font
文件夾,其中放入simsun.ttc
字體文件。
3、編寫(xiě)工具類
思想很簡(jiǎn)單
- 1、先使用上面的docx模板填充數(shù)據(jù)生成
臨時(shí)doc
文件, - 2、再將doc文件轉(zhuǎn)換為pdf文件
- 3、刪除臨時(shí)文件
【注意:】
為了避免出現(xiàn)多人同時(shí)操作,導(dǎo)致文件誤刪的問(wèn)題,需要盡可能地保證臨時(shí)文件名稱的唯一性。
import com.deepoove.poi.XWPFTemplate; import com.itextpdf.text.*; import com.itextpdf.text.Image; import com.itextpdf.text.pdf.*; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.text.WordUtils; import org.docx4j.Docx4J; import org.docx4j.convert.out.FOSettings; import org.docx4j.fonts.IdentityPlusMapper; import org.docx4j.fonts.Mapper; import org.docx4j.fonts.PhysicalFonts; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.springframework.stereotype.Component; import java.io.*; import java.util.Map; import java.util.UUID; import java.util.zip.ZipOutputStream; /** * pdf 導(dǎo)出工具類 */ @Component @Slf4j public final class FreeMarkUtils { /** * 根據(jù)docx模板填充數(shù)據(jù) 并生成pdf文件 * * @param dataMap 數(shù)據(jù)源 * @param docxFile docx模板的文件名 * @return 生成的文件路徑 */ public static byte[] createDocx2Pdf(Map<String, Object> dataMap, String docxFile) { //輸出word文件路徑和名稱 (臨時(shí)文件名,本次為測(cè)試,最好使用雪花算法生成,或者用uuid) String fileName = UUID.randomUUID().toString() + ".docx"; // word 數(shù)據(jù)填充 // 生成docx臨時(shí)文件 final File tempPath = new File(fileName); final File docxTempFile = getTempFile(docxFile); XWPFTemplate template = XWPFTemplate.compile(docxTempFile).render(dataMap); try { template.write(new FileOutputStream(tempPath)); } catch (IOException e) { e.printStackTrace(); } // word轉(zhuǎn)pdf final String pdfFile = convertDocx2Pdf(fileName); return getFileOutputStream(new File(pdfFile)).toByteArray(); } /** * word(doc)轉(zhuǎn)pdf * * @param wordPath doc 生成的臨時(shí)文件路徑 * @return 生成的帶水印的pdf路徑 */ public static String convertDocx2Pdf(String wordPath) { OutputStream os = null; InputStream is = null; //輸出pdf文件路徑和名稱 (臨時(shí)文件 盡可能保證文件名稱的唯一性) final String fileName = UUID.randomUUID().toString() + ".pdf"; try { is = new FileInputStream(wordPath); WordprocessingMLPackage mlPackage = WordprocessingMLPackage.load(is); Mapper fontMapper = new IdentityPlusMapper(); fontMapper.put("隸書(shū)", PhysicalFonts.get("LiSu")); fontMapper.put("宋體", PhysicalFonts.get("SimSun")); fontMapper.put("微軟雅黑", PhysicalFonts.get("Microsoft Yahei")); fontMapper.put("黑體", PhysicalFonts.get("SimHei")); fontMapper.put("楷體", PhysicalFonts.get("KaiTi")); fontMapper.put("新宋體", PhysicalFonts.get("NSimSun")); fontMapper.put("華文行楷", PhysicalFonts.get("STXingkai")); fontMapper.put("華文仿宋", PhysicalFonts.get("STFangsong")); fontMapper.put("宋體擴(kuò)展", PhysicalFonts.get("simsun-extB")); fontMapper.put("仿宋", PhysicalFonts.get("FangSong")); fontMapper.put("仿宋_GB2312", PhysicalFonts.get("FangSong_GB2312")); fontMapper.put("幼圓", PhysicalFonts.get("YouYuan")); fontMapper.put("華文宋體", PhysicalFonts.get("STSong")); fontMapper.put("華文中宋", PhysicalFonts.get("STZhongsong")); //解決宋體(正文)和宋體(標(biāo)題)的亂碼問(wèn)題 PhysicalFonts.put("PMingLiU", PhysicalFonts.get("SimSun")); PhysicalFonts.put("新細(xì)明體", PhysicalFonts.get("SimSun")); // 字體文件 PhysicalFonts.addPhysicalFonts("SimSun", WordUtils.class.getResource("/font/simsun.ttc")); mlPackage.setFontMapper(fontMapper); os = new FileOutputStream(fileName); //docx4j docx轉(zhuǎn)pdf FOSettings foSettings = Docx4J.createFOSettings(); foSettings.setWmlPackage(mlPackage); Docx4J.toFO(foSettings, os, Docx4J.FLAG_EXPORT_PREFER_XSL); is.close();//關(guān)閉輸入流 os.close();//關(guān)閉輸出流 } catch (Exception e) { e.printStackTrace(); } finally { // 刪除docx 臨時(shí)文件 File file = new File(wordPath); if (file != null && file.isFile() && file.exists()) { file.delete(); } try { if (is != null) { is.close(); } if (os != null) { os.close(); } } catch (Exception ex) { ex.printStackTrace(); } } return fileName; } /** * 文件轉(zhuǎn)字節(jié)輸出流 * * @param outFile 文件 * @return */ public static ByteArrayOutputStream getFileOutputStream(File outFile) { // 獲取生成臨時(shí)文件的輸出流 InputStream input = null; ByteArrayOutputStream bytestream = null; try { input = new FileInputStream(outFile); bytestream = new ByteArrayOutputStream(); int ch; while ((ch = input.read()) != -1) { bytestream.write(ch); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { bytestream.close(); input.close(); log.info("刪除臨時(shí)文件"); if (outFile.exists()) { outFile.delete(); } } catch (IOException e) { e.printStackTrace(); } } return bytestream; } /** * 獲取資源文件的臨時(shí)文件 * 資源文件打jar包后,不能直接獲取,需要通過(guò)流獲取生成臨時(shí)文件 * * @param fileName 文件路徑 templates/xxx.docx * @return */ public static File getTempFile(String fileName) { final File tempFile = new File(fileName); InputStream fontTempStream = null; try { fontTempStream = FreeMarkUtils.class.getClassLoader().getResourceAsStream(fileName); FileUtils.copyInputStreamToFile(fontTempStream, tempFile); } catch (Exception e) { e.printStackTrace(); } finally { try { if (fontTempStream != null) { fontTempStream.close(); } } catch (IOException e) { e.printStackTrace(); } } return tempFile; } /** * 插入圖片水印 * @param srcByte 已生成PDF的字節(jié)數(shù)組(流轉(zhuǎn)字節(jié)) * @param destFile 生成有水印的臨時(shí)文件 temp.pdf * @return */ public static FileOutputStream addWaterMark(byte[] srcByte, String destFile) { // 待加水印的文件 PdfReader reader = null; // 加完水印的文件 PdfStamper stamper = null; FileOutputStream fileOutputStream = null; try { reader = new PdfReader(srcByte); fileOutputStream = new FileOutputStream(destFile); stamper = new PdfStamper(reader, fileOutputStream); int total = reader.getNumberOfPages() + 1; PdfContentByte content; // 設(shè)置字體 //BaseFont font = BaseFont.createFont(); // 循環(huán)對(duì)每頁(yè)插入水印 for (int i = 1; i < total; i++) { final PdfGState gs = new PdfGState(); // 水印的起始 content = stamper.getUnderContent(i); // 開(kāi)始 content.beginText(); // 設(shè)置顏色 默認(rèn)為藍(lán)色 //content.setColorFill(BaseColor.BLUE); // content.setColorFill(Color.GRAY); // 設(shè)置字體及字號(hào) //content.setFontAndSize(font, 38); // 設(shè)置起始位置 // content.setTextMatrix(400, 880); //content.setTextMatrix(textWidth, textHeight); // 開(kāi)始寫(xiě)入水印 //content.showTextAligned(Element.ALIGN_LEFT, text, textWidth, textHeight, 45); // 設(shè)置水印透明度 // 設(shè)置筆觸字體不透明度為0.4f gs.setStrokeOpacity(0f); Image image = null; image = Image.getInstance("url"); // 設(shè)置坐標(biāo) 絕對(duì)位置 X Y 這個(gè)位置大約在 A4紙 右上角展示LOGO image.setAbsolutePosition(472, 785); // 設(shè)置旋轉(zhuǎn)弧度 image.setRotation(0);// 旋轉(zhuǎn) 弧度 // 設(shè)置旋轉(zhuǎn)角度 image.setRotationDegrees(0);// 旋轉(zhuǎn) 角度 // 設(shè)置等比縮放 圖片大小 image.scalePercent(4);// 依照比例縮放 // image.scaleAbsolute(200,100);//自定義大小 // 設(shè)置透明度 content.setGState(gs); // 添加水印圖片 content.addImage(image); // 設(shè)置透明度 content.setGState(gs); //結(jié)束設(shè)置 content.endText(); content.stroke(); } } catch (IOException e) { e.printStackTrace(); } catch (DocumentException e) { e.printStackTrace(); } finally { try { stamper.close(); fileOutputStream.close(); reader.close(); } catch (DocumentException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } return fileOutputStream; } }
4、編寫(xiě)測(cè)試接口
import cn.xj.util.FreeMarkUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.UUID; @RestController @RequestMapping("/report") public class ReportController { @GetMapping("/doc2pdf") public void doc2pdf(HttpServletResponse response) { Map<String, Object> params = new HashMap<>(); params.put("username","xiangjiao1"); params.put("password","******"); params.put("age",22); params.put("email","專注寫(xiě)bug測(cè)試中文"); final byte[] data = FreeMarkUtils.createDocx2Pdf(params, "templates_report/001.docx"); String fileName = UUID.randomUUID().toString() + "_001_test.pdf"; generateFile(response, data, fileName); } /** * 下載文件 * @param response 相應(yīng) * @param data 數(shù)據(jù) * @param fileName 文件名 */ private void generateFile(HttpServletResponse response, byte[] data, String fileName) { response.setHeader("content-Type", "application/octet-stream"); response.setCharacterEncoding("utf-8"); response.setHeader("Content-disposition", "attachment;filename=" + fileName); try { response.getOutputStream().write(data); } catch (IOException e) { e.printStackTrace(); } finally { try { response.getOutputStream().close(); } catch (IOException e) { e.printStackTrace(); } } } }
請(qǐng)求測(cè)試
http://localhost/report/doc2pdf
docx4j 復(fù)雜docx文件轉(zhuǎn)pdf碰見(jiàn)的坑總結(jié)
轉(zhuǎn)pdf出現(xiàn)空格壓縮、中文縮減等問(wèn)題,可以考慮將半角替換成全角,將模板中的空格使用全角空格替換。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- SpringBoot使用iText7實(shí)現(xiàn)將HTML轉(zhuǎn)成PDF并添加頁(yè)眉頁(yè)腳水印
- SpringBoot集成PDFBox實(shí)現(xiàn)電子簽章的代碼詳解
- SpringBoot集成SpirePDF實(shí)現(xiàn)文本替換功能
- SpringBoot使用itext填充pdf表單及導(dǎo)出pdf的流程
- SpringBoot實(shí)現(xiàn)Word轉(zhuǎn)PDF和TXT的實(shí)踐分享
- SpringBoot實(shí)現(xiàn)PDF轉(zhuǎn)圖片的代碼示例
- SpringBoot整合iText7導(dǎo)出PDF及性能優(yōu)化方式
相關(guān)文章
本地MinIO存儲(chǔ)服務(wù)Java遠(yuǎn)程調(diào)用上傳文件的操作過(guò)程
MinIO是一款高性能、分布式的對(duì)象存儲(chǔ)系統(tǒng),它可以100%的運(yùn)行在標(biāo)準(zhǔn)硬件上,即X86等低成本機(jī)器也能夠很好的運(yùn)行MinIO,這篇文章主要介紹了本地MinIO存儲(chǔ)服務(wù)Java遠(yuǎn)程調(diào)用上傳文件的操作過(guò)程,需要的朋友可以參考下2023-11-11mybatis注解開(kāi)發(fā) 一對(duì)多嵌套查詢方式
這篇文章主要介紹了mybatis注解開(kāi)發(fā) 一對(duì)多嵌套查詢方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。2023-03-03FeignClientFactoryBean創(chuàng)建動(dòng)態(tài)代理詳細(xì)解讀
這篇文章主要介紹了FeignClientFactoryBean創(chuàng)建動(dòng)態(tài)代理詳細(xì)解讀,當(dāng)直接進(jìn)去注冊(cè)的方法中,一步步放下走,都是直接放bean的定義信息中放入值,然后轉(zhuǎn)成BeanDefinitionHolder,最后在注冊(cè)到IOC容器中,需要的朋友可以參考下2023-11-11Java時(shí)間轉(zhuǎn)換成unix時(shí)間戳的方法
這篇文章主要為大家詳細(xì)介紹了Java時(shí)間轉(zhuǎn)換成unix時(shí)間戳的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12WebSocket實(shí)現(xiàn)系統(tǒng)后臺(tái)消息實(shí)時(shí)通知功能
在現(xiàn)代Web應(yīng)用中,提供實(shí)時(shí)通知對(duì)于改善用戶體驗(yàn)至關(guān)重要,WebSocket技術(shù)允許建立雙向通信通道,從系統(tǒng)后臺(tái)將消息實(shí)時(shí)傳送給系統(tǒng)用戶,下面我們就來(lái)深入探討一下如何使用WebSocket來(lái)實(shí)現(xiàn)這一功能吧2023-10-10Spring Boot + thymeleaf 實(shí)現(xiàn)文件上傳下載功能
最近同事問(wèn)我有沒(méi)有有關(guān)于技術(shù)的電子書(shū),我打開(kāi)電腦上的小書(shū)庫(kù),但是郵件發(fā)給他太大了,公司又禁止用文件夾共享,于是花半天時(shí)間寫(xiě)了個(gè)小的文件上傳程序,部署在自己的Linux機(jī)器上,需要的朋友可以參考下2018-01-01JavaWeb?Servlet實(shí)現(xiàn)文件上傳與下載功能實(shí)例
因自己負(fù)責(zé)的項(xiàng)目中需要實(shí)現(xiàn)文件上傳,所以下面下面這篇文章主要給大家介紹了關(guān)于JavaWeb?Servlet實(shí)現(xiàn)文件上傳與下載功能的相關(guān)資料,需要的朋友可以參考下2022-04-04