java-word模板转化为pdf

news2025/1/13 13:56:19

文章目录

  • 一、引入包
    • 1.1在pom引入
    • 1.2 因为我们的项目是打包成jar,所以以上方法在本地idea运行没有问题,linux系统不行
    • 1.2.1解决方法1
    • 1.2.2解决方法2
  • 二、配置文件--License.xml--去除水印
    • 2.1 license.xml直接放到resources的根目录下即可
    • 2.2 工具类
  • 三、调用
  • 效果

一、引入包

链接: https://pan.baidu.com/s/13V2ihOpRoLf3R9MFCH2kHQ?pwd=be64 提取码: be64 复制这段内容后打开百度网盘手机App,操作更方便哦

1.1在pom引入


        <dependency>
            <groupId>com.aspose</groupId>
            <artifactId>aspose-words</artifactId>
            <version>15.8.0</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/lib/aspose-words-15.8.0.jar</systemPath>
        </dependency>

在这里插入图片描述

1.2 因为我们的项目是打包成jar,所以以上方法在本地idea运行没有问题,linux系统不行

1.2.1解决方法1

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <!-- 打包时会将本地jar一起打包 -->
            <configuration>
                <includeSystemScope>true</includeSystemScope>
            </configuration>     
        </plugin>
    </plugins>
</build>


或者是

<build>
   <resources>
    <resource>
      <directory>lib</directory>
      <targetPath>/BOOT-INF/lib/</targetPath>
      <includes>
        <include>**/*.jar</include>
      </includes>
    </resource>
   </resources>
 </build>


1.2.2解决方法2

对比方法一,我觉得还是挺垃圾的,谁想动一个摇摇欲坠的项目

         <dependency>
            <groupId>com.aspose</groupId>
            <artifactId>aspose-words</artifactId>
            <version>15.8.0</version>
        </dependency>
该语句中参数:
DgroupId :组id 【对应pom中的groupId】
DartifactId:项目id 【对应pom中的artifactId】
Dversion:版本号 【对应pom中的version】
Dfile:jar包的绝对路径
Dpackaging:是什么包
DgeneratePom:是否生成pom


直接使用maven命令

mvn install:install-file -Dfile=C:\Users\yhc\Desktop\aspose-words-15.8.0.jar -DgroupId=com.aspose -DartifactId=aspose-words -Dversion=15.8.0 -Dpackaging=jar -DgeneratePom=true

在这里插入图片描述

不想运行也没关系,我放在云盘了,自行拷贝

在这里插入图片描述

生成这个文件夹,把这个放在你们的maven仓库就行了

在这里插入图片描述

二、配置文件–License.xml–去除水印

2.1 license.xml直接放到resources的根目录下即可

<License>
    <Data>
        <Products>
            <Product>Aspose.Total for Java</Product>
            <Product>Aspose.Words for Java</Product>
        </Products>
        <EditionType>Enterprise</EditionType>
        <SubscriptionExpiry>20991231</SubscriptionExpiry>
        <LicenseExpiry>20991231</LicenseExpiry>
        <SerialNumber>8bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7</SerialNumber>
    </Data>
    <Signature>sNLLKGMUdF0r8O1kKilWAGdgfs2BvJb/2Xp8p5iuDVfZXmhppo+d0Ran1P9TKdjV4ABwAgKXxJ3jcQTqE/2IRfqwnPf8itN8aFZlV3TJPYeD3yWE7IT55Gz6EijUpC7aKeoohTb4w2fpox58wWoF3SNp6sK6jDfiAUGEHYJ9pjU=</Signature>
</License>

在这里插入图片描述

2.2 工具类

import cn.hutool.system.OsInfo;
import cn.hutool.system.SystemUtil;
import com.aspose.words.Document;
import com.aspose.words.FontSettings;
import com.aspose.words.License;
import com.aspose.words.SaveFormat;
import lombok.SneakyThrows;
import org.springframework.core.io.ClassPathResource;
import org.apache.poi.ooxml.POIXMLDocument;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import java.io.InputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.*;
import java.util.Map.Entry;


/**
 * 通过word模板生成新的word工具类
 */
public class WordUtils {


    /**
     * 加载license 用于破解 不生成水印
     *
     */
    @SneakyThrows
    private static void getLicense() {
        try (InputStream inputStream = WordUtils.class.getClassLoader().getResourceAsStream("license.xml")) {
            License license = new License();
            license.setLicense(inputStream );
            //乱码问题
            garbledCode();
        }
    }

    /**
     * 乱码问题
     */
    @SneakyThrows
    private static void  garbledCode() {
        //把用到的字体包从windows的C:\Windows\Fonts里所有文件,复制到linux的/usr/share/fonts/windows下。
        OsInfo osInfo = SystemUtil.getOsInfo();
        if(osInfo.isLinux()){
            FontSettings.setFontsFolder("/usr/share/fonts/windows"+File.separator, true);
        }
    }

    /**
     * 获取Resource路径
     * @return
     */
    public static String getResourcePath(){
        String path = Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource("")).getPath();
        String osName = System.getProperty("os.name");
        if (osName.contains("Windows")) {
            return path.substring(1);
        }else{
            path="/usr/local";
        }
        return path;
    }



    /**
     * word转pdf
     * @param outputUrl word文件保存的路径
     * @return pdf生成的路径
     */
    @SneakyThrows
    public static String wordToPdf(String outputUrl) {
        //生成pdf
        String outputPdf = outputUrl.replace("docx", "pdf");
        File filePdf = new File(outputPdf);
        FileOutputStream streamPdf = new FileOutputStream(filePdf);

        //凭证 不然切换后有水印
        getLicense();

        //开始渲染转化,并且输出
        Document doc = new Document(outputUrl); // Address是将要被转化的word文档
        doc.save(streamPdf, SaveFormat.PDF);// 全面支持DOC, DOCX, OOXML, RTF HTML, OpenDocument, PDF,  EPUB, XPS, SWF 相互转换
        streamPdf.close();

       return outputPdf;
    }

    /**
     * web下载文件
     * @param path pdf临时保存路径
     * @param response
     */
    public static void downResult(String  path, HttpServletResponse response){
        try{

            File file = new File(path);
            boolean exists = file.exists();
            if (!exists){
                throw new Exception("文件不存在!");
            }
            // 获取文件名
            String filename = file.getName();

            // 获取文件后缀名
            String ext = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();

            // 将文件写入输入流
            FileInputStream fileInputStream = new FileInputStream(file);
            InputStream fis = new BufferedInputStream(fileInputStream);
            byte[] buffer = new byte[fis.available()];
            fis.read(buffer);
            fis.close();

            // 清空response
            response.reset();
            // 设置response的Header
            response.setCharacterEncoding("UTF-8");
            //Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存
            //attachment表示以附件方式下载 inline表示在线打开 "Content-Disposition: inline; filename=文件名.mp3"
            // filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
            response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
            // 告知浏览器文件的大小
            response.addHeader("Content-Length", "" + file.length());
            OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());

            //设置响应格式,已文件流的方式返回给前端。
            response.setContentType("application/octet-stream");
            outputStream.write(buffer);
            outputStream.flush();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 判断文本中时候包含$
     *
     * @param text 文本
     * @return 包含返回true, 不包含返回false
     */
    public static boolean checkText(String text) {
        boolean check = false;
        if (text.indexOf("$") != -1) {
            check = true;
        }
        return check;
    }

    /**
     * 根据模板生成新word文档
     * 判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
     *
     * @param inputUrl  模板存放地址
     * @param outputUrl 新文档存放地址
     * @param textMap   需要替换的信息集合
     * @param tableList 需要插入的表格信息集合
     */
    public static boolean changWord(String inputUrl, String outputUrl,
                                    Map<String, String> textMap, List<List<String[]>> tableList) {
        //模板转换默认成功
        boolean changeFlag = true;
        try {
            //获取docx解析对象
            XWPFDocument document = new XWPFDocument(POIXMLDocument.openPackage(inputUrl));
            //解析替换文本段落对象
            WordUtils.changeText(document, textMap);
            //解析替换表格对象
            WordUtils.changeTable(document, textMap, tableList);
            //生成新的word
            File file = new File(outputUrl);
            FileOutputStream stream = new FileOutputStream(file);
            document.write(stream);
            stream.close();
            System.out.println("成功生成!" + outputUrl);
        } catch (IOException e) {
            e.printStackTrace();
            changeFlag = false;
        }

        return changeFlag;

    }


    // word表格跨列合并单元格
    //row 指定行、fromCell 开始列数、toCell 结束列数。
    public static void mergeColumn(XWPFTable table, int row, int fromCell, int toCell) {
        for (int cellIndex = fromCell; cellIndex <= toCell; cellIndex++) {
            XWPFTableCell cell = table.getRow(row).getCell(cellIndex);
            if (cellIndex == fromCell) {
                cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
            } else {
                cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
            }
        }
    }

    // word表格跨行并单元格
    //col 指定列、fromRow 开始行、toRow 结束行。
    public static void mergeLine(XWPFTable table, int col, int fromRow, int toRow) {
        for (int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) {
            XWPFTableCell cell = table.getRow(rowIndex).getCell(col);
            if (rowIndex == fromRow) {
                cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
            } else {
                cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
            }
        }
    }


    /**
     * 替换段落文本
     *
     * @param document docx解析对象
     * @param textMap  需要替换的信息集合
     */
    public static void changeText(XWPFDocument document, Map<String, String> textMap) {
        //获取段落集合
        List<XWPFParagraph> paragraphs = document.getParagraphs();
        for (XWPFParagraph paragraph : paragraphs) {
            //判断此段落时候需要进行替换
            String text = paragraph.getText();
            if (checkText(text)) {
                List<XWPFRun> runs = paragraph.getRuns();
                for (XWPFRun run : runs) {
                    //替换模板原来位置
                    // run.setText(changeValue(run.toString(), textMap),0);
                    String textValue = changeValue(run.toString(), textMap);
                    run.setText(textValue, 0);
                }
            }
        }
    }


    /**
     * 遍历表格
     *
     * @param rows    表格行对象
     * @param textMap 需要替换的信息集合
     */
    public static void eachTable(List<XWPFTableRow> rows, Map<String, String> textMap) {
        for (XWPFTableRow row : rows) {
            List<XWPFTableCell> cells = row.getTableCells();
            for (XWPFTableCell cell : cells) {
                //判断单元格是否需要替换
                if (checkText(cell.getText())) {
                    List<XWPFParagraph> paragraphs = cell.getParagraphs();
                    for (XWPFParagraph paragraph : paragraphs) {
                        List<XWPFRun> runs = paragraph.getRuns();
                        for (XWPFRun run : runs) {
                            run.setText(changeValue(run.toString(), textMap), 0);
                        }
                    }
                }
            }
        }
    }



    /**
     * 为表格插入数据,行数不够添加新行
     *
     * @param table     需要插入数据的表格
     * @param tableList 插入数据集合
     */
    public static void insertTable(XWPFTable table, List<String[]> tableList) {

        //创建行,根据需要插入的数据添加新行,不处理表头
        for (int i = 1; i < tableList.size(); i++) {
            XWPFTableRow row = table.createRow();
        }
        //遍历表格插入数据
        List<XWPFTableRow> rows = table.getRows();
        for (int i = 1; i < rows.size(); i++) {
            XWPFTableRow newRow = table.getRow(i);
            List<XWPFTableCell> cells = newRow.getTableCells();
            for (int j = 0; j < cells.size(); j++) {
                XWPFTableCell cell = cells.get(j);
                cell.setText(tableList.get(i - 1)[j]);
            }
        }
    }

    /**
     * 替换表格对象方法
     *
     * @param document  docx解析对象
     * @param textMap   需要替换的信息集合
     * @param tableList 需要插入的表格信息集合
     */
    public static void changeTableDynamic(XWPFDocument document, Map<String, String> textMap,
                                          List<List<String[]>> tableList, List<List<int[]>> mergeLineTable, List<List<int[]>> mergeColumnTable) {
        //获取表格对象集合
        List<XWPFTable> tables = document.getTables();
        for (int i = 0; i < tables.size(); i++) {
            XWPFTable table = tables.get(i);
            if (mergeLineTable == null && mergeColumnTable == null) {
                insertTableDynamicCol(table, tableList.get(i));
            } else {
                insertTableMergeDynamicCol(table, tableList.get(i), mergeLineTable.get(i), mergeColumnTable.get(i));
            }
        }
    }
    /**
     * 替换表格对象方法-->(带合并单元格方法)
     *
     * @param document  docx解析对象
     * @param textMap   需要替换的信息集合
     * @param tableList 需要插入的表格信息集合
     */
    public static void changeTable(XWPFDocument document, Map<String, String> textMap,
                                   List<List<String[]>> tableList) {
        //获取表格对象集合
        List<XWPFTable> tables = document.getTables();
        for (int i = 0; i < tables.size(); i++) {
            //只处理行数大于等于2的表格,且不循环表头
            XWPFTable table = tables.get(i);
            if (table.getRows().size() > 1) {
                //判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
                if (checkText(table.getText())) {
                    List<XWPFTableRow> rows = table.getRows();
                    //遍历表格,并替换模板
                    eachTable(rows, textMap);
                } else {
//                  System.out.println("插入"+table.getText());
                    insertTable(table, tableList.get(i));
                }
            }
        }
    }


    /**
     * 为表格插入数据,行数不够添加新行 列不够添加新列
     *
     * @param table     需要插入数据的表格
     * @param tableList 插入数据集合
     */
    public static void insertTableDynamicCol(XWPFTable table, List<String[]> tableList) {
        //遍历表格插入数据
        List<XWPFTableRow> rows = table.getRows();
        // 列
        int col = rows.get(0).getTableCells().size();
        //创建列,根据需要插入的数据添加新列
        for (int i = col; i < tableList.get(0).length; i++) {
            table.addNewCol();
        }

        //创建行,根据需要插入的数据添加新行
        for (int i = rows.size() - 1; i < tableList.size(); i++) {
            XWPFTableRow row = table.createRow();
        }

        table.removeRow(0);

        for (int i = 0; i < rows.size(); i++) {
            XWPFTableRow newRow = table.getRow(i);
            List<XWPFTableCell> cells = newRow.getTableCells();
            for (int j = 0; j < cells.size(); j++) {
                XWPFTableCell cell = cells.get(j);
                //设置表格内容水平居中
                CTTc cttc = cell.getCTTc();
                CTTcPr ctPr = cttc.addNewTcPr();
                ctPr.addNewVAlign().setVal(STVerticalJc.CENTER);
                cttc.getPList().get(0).addNewPPr().addNewJc().setVal(STJc.CENTER);
                cell.setText(tableList.get(i)[j]);
            }
        }
    }


    /**
     * 为表格插入数据,行数不够添加新行 列不够添加新列--->带合并单元格
     *
     * @param table     需要插入数据的表格
     * @param tableList 插入数据集合
     */
    public static void insertTableMergeDynamicCol(XWPFTable table, List<String[]> tableList, List<int[]> mergeLine, List<int[]> mergeColumn) {
        //遍历表格插入数据
        List<XWPFTableRow> rows = table.getRows();
        // 列
        int col = rows.get(0).getTableCells().size();
        //创建列,根据需要插入的数据添加新列
        if (tableList.size() > 0) {
            for (int i = col; i < tableList.get(0).length; i++) {
                table.addNewCol();
            }
        }

        //创建行,根据需要插入的数据添加新行
        for (int i = rows.size() - 1; i < tableList.size(); i++) {
            XWPFTableRow row = table.createRow();
        }

        table.removeRow(0);

        for (int i = 0; i < rows.size(); i++) {
            XWPFTableRow newRow = table.getRow(i);
            List<XWPFTableCell> cells = newRow.getTableCells();
            for (int j = 0; j < cells.size(); j++) {
                XWPFTableCell cell = cells.get(j);
                //设置表格内容水平居中
                CTTc cttc = cell.getCTTc();
                CTTcPr ctPr = cttc.addNewTcPr();
                ctPr.addNewVAlign().setVal(STVerticalJc.CENTER);
                cttc.getPList().get(0).addNewPPr().addNewJc().setVal(STJc.CENTER);
                cell.setText(tableList.get(i)[j]);
            }
        }
        if (tableList.size() > 0) {
            if (mergeLine != null && !mergeLine.isEmpty()) {
                for (int i = 0; i < mergeLine.size(); i++) {
                    mergeLine(table, mergeLine.get(i)[0], mergeLine.get(i)[1], mergeLine.get(i)[2]);
                }
            }
            if (mergeColumn != null && !mergeColumn.isEmpty()) {
                for (int i = 0; i < mergeColumn.size(); i++) {
                    mergeColumn(table, mergeColumn.get(i)[0], mergeColumn.get(i)[1], mergeColumn.get(i)[2]);
                }
            }
            // 设置列宽
            table.setWidthType(TableWidthType.DXA);



//            //手动添加新的一行
//            XWPFTableCell cell = table.getRow(rows.size() - 1).getCell(0);
//            /** 设置水平居中 */
//            CTTc cttc = cell.getCTTc();
//            CTTcPr ctPr = cttc.addNewTcPr();
//            ctPr.addNewVAlign().setVal(STVerticalJc.CENTER);
//            cttc.getPList().get(0).addNewPPr().addNewJc().setVal(STJc.RIGHT);
//
//
//            XWPFParagraph para = cell.getParagraphArray(0);
//
//            //一个XWPFRun代表具有相同属性的一个区域。
//            XWPFRun run = para.createRun();
//
//            run.setText("小计:");
//            run = para.createRun();
//            run.setColor("FF0000");
//            run.setText(tableList.get(tableList.size() - 1)[tableList.get(0).length - 1]);
//            run = para.createRun();
//            run.setColor("000000");
//            run.setText("元");
        }

    }



    /**
     * 匹配传入信息集合与模板
     *
     * @param value   模板需要替换的区域
     * @param textMap 传入信息集合
     * @return 模板需要替换区域信息集合对应值
     */
    public static String changeValue(String value, Map<String, String> textMap) {
        Set<Entry<String, String>> textSets = textMap.entrySet();
        for (Entry<String, String> textSet : textSets) {
            //匹配模板与替换值 格式${key}
            String key = "${" + textSet.getKey() + "}";
            if (value.indexOf(key) != -1) {
                //全部参数替换
                // value = textSet.getValue();
                //仅替换参数
                value = value.replace(key, textSet.getValue()==null?"无":textSet.getValue());
            }
        }
        //模板未匹配到区域替换为空
        if (checkText(value)) {
            value = "";
        }
        return value;
    }


    /**
     * 关闭输入流
     *
     * @param is
     */
    private static void close(InputStream is) {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 关闭输出流
     *
     * @param os
     */
    private static void close(OutputStream os) {
        if (os != null) {
            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 实例1
     *
     * 根据模板生成Pdf并下载
     * 判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
     * @param dynamic  模板存放地址 true 有合并的内容
     * @param inputUrl  模板存放地址
     * @param response
     * @param textMap   需要替换的信息集合
     * @param tableList 需要插入的表格信息集合
     */
    public static String changWordToPdfDownloadUrl(boolean dynamic, String inputUrl, String outputUrl, String fileName, HttpServletResponse response,
                                                   Map<String, String> textMap, List<List<String[]>> tableList, List<List<int[]>> mergeLineTable, List<List<int[]>> mergeColumnTable) {
        //模板转换默认成功
        boolean changeFlag = true;
        try {
            //获取docx解析对象
            File inputWordFile = new File(inputUrl);
            InputStream source = new FileInputStream(inputWordFile);
            XWPFDocument document = new XWPFDocument(source);

            //解析替换文本段落对象
            WordUtils.changeText(document, textMap);
            //解析替换表格对象 true 有合并的内容
            if (!dynamic) {
                WordUtils.changeTable(document, textMap, tableList);
            } else {
               // true 有合并的内容
                WordUtils.changeTableDynamic(document, textMap, tableList, mergeLineTable, mergeColumnTable);
            }
            //生成新的word
            File file = new File(outputUrl);
            FileOutputStream stream = new FileOutputStream(file);
            document.write(stream);
            stream.close();


            //word转化pdf 渲染
            String outputPdf= wordToPdf(outputUrl);

            //web下载
            downResult(outputPdf,response);

            // 删除过渡word文件
            if (file.exists()) {
                file.delete();
            }
            // 删除过渡pdf文件
            File filePdf=  new File(outputUrl);
            if (filePdf.exists()) {
                filePdf.delete();
            }
            close(source);
            return outputPdf;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 实例2
     *
     * @param dynamic
     * @param inputUrl
     * @param outputUrl
     * @param fileName
     * @param response
     * @param textMap
     * @param tableList
     * @param mergeLineTable
     * @param mergeColumnTable
     * @return
     */
    public static Map<String, String> changWordToPdfDownloadUrlAndMD5(boolean dynamic, String inputUrl, String outputUrl, String fileName, HttpServletResponse response,
                                                                      Map<String, String> textMap, List<List<String[]>> tableList, List<List<int[]>> mergeLineTable, List<List<int[]>> mergeColumnTable) {
        //模板转换默认成功
        boolean changeFlag = true;
        try {
            //获取docx解析对象
            File inputWordFile = new File(inputUrl);
            InputStream source = new FileInputStream(inputWordFile);
            XWPFDocument document = new XWPFDocument(source);

            //解析替换文本段落对象
            WordUtils.changeText(document, textMap);
            //解析替换表格对象
            if (!dynamic) {
                WordUtils.changeTable(document, textMap, tableList);
            } else {
                /**
                 * mergeLineTable,行设置
                 * mergeColumnTable,列设置
                 */
                WordUtils.changeTableDynamic(document, textMap, tableList, mergeLineTable, mergeColumnTable);
            }
            //生成新的word
            File file = new File(outputUrl);
            FileOutputStream stream = new FileOutputStream(file);
            document.write(stream);
            stream.close();


            //word转化pdf 渲染
            String outputPdf= wordToPdf(outputUrl);

            //web下载
            downResult(outputPdf,response);


            File filePdf=  new File(outputUrl);
            String stringMD5 = MD5Util.MD5File(filePdf);
            Map<String, String> result = new HashMap<>();
            result.put("url", outputPdf);
            result.put("MD5", stringMD5);

            // 删除过渡word文件
            if (file.exists()) {
                file.delete();
            }
            // 删除过渡pdf文件
            if (filePdf.exists()) {
                filePdf.delete();
            }
            close(source);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }






    /**
     * 预付款证明单pdf
     * @param dynamic
     * @param inputStream 原始文件输入流
     * @param outputUrl 输出文件 路径和名称定义
     * @param response
     * @param textMap 替换${XX}内容
     * @param tableList  插入表数据
     * @param mergeLineTable 行合并
     * @param mergeColumnTable 列合并
     * @return
     */
    public static Map<String, String> exportProofOfAdvancePayment(boolean dynamic, InputStream inputStream, String outputUrl, HttpServletResponse response,
                                                                                Map<String, String> textMap, List<List<String[]>> tableList, List<List<int[]>> mergeLineTable, List<List<int[]>> mergeColumnTable) {
        //模板转换默认成功
        boolean changeFlag = true;
        try {

            InputStream source = inputStream;
            XWPFDocument document = new XWPFDocument(inputStream);

            //解析替换文本段落对象
            WordUtils.changeText(document, textMap);
            //解析替换表格对象
            if (!dynamic) {
                WordUtils.changeTable(document, textMap, tableList);
            } else {
                /**
                 * mergeLineTable,行设置
                 * mergeColumnTable,列设置
                 */
                WordUtils.changeTableDynamic(document, textMap, tableList, mergeLineTable, mergeColumnTable);
            }
            //生成新的word
            File file = new File(outputUrl);
            FileOutputStream stream = new FileOutputStream(file);
            document.write(stream);
            stream.close();


            //word转化pdf 渲染
            String outputPdf= wordToPdf(outputUrl);

            //web下载
            downResult(outputPdf,response);


            File filePdf=  new File(outputUrl);
            String stringMD5 = MD5Util.MD5File(filePdf);
            Map<String, String> result = new HashMap<>();
            result.put("url", outputPdf);
            result.put("MD5", stringMD5);
            // 删除过渡word文件
            if (file.exists()) {
                file.delete();
            }
            // 删除过渡pdf文件
            if (filePdf.exists()) {
                filePdf.delete();
            }
            close(source);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


}

MD5File工具类

/**
 *  @author   yanghucheng
 *  @created  2017/08/14
 *  Copyright (C),2007-2016, LonBon Technologies Co. Ltd. All Rights Reserved.
 */
package com.zdft.warehouse.common.util.pdf;

import org.apache.commons.codec.binary.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * The type Md 5 util.
 *
 * @author  yanghucheng
 * @Date    2017/08/14
 */
public class MD5Util {
    private static Logger logger = LoggerFactory.getLogger(MD5Util.class);
    /**
     * Byte array to hex string string.
     *
     * @param b the b
     * @return the string
     */
    private static String byteArrayToHexString(byte b[]) {
        StringBuffer resultSb = new StringBuffer();
        for (int i = 0; i < b.length; i++) {
            resultSb.append(byteToHexString(b[i]));
        }

        return resultSb.toString();
    }

    /**
     * Byte to hex string string.
     *
     * @param b the b
     * @return the string
     */
    private static String byteToHexString(byte b) {
        int n = b;
        if (n < 0) {
            n += 256;
        }
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigits[d1] + hexDigits[d2];
    }

    /**
     * Md 5 encode string.
     *
     * @param origin      the origin
     * @param charsetname the charsetname
     * @return the string
     */
    public static String MD5Encode(String origin, String charsetname) {
        String resultString = null;
        try {
            resultString = new String(origin);
            MessageDigest md = MessageDigest.getInstance("MD5");
            if (charsetname == null || "".equals(charsetname)) {
                resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
            } else {
                resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
            }
        } catch (IOException|NoSuchAlgorithmException e) {
            e.printStackTrace();
            logger.error(e.getMessage());
            return null;
        }
        return resultString.toUpperCase();
    }

    /**
     * Md 5 encode utf 8 string.
     *
     * @param origin the origin
     * @return the string
     */
    public static String MD5EncodeUtf8(String origin) {
        //origin = origin +PropertiesUtil.getProperty("password.salt", "");
        return MD5Encode(origin, "utf-8");
    }

    public static String MD5File(File file) {
        FileInputStream fileInputStream = null;
        try {
            MessageDigest MD5 = null;
            MD5 = MessageDigest.getInstance("MD5");
            fileInputStream = new FileInputStream(file);
            byte[] buffer = new byte[8192];
            int length;
            while ((length = fileInputStream.read(buffer)) != -1) {
                MD5.update(buffer, 0, length);
            }
            return new String(Hex.encodeHex(MD5.digest()));
        } catch (IOException | NoSuchAlgorithmException e) {
            logger.error(e.getMessage());
            return null;
        }finally {
            try {
                if (fileInputStream != null){
                    fileInputStream.close();
                }
            } catch (IOException e) {
                logger.error(e.getMessage());
            }
        }
    }

    /**
     * The constant hexDigits.
     */
    private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5",
            "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};

}

三、调用

在这里插入图片描述

在这里插入图片描述

控制层

    @GetMapping("/export/test")
    @ApiOperation("打印预付款支付证明单")
    public void exportProofOfAdvancePayment( HttpServletResponse response) throws IOException {
        prepaymentPoolDetailService.exportProofOfAdvancePaymentV2(response);
    }

service

void exportProofOfAdvancePaymentV2(HttpServletResponse response) throws IOException;

serviceImpl

    @Override
    public void exportProofOfAdvancePaymentV2(HttpServletResponse response) throws IOException {

        DecimalFormat df = new DecimalFormat("#,##0.00");
        InputStream inputStream = new ClassPathResource("/exportModel/预付款支付证明单.docx").getInputStream();

        String filepathOut = WordUtils.getResourcePath() + "/" + UUID.randomUUID().toString() + "_预付款支付证明单.docx";

        Map<String, String> baseMap = new HashMap<String, String>();
        //支付申请时间
        baseMap.put("createTime",DateUtils.format(new Date()));
        //打印日期
        baseMap.put("newTime",DateUtils.format(new Date()));
        //业务申办人
        baseMap.put("createUser","业务申办人");
        //供应商
        baseMap.put("vendorName","供应商");
        //核算公司
        baseMap.put("vendorCorpName","核算公司");

        //应付款金额
        baseMap.put("deductionAmount","55.00");
        //大写金额
        baseMap.put("money", "五十五元整");
        //付款方式
        baseMap.put("payMethod","现金支付");
        //收款人
        baseMap.put("payee","代码浪人");
        //已发放金额
        baseMap.put("grantAdvanceAmount","10.20");
        //本次发放金额
        baseMap.put("thisMoney","56.21");
        //开户行
        baseMap.put("bankName","中国银行");
        //帐号
        baseMap.put("account","45646465");
        //备注
        baseMap.put("remark","无");
        //单据号
        baseMap.put("processNumber","yfk123");
        //单据号
        baseMap.put("prepaymentNo","No123");
        //申请部门
        baseMap.put("dept","部门");



        List<List<String[]>> tableList = new ArrayList<>();//建立所要插入每个表的所有数据


        List<String[]> tableDate=new ArrayList<>();//具体表数据
        int index=1;
        for (int i = 1; i < 2; i++) {
//            tableDate.add(new String[]{"","","","","",""});
            tableDate.add(new String[]{index + "", "现金支付","566", DateUtils.format(new Date()), "备注"});
            index++;
        }
        tableList.add(tableDate);
        tableList.add(tableDate);
        try {
            WordUtils.exportProofOfAdvancePayment(false, inputStream, filepathOut, response, baseMap, tableList, null, null);

        } catch (Exception e) {
            throw e;
        }
    }


效果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/444146.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

部署架构 因为单体架构痛点 升级到微服务架构

如图为单体部署 痛点 多人协作可能产生很多的回归测试 代码管理复杂度提升 软件包升级会导致增加测试次数 举例 单体电商 1增加功能(增加小程序平台) 2 并发增加 出现 1 代码复用 2 系统间相互调用 3 接口不仅要对外服务&#xff0c;也得对内提供接口 4 数据分析功…

OTG是什么意思?

OTG是什么意思&#xff1f; OTG是怎么样实现的&#xff1f; TYPE-C接口的手机如何实现同时充电OTG功能&#xff1f; OTG是什么意思&#xff1f; OTG是On-The-Go的缩写&#xff0c;是一项新兴技术&#xff0c;主要应用于不同的设备或移动设备间的联接&#xff0c;进行数据交…

腾讯安全助力高校信息安全建设,护航湾区教育高质量发展

2023年4月20日-21日&#xff0c;首届大湾区信息网络安全大会在广州市长隆国际会展中心隆重召开。会议以“同筑安全屏障&#xff0c;共赢湾区未来”为主题&#xff0c;旨在响应国家安全战略&#xff0c;推动粤港澳大湾区信息网络安全的建设和发展&#xff0c;保障经济社会稳定运…

「OceanBase 4.1 体验」|国产分布式数据库不好用?别再打脸了

文章目录 分布式数据库分布式数据库有哪些&#xff1f;OceanBase4.1安装部署Index Skip Scan总结 随着互联网的高速发展和数据量的爆炸式增长&#xff0c;如何能够高效、可靠、安全地存储海量数据成为了每个企业的重要课题。 分布式数据库 分布式数据库通常是由多个独立的数据…

无人机各个类型介绍

为了执行&#xff0c;无人机可能由类似的元件制成&#xff0c;但无论是它们的能力&#xff0c;还是由什么组成的&#xff0c;它们都在某种程度上有所不同。大多数无人机都是为了执行特定任务而制造的&#xff0c;因此以特定的方式建造&#xff0c;以适应它们将要使用的环境。 …

docker-mysql-主从设计

一、docker主从 1.新建主从镜像 docker run -p 3307:3306 --name mysql -e MYSQL_ROOT_PASSWORDroot -d mysql:5.7.28 docker run -p 3308:3306 --name mysqlslave -e MYSQL_ROOT_PASSWORDroot -d mysql:5.7.282.分别进入两个容器&#xff0c;修改配置文件 #1.进入容器 $ do…

Java8新特性【函数式接口、Lambda表达、Stream流】

一、Lambda表达式 Java8是Java语言自JDK1.5以后的一个重大的里程碑版本&#xff0c;因为它增加了很多新特性&#xff0c;这些新特性会改变编程的风格和解决问题的方式。 例如&#xff1a;日期时间API、Lambda表达式&#xff08;λ&#xff09;、Stream API(操作集合)、方法引用…

用户量达6.33亿即时配送,拼的不止这些

上班忘了带电脑&#xff0c;回去取的话&#xff0c;来回要3个小时&#xff0c;还得损失半天薪资&#xff1b;好友生日&#xff0c;想在聚餐时给对方一个惊喜&#xff0c;但带着蛋糕去又容易提前剧透&#xff1b;老人突然生病在家&#xff0c;医院的号难挂、得排长队&#xff0c…

TSINGSEE视频能力在交通运输可视化管理平台项目中的应用

一、行业背景 为贯彻落实交通强国试点工作要求&#xff0c;提升交通建设工程信息化管理水平&#xff0c;进一步强化交通建设工程管理&#xff0c;各地政府部门决定在全省高速公路、国省道、大型水运、地方铁路、机场工程安装视频监控系统&#xff0c;在建交通建设项目尚未安装…

OldWang带你了解MySQL(九)

文章目录 &#x1f525;MySQL中的索引&#x1f525;MySQL中的索引类型&#x1f525;普通索引&#x1f525;唯一索引&#x1f525;主键索引&#x1f525;组合索引 &#x1f525;MySQL中的索引 索引介绍 索引是对数据库表中的一列或多列值进行排序的一种结构&#xff0c;使用索引…

【C++学习笔记】字符串、向量和数组

字符串类型 1.C语言风格字符串&#xff1a;char 变量名[] "字符串值" 1. char: 字符常量或者单个字符 单引号定义 &#xff1b;字符串常量用 双引号 定义 2. 输出直接用cout char str1[] "hello world"; cout << str1 << endl;2.C语言风格…

Linux环境下 通过V4L2读取视频+UDP发送图片文件

该图为整个项目的流程图 其中左边的流程为总流程&#xff0c;包括通过中断读取摄像头的帧数据&#xff0c;通过内存映射将内核态的数据映射到用户态&#xff0c;读取用户态的数据&#xff0c;采用循环发送图片数据。 右边是发送图片的流程图&#xff0c;将用户态的缓冲区的数…

780E编译底包教程

这里写目录标题 准备1 安装开发环境准备2 拉取编译工程源码 代码编译修改或者增加用户程序说明 准备 1 安装开发环境准备 需要用户自行安装好Xmake\vscode\git 环境教程传送门 2 拉取编译工程源码 注意, 需要两个库 主库 https://gitee.com/openLuat/LuatOS bsp库 https://…

LAMP架构中的网站搭建

前言&#xff1a;本次操作依赖于LAMP的环境已经配置完全&#xff0c;网站也是依赖于开发人员现有的网站包框架&#xff0c;实施在LAMP已搭建好的环境进行安装部署 1. 对mysql进行操作 ——创建数据库&#xff0c;并进行授权 1.创建数据库&#xff0c;并进行授权 mysql -u roo…

Redis6学习

Redis6 1. NoSQL数据库简介 1.1 技术发展 技术的分类 1、解决功能性的问题&#xff1a;Java、Jsp、RDBMS、Tomcat、HTML、Linux、JDBC、SVN。 2、解决扩展性的问题&#xff1a;Struts、Spring、SpringMVC、Hibernate、Mybatis。 3、解决性能的问题&#xff1a;NoSQL、Jav…

国产什么牌子的蓝牙耳机音质好?国产适合听音乐的蓝牙耳机推荐

现如今&#xff0c;蓝牙耳机的性能越来越多&#xff0c;一款蓝牙耳机不可能将各种性能做到极致。大家在选择蓝牙耳机时&#xff0c;无外乎从佩戴、音质、降噪、延迟等因素出发&#xff0c;那么&#xff0c;国产什么牌子的蓝牙耳机音质好&#xff1f;根据这个问题&#xff0c;我…

Docker更换国内镜像源

什么是Docker Docker 是一个开源的应用容器引擎&#xff0c;基于 Go 语言 并遵从 Apache2.0 协议开源。 Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#xff0c;也可以实现虚拟化。 容器是完全…

【三十天精通Vue 3】第十二天 Vue 3 的函数式组件详解(过滤器已废弃)

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: 三十天精通 Vue 3 文章目录 引言一、Vue3 中的函数式组件1.1 函数式组件的概念和特点1.2 函数…

DolphinDB 计算节点使用指南

导读 为了提升 DolphinDB 在高并发读写场景下的性能与稳定性&#xff0c;DolphinDB 在架构上引入了计算节点 &#xff08;compute node&#xff09; 。计算节点接管了数据节点的部分职能&#xff0c;负责响应客户端的请求并返回结果。在架构层面&#xff0c;将集群的计算与存储…

QT - 布局方式

Qt里面的头文件和类名是一致的&#xff0c;知道头文件就知道类名&#xff0c;反之亦然 Qt头文件是没有.h的&#xff0c;基本都是以大写的Q开头 后续的代码编写都在widget.h和widget.cpp 一. widget.h #ifndef WIDGET_H #define WIDGET_H #include <QWidget>/*QT系统自己使…