springboot word-poi-tl填充word动态表格,word转换成pdf,无水印

2,451 阅读3分钟

需求

公司业务人员添加订单时要自动生成出库单pdf格式。
考虑过使用apache poi 功能强大,依赖组件多,文档少,不友好,放弃。
使用itext,我需求里边有用到表格,需要代码手画表格,过于繁琐放弃
使用阿里easyexcel,表格填充数据,功能很强大,但不适用我,有些表格内容数据过多,没法自动换行,放弃。
使用poi-tl,加载word模板填充数据生成word,然后通过使用aspose转换pdf,采用。

poi-tl官网

poi-tl官网

word模板

普通文本使用{{字段}}填充数据,代码要与word里边的字段相同即可,赋值数据

image.png 列表循环可以要使用{{goods}}里边的值要使用[值],还是比较简单的。

java代码

前期准备工作就这些,服务端开始 首先是引入jar包,这里采用的是maven引入

    <!-- POI -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>4.1.2</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>4.1.2</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml-schemas</artifactId>
        <version>4.1.2</version>
    </dependency>

    <!-- poi-tl -->
    <dependency>
        <groupId>com.deepoove</groupId>
        <artifactId>poi-tl</artifactId>
        <version>1.10.0</version>
    </dependency>

记得要刷新maven java端,填充数据 循环表格 生成word

创建生成word方法

//创建word 填充数据
@GetMapping(value = "/pdf/{id}")
public AjaxResult generatePDF(@PathVariable("id") Long id) throws IOException {

    HjOrder order = hjOrderService.selectHjOrderById(id);
    //加载模板
    File file = new ClassPathResource("templates/orderTemplate.docx").getFile();
    Map<String, Object> map = new HashMap<>();
    //发往地区
    map.put("city", order.getArea());
    //共计袋数
    map.put("total", 1);
    //物流
    map.put("logistics", order.getLogistics());
    //收货人
    map.put("consignee", order.getConsignee());
    //电话
    map.put("phone", order.getPhone());
    //发货人
    map.put("consignor", order.getConsignor());
    //备注
    map.put("remarks", order.getRemarks());
    //制单人当前用户
    map.put("creator", SecurityUtils.getLoginUser().getUser().getNickName());
    //日期
    map.put("date", DateUtils.getDate());

    Map<String, Object> data = new HashMap<>();
    List<Map<String, Object>> goodsList = new ArrayList<>();
    // 初始化
    int num = 1;
    int totalBag = 0; // 初始化总袋数
    //产品信息添加列表中
    for (HjOrderSpecification specification : order.getHjOrderSpecificationList()) {
        Map<String, Object> good = new HashMap<>();
        good.put("num", num++); //序号
        good.put("invoice", specification.getInvoice() == 0 ? "否" : "是");//发票
        good.put("commission", specification.getCommission());//提成
        good.put("productName", specification.getProductName()); // 产品名称
        good.put("productRemark", specification.getProductRemark());//备注
        good.put("bag", specification.getBag());//袋
        good.put("ton", specification.getTon());//吨
        good.put("money", specification.getMoney());//金额
        good.put("totalPrice", 1600);
        totalBag += specification.getBag();
        goodsList.add(good);
    }
    //总袋数
    map.put("total", totalBag);
    data.put("goods", goodsList);
    //合并map
    data.putAll(map);
    Configure config = Configure.builder().bind("goods", new LoopRowTableRenderPolicy()).build();
    XWPFTemplate template = XWPFTemplate.compile(file, config).render(data);
    template.writeToFile("template1_out.docx");
    }

根据实际情况修改值,这上边是填充数据生成表格

生成后的效果

image.png

word转pdf

我是通过文件流的形式下载的pdf 没有保存到服务器上

// 获取文件流
try {
    InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("template1_out.docx");
    System.out.println(inputStream);
    XWPFTemplate template = XWPFTemplate.compile(inputStream).render(data);

    Map<String, Object> maps = new HashMap<>();
    ByteArrayOutputStream fos = new ByteArrayOutputStream();
    template.write(fos);
    byte[] bytes = fos.toByteArray();
    byte[] convertToPdf = Doc2PdfUtil.doc2Pdf(bytes);
    String bytesRes = StringUtils.bytesToHexString2(convertToPdf);
    maps.put("bytes", bytesRes);
    maps.put("title", System.currentTimeMillis() + "培训服务报告.pdf");
    fos.close();
    template.close();

    return success(maps);
} catch (Exception e) {
    throw new RuntimeException(e);
}

这里有用到bytesToHexString2

public static String bytesToHexString2(byte[] src) {
    StringBuilder stringBuilder = new StringBuilder("");
    if (src != null && src.length > 0) {
        for(int i = 0; i < src.length; ++i) {
            int v = src[i] & 255;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }

            stringBuilder.append(hv);
        }

        return stringBuilder.toString();
    } else {
        return null;
    }
}

这里是转换pdf代码,由于使用aspose,我们需要下载个本地jar包,进行引入,这样就可以没有水印。里边有工具类,可以直接使用 aspose去水印jar包下载链接 下载完成后需要把jar包放在resources/lib下 使用maven进行导入

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

刷新maven 把工具类放到对应的文件夹下,license.xml放到resources下边

package com.ruoyi.business.utils;

import com.aspose.words.*;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;

public class Doc2PdfUtil {

    /**
     * 加载授权配置文件
     *
     * @return
     */
    private static boolean getLicense() {
        boolean result = false;
        try (InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("license.xml")) {
            // License的包路径必须为com.aspose.words.License
            License license = new License();
            license.setLicense(in);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * doc转pdf
     *
     * @return
     */
    public static byte[] doc2Pdf(byte[] bytes) {
        System.out.println("pdf转换中...");
        long old = System.currentTimeMillis();

        try (ByteArrayOutputStream fos = new ByteArrayOutputStream()) {
            // 验证
            if (!getLicense()) {
                throw new RuntimeException("文件转换失败!");
            }
            // 加载字体
            //FontSettings settings = FontSettings.getDefaultInstance();
            //String[] fontFamilyNames = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
            //settings.setFontsFolders(fontFamilyNames, true);
            //LoadOptions loadOptions = new LoadOptions();
            //loadOptions.setFontSettings(settings);

            //加载字体
            //FontSettings settings = FontSettings.getDefaultInstance();
            //settings.setFontsFolder("C:\Windows\Fonts", true);
            //LoadOptions loadOptions = new LoadOptions();
            //loadOptions.setFontSettings(settings);

            ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
            //Document document = new Document(inputStream, loadOptions);
            Document document = new Document(inputStream);

            document.save(fos, SaveFormat.PDF);
            byte[] buffer = fos.toByteArray();
            long now = System.currentTimeMillis();
            System.out.println("pdf转换成功,共耗时:" + ((now - old) / 1000.0) + "秒");
            return buffer;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("文件转换失败!");
        }
    }
}

这样就可以了。

前端预览下载pdf,由于使用的是文件流形式不存服务器,前端需要解析

function handlePDF(row) {
  exportPdf(row.id).then((response) => {
    if (response.code === 200) {
          const data = response.data
          const bytes = hexStr2Byte(data.bytes)

          const blob = new Blob([bytes], {
            type: `application/vnd.openxmlformats-officedocument.wordprocessingml.document`
          })
          const objectUrl = URL.createObjectURL(blob)
          const link = document.createElement('a')
          link.href = objectUrl
          link.setAttribute('download', data.title)
          document.body.appendChild(link)
          link.click()
        } else {
          this.$message.error(response.data.msg)
        }
  });
}

hexStr2Byte方法

function hexStr2Byte(str) {
  var bytes = [];
  for (var i = 0; i < str.length; i += 2) {
    bytes.push(parseInt(str.substr(i, 2), 16));
  }
  return new Uint8Array(bytes);
}

这样就是使用poi-tl填充word循环列表生成word,并word转换pdf了,生成pdf效果图

image.png