freemarker生成word

之前写过freemarker生成pdf的文章,这次应需求的不同,来使用freemarker生成word文档,与生成pdf不同的是

生成PDF:生成pdf使用itextpdf配合freemarker生成的,原理是相当于把我们写好的html模板,原封不动的印到了上面

生成WORD:生成word更像是直接将文件的后缀换成了doc或docx,这就有一个问题,并不是所有的htem标签和css样式word都能兼容的,所以有些时候会发现html预览的样式和导出的文件并不是一样的比如我下面的例子

freemarker版本

        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.28</version>
        </dependency>

先上java代码,工具类

默认生成的是docx文件,读取的则是ftl(原因是ftl文件freemarker官方指定文件类型,在IDEA里面写相关代码会有高亮提示,使用html问题也不大)

@Slf4j
public class WordUtils {

    /**
     * 生成 word 文档方法
     *
     * @param data         要填充的数据
     * @param templateName 模版名称(不需要代后缀)
     * @param fileName     要输出的文件名称(不需要代后缀)
     */
    public static void generateWord(Object data, String templateName, String fileName) {
        // 设置FreeMarker的版本和编码格式日期格式
        Configuration configuration = new Configuration(new Version("2.3.28"));
        configuration.setDefaultEncoding("UTF-8");
        configuration.setDateFormat("yyyy-MM-dd");
        // 此处把模版文件都放在 resources 下的 templates 中
        configuration.setClassForTemplateLoading(WordUtils.class, "/templates/");
        // 创建一个Word文档的输出流
        try (ServletOutputStream os = getHttpServletResponse(fileName).getOutputStream();
             OutputStreamWriter osw = new OutputStreamWriter(os, StandardCharsets.UTF_8);
             Writer bw = new BufferedWriter(osw)) {
            // 设置FreeMarker生成Word文档所需要的模板
            Template tem = configuration.getTemplate(templateName + ".ftl", "UTF-8");
            // FreeMarker使用Word模板和数据生成Word文档
            tem.process(data, bw);
        } catch (Exception e) {
            log.error("导出" + fileName + "失败! 【{}】:", e.getMessage());
            throw new RuntimeException("导出" + fileName + "失败!");
        }
    }


    /**
     * 设置响应头信息
     *
     * @param fileName 输出文件名称
     */
    private static HttpServletResponse getHttpServletResponse(String fileName) throws Exception {
        ServletRequestAttributes servletRequestAttributes =  (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = servletRequestAttributes.getResponse();
        response.setContentType("application/octet-stream");
        response.setHeader("content-type", "application/octet-stream");
        response.setContentType("application/json;charset=utf-8");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码
        fileName = URLEncoder.encode(fileName + ".docx", "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName);
        return response;
    }
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <style>
        @page {

            @bottom-left {
                content: element(footer);
                vertical-align: top;
                padding-top: 10px;
            }

            @top-right {
                content: element(header);
                vertical-align: bottom;
                padding-bottom: 10px;
            }

            /*页面布局*/
            margin-top: 1.5cm;
            margin-left: 1.5cm;
            margin-right: 1.5cm;
            margin-bottom: 1.5cm;
            size: A4 portrait;
        }

        /*默认字体*/
        body {
            font-family: 宋体;
            font-size: 10.5pt;
        }

        div.header {
            position: running(header);
            font-size: 14px;
        }

        div.footer {
            display: block;
            margin-top: 0.5cm;
            position: running(footer);
        }

        #pagenumber:before {
            content: counter(page);
        }

        #pagecount:before {
            content: counter(pages);
        }

        div.header {
            position: running(header);
            font-size: 14px;
        }

        div.footer {
            display: block;
            margin-top: 0.5cm;
            position: running(footer);
        }

        .three {
            font-family: 宋体;
            font-size: 16pt;
        }

        .threeb {
            font-family: 宋体;
            font-size: 16pt;
            font-weight: bold;
        }

        /*换页(在这一行之前分页 / 之后分页是page-break-after: always)*/
        .next-page {
            page-break-before: always
        }

        /*宋体四号字*/
        .four {
            font-family: 宋体;
            font-size: 14pt;
        }

        /*宋体四号加粗字*/
        .fourb {
            font-family: 宋体;
            font-size: 14pt;
            font-weight: bold;
        }

        .five {
            font-family: 宋体;
            font-size: 10.5pt;
        }

        .fiveb {
            font-family: 宋体;
            font-size: 10.5pt;
            font-weight: bold;
        }

        /*分页*/
        .next-page {
            page-break-before: always
        }

        /*左对齐*/
        .right {
            text-align: right;
        }

        /*右对齐*/
        .left {
            text-align: left;
        }

        /*首行缩进*/
        .indent {
            text-indent: 2em;
        }

        /*居中*/
        .center {
            text-align: center;
        }

        /*默认1.5倍行高*/
        p {
            line-height: 150%;
        }

        table {
            width: 100%;
            border-collapse: collapse;
            margin-left: 0pt;
            border: solid;
        }

        th {
            text-align: center;
            border: solid windowtext 1.0pt;
            height: 26pt;

        }

        td {
            text-align: center;
            border: solid windowtext 1.0pt;
            height: 26pt;

        }

        hr {
            background-color: #000000;
            border: dashed #000000 0.5px;
            height: 1px;
        }
    </style>
</head>
<body>
<page>
    <p><span class="threeb">领导干部个人基本信息</span></p>

    <div style="width: 50%;float: left" disabled="inline-block">
        <p class="indent"><span class="fiveb">姓   名 :</span></p>
        <p class="indent"><span class="fiveb">民   族 :</span></p>
        <p class="indent"><span class="fiveb">工作单位 :</span></p>
        <p class="indent"><span class="fiveb">现任职务 :</span></p>
        <p class="indent"><span class="fiveb">户籍地址 :</span></p>
    </div>
    <div disabled="inline-block">
        <p class="indent"><span class="fiveb">性   别 :</span></p>
        <p class="indent"><span class="fiveb">政治面貌 :</span></p>
        <p class="indent"><span class="fiveb">现任部门 :</span></p>
        <p class="indent"><span class="fiveb">身份证号码 :</span></p>
        <p class="indent"><span class="fiveb">常住地址 :</span></p>
    </div>
</page>
</body>
</html>

html预览样式

 我们期望导出后也是这种样式

 实际导出后的样式

可以发现分栏信息都跑到了下面,这个问题我尝试了N多种css样式,始终是无法实现,

有一种可以完美导出的方法,就是先写好word模板.然后把word模板转换成xml文件,在把xml文件的后缀改成ftl,其实原理也就是freemarker拼好参数后,改成了word后缀文件,但xml文件可读性太差,个人不推荐,如果数据量小还可以,尤其是涉及到循环展示项,就更需要慎用了,以上的问题,最终我选择把分栏信息做成了表格的样式,算是另辟蹊径了吧,如果有什么更好的实现方法,请告知

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值