Java 将Word文档转换为PDF

最近公司有需求,需要实现数据打印功能。需要将数据填充到对应的模板文件数据,并将word转成pdf流传给前端。其中呢数据的渲染使用了 poi-tl Java Word 的模板引擎,转pdf使用到了Free Spire.Doc for Java 产品(免费java word组件)

poi-tl Word 模板引擎

poi-tl 是一个Word 模板引擎,基于 Microsoft Word 模板和数据生成新的文档,并且支持用户自定义函数,函数可以在 Word 模板的任何位置执行。

导入依赖:

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

具体的使用可以去查相关的文档,比较简单,只需定义要你的模板,查询对应的数据出来

1、简单的测试代码

public static void main(String[] args) throws IOException {
        Map<String, Object> map = new HashMap<>();
        //销售门店
        map.put("storeName", "销售门店AAA");
        //手机号码
        map.put("phone","15880919255");
        //委托书.docx是模板路径
        XWPFTemplate.compile("D://委托书.docx",config)
                .render(map)
                //A委托书.docx是写入的路径
                .writeToFile("D://A委托书.docx");
    }

委托书.docx模板设置如下图


image.png

2、如果模板中有需要打印多行list的 则需要像以下这样设置

public static void main(String[] args) throws IOException {
     
        /*文本*/
        Map<String, Object> map = new HashMap<>();
        //销售门店
        map.put("storeName", "销售门店AAA");
        //手机号码
        map.put("phone","15880919255");
        //定义一个list去存数据
        List<Map<String, Object>> list= Lists.newArrayList();
        for (int i = 0; i < 39; i++) {
            Map<String, Object> data = new HashMap<>();//通过map存放要填充的数据
            data.put("Xh", i + 1);
            data.put("name", "四川有限公司" + i);
            data.put("time", i + 10 + "小时");
            data.put("size", "800" + i);
            list.add(data);
        }
        //将list设置到模板的数据中  list为模板中的值
        map.put("list", list);
        //设置了一个自定义的表格渲染策
        HackLoopTableRenderPolicy policy = new HackLoopTableRenderPolicy();
        //将上面创建的 HackLoopTableRenderPolicy 对象与字符串标识符 "list" 绑定
        Configure config = Configure.newBuilder().bind("list", policy).build();
        //委托书.docx是模板路径
        XWPFTemplate.compile("D://委托书.docx",config)
                .render(map)
                //A委托书.docx是写入的路径
                .writeToFile("D://A委托书.docx");
    }

创建一个名为 HackLoopTableRenderPolicy 的实例,这是一个自定义实现的表格渲染策略类。这个类可能用于处理循环列表或其他迭代结构在渲染成文档时的行为,例如控制表格样式、布局或者处理复杂的数据结构。

导出list的模板如下:


image.png

2、Word转pdf

使用Free Spire.Doc for Java 工具
对应文档链接:https://www.e-iceblue.cn/Downloads/Free-Spire-Doc-JAVA.html

导入依赖:
可以通过maven仓库安装,也可以自行下载安装

<repositories>
    <repository>
        <id>com.e-iceblue</id>
        <name>e-iceblue</name>
        <url>https://repo.e-iceblue.cn/repository/maven-public/</url>
    </repository>
</repositories>


<dependencies>
    <dependency>
        <groupId>e-iceblue</groupId>
        <artifactId>spire.doc.free</artifactId>
        <version>5.2.0</version>
    </dependency>
</dependencies>

示例:

public static void main(String[] args) {

        //加载Word示例文档
        Document document =new Document();
        document.loadFromFile("D://A委托书.docx");
        //保存结果文档
        document.saveToFile("D://最终委托书.pdf", FileFormat.PDF);

}

以下是对应开发的接口代码如下:

package com.yzqc.erp.controller.web;

import com.alibaba.nacos.common.utils.CollectionUtils;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.plugin.table.HackLoopTableRenderPolicy;
import com.google.common.collect.Lists;
import com.spire.doc.*;
import com.yzqc.common.core.enums.MinioBucketsEnum;
import com.yzqc.common.core.utils.SecurityUtils;
import com.yzqc.common.core.web.controller.BaseController;
import com.yzqc.erp.config.properties.ApplicationProperties;
import com.yzqc.erp.model.mtOrderItemTime.MtOrderItemTimeExport;
import com.yzqc.erp.model.mtOrderTime.MtOrderExportInfo;
import com.yzqc.erp.service.IMtOrderTimeService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@RestController
@Api(value = "/mtPdf", tags = "打印")
@RequestMapping("/mtPdf")
public class MtPdfController extends BaseController {

    @Autowired
    private ApplicationProperties applicationProperties;

    @DubboReference(version = "${dubbo.application.version}")
    private IMtOrderTimeService mtOrderTimeService;

    @ApiOperation(value = "导出委托书Word模板")
    @GetMapping(value = "/exportDocx/{id}")
    public void exportDocx(HttpServletResponse response, @PathVariable("id") String id, Date createTime) throws Exception {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        String fileName = URLEncoder.encode("委托书", "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
        //获取模板
        //获取模板
        String FileName = "委托书.docx";
        // 获取到文件服务器上面的文件
        String path = applicationProperties.getDomain() + "/" + MinioBucketsEnum.SYSTEM.getBucketName() + "/" + SecurityUtils.getGroupId() + "/" + FileName;
       //临时文件存放路径
        String filePath = System.getProperty("user.dir") + File.separator + SecurityUtils.getUserId() + "temp.docx";
        //获取到http的文件流
        URL url = new URL(path);
        Resource resource = new UrlResource(url);
        try (InputStream inputStream = resource.getInputStream();
             OutputStream outputStream = response.getOutputStream()) {
            //定义两个list数据
            HackLoopTableRenderPolicy policy = new HackLoopTableRenderPolicy();
            Configure config = Configure.newBuilder().bind("itemList", policy)
                    .bind("goodsList",policy)
                    .build();
            //获取本次模板需要用到的数据信息
            MtOrderExportInfo exportInfo = mtOrderTimeService.selectExportInfo(id, createTime, SecurityUtils.getGroupId(), SecurityUtils.getUserId());
            //将数据信息的实体类转成map数据
            Map<String, Object> map = convert(exportInfo);
            //获取施工项目list
            List<MtOrderItemTimeExport> itemInfoList = exportInfo.getItemInfoList();
            List<Map<String, Object>> itemList= Lists.newArrayList();
            if(CollectionUtils.isNotEmpty(itemInfoList)){
                itemInfoList.forEach(u->{
                    Map<String, Object> dataMap = convert(u);
                    itemList.add(dataMap);
                });
            }
            //获取商品项目list
            List<MtOrderItemTimeExport> goodsItemInfoList = exportInfo.getGoodsItemInfoList();
            List<Map<String, Object>> goodsList= Lists.newArrayList();
            if(CollectionUtils.isNotEmpty(goodsItemInfoList)){
                goodsItemInfoList.forEach(u->{
                    Map<String, Object> dataMap = convert(u);
                    goodsList.add(dataMap);
                });
            }
            //将两个list 传入数据集合中
            map.put("itemList",itemList);
            map.put("goodsList",goodsList);
            //处理模板
            XWPFTemplate template = XWPFTemplate.compile(inputStream,config)
                    .render(map);
            //写入到临时文件中
            template.writeToFile(filePath);
            template.close();

            // 创建一个新的Word文档
            Document document = new Document();
            //加载要处理的文件
            document.loadFromFile(filePath);
            //将其转换成pdf流 并传给接口中
            document.saveToStream(outputStream,FileFormat.PDF);
            //临时文件需要删除掉
            File file = new File(filePath);
            if (file.exists()) {
                file.delete();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //实体类转map
    public static Map<String, Object> convert(Object entity) {
        Map<String, Object> map = new HashMap<>();
        Field[] fields = entity.getClass().getDeclaredFields();

        for (Field field : fields) {
            field.setAccessible(true);
            try {
                map.put(field.getName(), field.get(entity));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

        return map;
    }
}

模板如图:


image.png

最终效果:


image.png
最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,100评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,308评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,718评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,275评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,376评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,454评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,464评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,248评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,686评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,974评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,150评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,817评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,484评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,140评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,374评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,012评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,041评论 2 351

推荐阅读更多精彩内容