最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot如何實現(xiàn)word文檔轉(zhuǎn)pdf

 更新時間:2022年07月04日 14:31:09   作者:雨夜歸人93  
這篇文章主要介紹了SpringBoot如何實現(xiàn)word文檔轉(zhuǎn)pdf,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

一.背景

項目中有個需求大體意思是,上傳一個word模板,根據(jù)word模板合成word文件,再將word文件轉(zhuǎn)為pdf。

二.方案選擇

1.Spire.Doc for Java方案

Spire.Doc for Java這個是商用收費的,不過API文檔豐富且集成簡單,免費版僅支持3頁轉(zhuǎn)換。類似的還有ITEXT,這個商用也是受限制的。

2.docx4j方案

開源可商用,僅支持docx格式的word。

3.jodconverter+LibreOffice 方案

開源可商用,調(diào)用本地office服務(wù),進(jìn)行pdf轉(zhuǎn)換,類似的還有jodconverter+openOffice。

4.其他

至于其他的由于不支持跨平臺不做考慮。

三.實操

1.docx4j

首先嘗試了docx4j,因為docx4j本身支持模板替換的操作,可一次性做替換及文檔類型轉(zhuǎn)換,而且僅支持docx類型,對于本次需求問題不大。

1.依賴僅需要一個即可

<dependency>
?? ?<groupId>org.docx4j</groupId>
?? ?<artifactId>docx4j-export-fo</artifactId>
?? ?<version>6.1.0</version>
</dependency>

2.主要代碼

@Slf4j
public class PdfUtil {
    public static <T> void exportByLocalPath(HttpServletResponse response, String fileName, String path, Map<String,String> params){
        try (InputStream in = PdfUtil.class.getClassLoader().getResourceAsStream(path)) {
            convertDocxToPdf(in, response,fileName,params);
        } catch (Exception e) {
            log.error("docx文檔轉(zhuǎn)換為PDF失敗", e.getMessage());
        }
    }
    /**
     * docx文檔轉(zhuǎn)換為PDF
     * @param in
     * @param response
     * @return
     */
    public static void convertDocxToPdf(InputStream in, HttpServletResponse response, String fileName, Map<String,String> params) throws Exception {
        response.setContentType("application/pdf");
         String fullFileName = new String(fileName.getBytes(), StandardCharsets.ISO_8859_1);
        response.setHeader("Content-disposition", "attachment;filename=" + fullFileName + ".pdf");
        WordprocessingMLPackage wmlPackage = WordprocessingMLPackage.load(in);
        if (params!=null&&!params.isEmpty()) {
            MainDocumentPart documentPart = wmlPackage.getMainDocumentPart();
            cleanDocumentPart(documentPart);
            documentPart.variableReplace(params);
        }
        setFontMapper(wmlPackage);
        Docx4J.toPDF(wmlPackage,response.getOutputStream());
    }
    /**
     * 清除文檔空白占位符
     * @param documentPart
     * @return {@link boolean}
     */
    public static boolean cleanDocumentPart(MainDocumentPart documentPart) throws Exception {
        if (documentPart == null) {
            return false;
        }
        Document document = documentPart.getContents();
        String wmlTemplate =
                XmlUtils.marshaltoString(document, true, false, Context.jc);
        document = (Document) XmlUtils.unwrap(DocxVariableClearUtil.doCleanDocumentPart(wmlTemplate, Context.jc));
        documentPart.setContents(document);
        return true;
    }
    /**
     * 設(shè)置字體樣式
     * @param mlPackage
     */
    private static void setFontMapper(WordprocessingMLPackage mlPackage) throws Exception {
        Mapper fontMapper = new IdentityPlusMapper();
        fontMapper.put("隸書", 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"));
        mlPackage.setFontMapper(fontMapper);
    }
}

清除工具類,用于處理占位符替換不生效的問題,這里參考文章

public class DocxVariableClearUtil {
    /**
     * 去任意XML標(biāo)簽
     */
    private static final Pattern XML_PATTERN = Pattern.compile("<[^>]*>");
    private DocxVariableClearUtil() {
    }
    /**
     * start符號
     */
    private static final char PREFIX = '$';
    /**
     * 中包含
     */
    private static final char LEFT_BRACE = '{';
    /**
     * 結(jié)尾
     */
    private static final char RIGHT_BRACE = '}';
    /**
     * 未開始
     */
    private static final int NONE_START = -1;
    /**
     * 未開始
     */
    private static final int NONE_START_INDEX = -1;
    /**
     * 開始
     */
    private static final int PREFIX_STATUS = 1;
    /**
     * 左括號
     */
    private static final int LEFT_BRACE_STATUS = 2;
    /**
     * 右括號
     */
    private static final int RIGHT_BRACE_STATUS = 3;
    /**
     * doCleanDocumentPart
     *
     * @param wmlTemplate
     * @param jc
     * @return
     * @throws JAXBException
     */
    public static Object doCleanDocumentPart(String wmlTemplate, JAXBContext jc) throws JAXBException {
        // 進(jìn)入變量塊位置
        int curStatus = NONE_START;
        // 開始位置
        int keyStartIndex = NONE_START_INDEX;
        // 當(dāng)前位置
        int curIndex = 0;
        char[] textCharacters = wmlTemplate.toCharArray();
        StringBuilder documentBuilder = new StringBuilder(textCharacters.length);
        documentBuilder.append(textCharacters);
        // 新文檔
        StringBuilder newDocumentBuilder = new StringBuilder(textCharacters.length);
        // 最后一次寫位置
        int lastWriteIndex = 0;
        for (char c : textCharacters) {
            switch (c) {
                case PREFIX:
                    // 不管其何狀態(tài)直接修改指針,這也意味著變量名稱里面不能有PREFIX
                    keyStartIndex = curIndex;
                    curStatus = PREFIX_STATUS;
                    break;
                case LEFT_BRACE:
                    if (curStatus == PREFIX_STATUS) {
                        curStatus = LEFT_BRACE_STATUS;
                    }
                    break;
                case RIGHT_BRACE:
                    if (curStatus == LEFT_BRACE_STATUS) {
                        // 接上之前的字符
                        newDocumentBuilder.append(documentBuilder.substring(lastWriteIndex, keyStartIndex));
                        // 結(jié)束位置
                        int keyEndIndex = curIndex + 1;
                        // 替換
                        String rawKey = documentBuilder.substring(keyStartIndex, keyEndIndex);
                        // 干掉多余標(biāo)簽
                        String mappingKey = XML_PATTERN.matcher(rawKey).replaceAll("");
                        if (!mappingKey.equals(rawKey)) {
                            char[] rawKeyChars = rawKey.toCharArray();
                            // 保留原格式
                            StringBuilder rawStringBuilder = new StringBuilder(rawKey.length());
                            // 去掉變量引用字符
                            for (char rawChar : rawKeyChars) {
                                if (rawChar == PREFIX || rawChar == LEFT_BRACE || rawChar == RIGHT_BRACE) {
                                    continue;
                                }
                                rawStringBuilder.append(rawChar);
                            }
                            // 要求變量連在一起
                            String variable = mappingKey.substring(2, mappingKey.length() - 1);
                            int variableStart = rawStringBuilder.indexOf(variable);
                            if (variableStart > 0) {
                                rawStringBuilder = rawStringBuilder.replace(variableStart, variableStart + variable.length(), mappingKey);
                            }
                            newDocumentBuilder.append(rawStringBuilder.toString());
                        } else {
                            newDocumentBuilder.append(mappingKey);
                        }
                        lastWriteIndex = keyEndIndex;
                        curStatus = NONE_START;
                        keyStartIndex = NONE_START_INDEX;
                    }
                default:
                    break;
            }
            curIndex++;
        }
        // 余部
        if (lastWriteIndex < documentBuilder.length()) {
            newDocumentBuilder.append(documentBuilder.substring(lastWriteIndex));
        }
        return XmlUtils.unmarshalString(newDocumentBuilder.toString(), jc);
    }
}

2.poi-tl+jodconverter+LibreOffice 方案

poi-tl這個是專門用來進(jìn)行word模板合成的開源庫,文檔很詳細(xì)。

LibreOffice 下載最新的穩(wěn)定版本即可。

1.maven依賴

		<!-- word合成 -->
		<!-- 這里注意版本,1.5版本依賴的poi 3.x的版本 -->
		<dependency>
			<groupId>com.deepoove</groupId>
			<artifactId>poi-tl</artifactId>
			<version>1.5.1</version>
		</dependency>
		<!-- jodconverter  word轉(zhuǎn)pdf -->
		<!-- jodconverter-core這個依賴,理論上不用加的,jodconverter-local已經(jīng)依賴了,但測試的時候不添加依賴找不到 -->
		<dependency>
			<groupId>org.jodconverter</groupId>
			<artifactId>jodconverter-core</artifactId>
			<version>4.2.0</version>
		</dependency>
		<dependency>
			<groupId>org.jodconverter</groupId>
			<artifactId>jodconverter-local</artifactId>
			<version>4.2.0</version>
		</dependency>
		<dependency>
			<groupId>org.jodconverter</groupId>
			<artifactId>jodconverter-spring-boot-starter</artifactId>
			<version>4.2.0</version>
		</dependency>
		<!--  工具類,非必須 -->
		<dependency>
			<groupId>cn.hutool</groupId>
			<artifactId>hutool-all</artifactId>
			<version>5.4.3</version>
		</dependency>

2.主要代碼

JodConverterConfig配置類

@Configuration
public class JodConverterConfig {
    @Autowired
    private OfficeManager officeManager;
    @Bean
    public DocumentConverter documentConverter() {
        return LocalConverter.builder()
                .officeManager(officeManager)
                .build();
    }
}

yml配置文件

jodconverter:
  local:
    enabled: true
    office-home: "C:\\Program Files\\LibreOffice"

PdfService合成導(dǎo)出代碼

@Slf4j
@Component
public class PdfService {
    @Autowired
    private DocumentConverter documentConverter;
    public  void docxToPDF(InputStream inputStream,HttpServletResponse response,String fileName) {
        response.setContentType("application/pdf");
        try {
            String fullFileName = new String(fileName.getBytes(), StandardCharsets.ISO_8859_1);
            response.setHeader("Content-disposition","attachment;filename=\\"+fullFileName+".pdf\\");
            documentConverter
                    .convert(inputStream)
                    .as(DefaultDocumentFormatRegistry.DOCX)
                    .to(response.getOutputStream())
                    .as(DefaultDocumentFormatRegistry.PDF)
                    .execute();
        } catch (OfficeException |IOException e) {
           log.error("word轉(zhuǎn)pdf失敗:{}",e.getMessage());
        }
    }
    public void exportByLocalPath(HttpServletResponse response, String fileName, String path, Object params) throws Exception {
        BufferedOutputStream outputStream = null;
        BufferedInputStream wordInputStream = null;
        try (InputStream in = PdfService.class.getClassLoader().getResourceAsStream(path)) {
            // 生成臨時文件
            String outPutWordPath = System.getProperty("java.io.tmpdir").replaceAll(File.separator + "$", "") + fileName+".docx";
            File tempFile = FileUtil.touch(outPutWordPath);
            outputStream = FileUtil.getOutputStream(tempFile);
            // word模板合成寫到臨時文件
            WordUtil.replaceWord(outputStream, in, params);
            // word 轉(zhuǎn)pdf
            wordInputStream = FileUtil.getInputStream(tempFile);
            docxToPDF(wordInputStream, response,fileName);
            // 移除臨時文件
            FileUtil.del(tempFile);
        } catch (Exception e) {
            log.error("docx文檔轉(zhuǎn)換為PDF失敗", e.getMessage());
        } finally {
            IoUtil.close(outputStream);
            IoUtil.close(wordInputStream);
        }
    }

四.結(jié)論

1.docx4j方案

  • 依賴少
  • 同時支持word合成及格式轉(zhuǎn)換
  • 轉(zhuǎn)化效率較差
  • 對于含樣式及圖片轉(zhuǎn)換不友好,容易排版混亂

2.jodconverter+LibreOffice 方案

  • 操作穩(wěn)定
  • 轉(zhuǎn)換效率快
  • 集成依賴設(shè)置較多
  • 依賴本地服務(wù)
  • LibreOffice打開word可能排版樣式錯亂
  • 最后考慮項目需求,最終選擇了jodconverter+LibreOffice方案。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。 

相關(guān)文章

  • java向上轉(zhuǎn)型與向下轉(zhuǎn)型詳解

    java向上轉(zhuǎn)型與向下轉(zhuǎn)型詳解

    這篇文章主要為大家詳細(xì)介紹了java向上轉(zhuǎn)型與向下轉(zhuǎn)型,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-09-09
  • Jdbc的步驟以及簡單實現(xiàn)代碼

    Jdbc的步驟以及簡單實現(xiàn)代碼

    下面小編就為大家?guī)硪黄狫dbc的步驟以及簡單實現(xiàn)代碼。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-07-07
  • 詳解Idea中HTTP Client請求測試工具的使用

    詳解Idea中HTTP Client請求測試工具的使用

    今天抽空給大家分享Idea中HTTP Client請求測試工具的使用,小編在這建議使用HTTP Client的Idea版本最好在2018以上,不然的話體驗不是多好,今天就給大家介紹Idea中HTTP Client怎么使用的,感興趣的朋友跟隨小編一起看看吧
    2021-05-05
  • Maven添加reactor依賴失敗的解決方案

    Maven添加reactor依賴失敗的解決方案

    起初是自己在學(xué)spring boot3,結(jié)果到了reactor這一部分的時候,在項目的pom.xml文件中添加下列依賴報錯,接下來通過本文給大家介紹Maven添加reactor依賴失敗的解決方案,需要的朋友可以參考下
    2024-06-06
  • Java使用組件編寫窗口實現(xiàn)網(wǎng)絡(luò)圖片顯示

    Java使用組件編寫窗口實現(xiàn)網(wǎng)絡(luò)圖片顯示

    這篇文章主要為大家詳細(xì)介紹了Java使用組件編寫窗口實現(xiàn)網(wǎng)絡(luò)圖片顯示的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-02-02
  • Java使用RedisTemplate如何根據(jù)前綴獲取key列表

    Java使用RedisTemplate如何根據(jù)前綴獲取key列表

    這篇文章主要介紹了Java使用RedisTemplate如何根據(jù)前綴獲取key列表,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • Mybatis入門教程(四)之mybatis動態(tài)sql

    Mybatis入門教程(四)之mybatis動態(tài)sql

    這篇文章主要介紹了Mybatis入門教程(四)之mybatis動態(tài)sql的相關(guān)資料,涉及到動態(tài)sql及動態(tài)sql的作用知識,本文介紹的非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-09-09
  • Java Mybatis一級緩存和二級緩存

    Java Mybatis一級緩存和二級緩存

    緩存是內(nèi)存當(dāng)中一塊存儲數(shù)據(jù)的區(qū)域,目的是提高查詢效率,降低服務(wù)器和數(shù)據(jù)庫的壓力,這篇文章主要介紹了Mybatis一級緩存和二級緩存,感興趣的同學(xué)可以參考閱讀本文
    2023-04-04
  • Java多線程實現(xiàn)阻塞隊列的示例代碼

    Java多線程實現(xiàn)阻塞隊列的示例代碼

    本文主要介紹了Java多線程實現(xiàn)阻塞隊列的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-12-12
  • Java實現(xiàn)兩人五子棋游戲(七) 屏幕提示信息

    Java實現(xiàn)兩人五子棋游戲(七) 屏幕提示信息

    這篇文章主要為大家詳細(xì)介紹了Java實現(xiàn)兩人五子棋游戲,屏幕提示游戲信息,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-03-03

最新評論

儋州市| 霍城县| 文化| 台中市| 馆陶县| 冕宁县| 天津市| 项城市| 西乡县| 曲阜市| 乐至县| 迁安市| 隆化县| 巢湖市| 松滋市| 南岸区| 岢岚县| 正蓝旗| 龙岩市| 黑龙江省| 兖州市| 信丰县| 榕江县| 新和县| 前郭尔| 九龙坡区| 盐津县| 嘉黎县| 宽甸| 昂仁县| 惠安县| 绥化市| 永吉县| 稻城县| 汉川市| 天台县| 瓦房店市| 丹棱县| 岢岚县| 新余市| 平定县|