java基于POI根据word模板生成word文档并下载

前言:项目中通常会需要导出合同,导出周报等需求等。本文是基于poi技术,实现通过填充word模板生成word文件并支持下载。

话不多说,上代码:

public class WordUtils {

    private static Logger logger = LoggerFactory.getLogger(WordUtils.class);
    
    /**
     * 根据word模板填充数据并下载
     *
     * @param templateAbsolutePath:模板文件绝对路径
     * @param data:写入模板的数据
     * @param response
     */
    public static void generateWordByTemplate(String templateAbsolutePath, Map<String, Object> data, HttpServletResponse response) {

        //
        File wordTemplate = new File(templateAbsolutePath);
        try {
            //创建临时文件
            File newFile = File.createTempFile("hwt", ".docx");
            //循环插入策略
            LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
            //策略绑定位置
            Configure build = Configure.builder().bind(policy, "contractTable", "returnMoneyTable").build();
            //XWPFTemplate读取模板文件并渲染数据
            XWPFTemplate render = XWPFTemplate.compile(wordTemplate, build).render(data);
            //数据渲染结束后,设置样式
            setTableStyle(data,render);
            //渲染完成的数据写临时文件
            render.writeToFile(newFile.getAbsolutePath());
            //文件下载
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode("周报.docx", "UTF-8"));
            FileInputStream in = new FileInputStream(newFile.getAbsolutePath());
            int len = 0;
            //创建缓冲区写入文件数据
            byte[] buffer = new byte[1024];
            ServletOutputStream out = response.getOutputStream();
            while ((len = in.read(buffer)) > 0) {
                out.write(buffer, 0, len);
            }
            //关闭io
            in.close();
            out.close();
            //删除临时文件
            newFile.delete();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

上word模板

word模板数据填充是采用的占位符方式,其中数据采用key-value接口,推荐采用HashMap。

模板中部分占位符解释说明:

  • 基本上所有的变量都由{{}}包裹,交给poi-tl解析(变量命名随便你,中文,英文都可以)
  • {{contractTable}}:下方的[departmentName]、[targetMoney]、[totalMoney]、[salary]、[finishPercent] 就是使用了 poi-tl的表格行插件,动态渲染表格行的语法,可以简单理解为此处表格对应的每一行就是一个对象实体类,而{{contractTable}}就是一个list,则Map中对应的数据为:key:contractTbale,    value:list;其中list列表的对象中字段名需要和  [] 里一一对应
  • {{?importantProjects}} {{item}} {{/importantProjects}},此处则是使用了poi-tl的区块对标签。区块对在此处的,我只是用它来实现渲染同一个表格内的多行文本渲染,可以理解为{{?importantProjects}} {{item}} {{/importantProjects}}对应的就是一个list,< item >就是一个list中的一个个对象元素。并且此处会触发,换行后序号累加的word编排编号样式。数据格式为:key:importantPeojects;   value:List<Map>,其中list中的map的key为item,需要与区块对标签中占位符对应
  • {{@***}},此处需要注意,word模板中代表图片的变量,必须要加上@特殊符号修饰!

来,展示!

【补充-扩展】

上面展示的是最基本的word动态渲染过程。最后由于近期需求的调整,需要对word中的动态表格设置样式,查询了很多资料,网上对于word中的动态表格Table样式的修改案例较少,特此记录一下。

    /**
     * 设置周报word中table的样式
     * 注意:该方法和word模板及数据强依赖,没有普适性!!!
     * word中表格的单元格样式可通过XWPFRun来设置

     * 本段代码只可参考其中的部分类、方法的使用,没有普适性
     * @param data
     * @param render
     */
    private static void setTableStyle(Map<String, Object> data, XWPFTemplate render) {
        BigDecimal timePercent = (BigDecimal) data.get("timePercent");
        List<XWPFTable> tables = render.getXWPFDocument().getTables();
        for (XWPFTable table : tables) {
            List<XWPFTableRow> rows = table.getRows();
            if (CollectionUtils.isNotEmpty(rows)) {
                for (int i = 2; i < rows.size(); i++) {//前两行为吧表头,过滤掉前两行
                    List<XWPFTableCell> rowCells = rows.get(i).getTableCells();
                    if (i == 2 || i == 5 || i == 8) {
                        for (XWPFTableCell cell : rowCells) {
                            cell.getParagraphArray(0).getRuns().get(0).setBold(true);//单元格字体加粗
                            if (i == 2 || i == 5) {
                                cell.getCTTc().getTcPr().addNewTcBorders().addNewBottom().setVal(STBorder.NIL);
                            }
                        }
                    } else if (i == 3 || i == 4 || i == 6 || i == 7) {
                        for (XWPFTableCell cell : rowCells) {
                            XWPFRun run = cell.getParagraphArray(0).getRuns().get(0);
                            run.setItalic(true);//单元格字体倾斜
                            run.setFontFamily("楷体");//字体格式设置为楷体,默认只对ASCII(0-127)码有效,特殊的比如中文需要指定范围FontCharRange,可参考下面的代码
                            if (i == 3 || i == 6) {
                                cell.getCTTc().getTcPr().addNewTcBorders().addNewBottom().setVal(STBorder.NIL);//单元格底部边框隐藏
                                cell.getCTTc().getTcPr().addNewTcBorders().addNewTop().setVal(STBorder.NIL);//单元格顶部边框隐藏
                            }
                            if (i == 4 || i == 7) {
                                cell.getCTTc().getTcPr().addNewTcBorders().addNewTop().setVal(STBorder.NIL);
                            }
                        }
                        rowCells.get(0).getParagraphArray(0).getRuns().get(0).setFontFamily("楷体", XWPFRun.FontCharRange.eastAsia);//第一列是中文 ,需要使用eastAsia才能生效;但是eastAsia对是ascii(0-127)又无效,所以需要单独设置
                    } else {
                        if ((new BigDecimal(rowCells.get(rowCells.size() - 1).getText().split("%")[0])).compareTo(timePercent) < 0) {
                            rowCells.get(rowCells.size() - 1).getParagraphArray(0).getRuns().get(0).setBold(true);
                            rowCells.get(rowCells.size() - 1).getParagraphArray(0).getRuns().get(0).setColor("FF0000");//FF0000是红色的rgb16进制数值,rgb16进制数值可以通过https://www.rgbku.com/zhuanhuanqi.html计算
                        }
                    }
                }
            }
        }
    }

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr_hwt_123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值