java实现按模板导出word文档-freemarker

需求:
根据指定的word模板,用代码生成word文件,数据为业务数据

方案:
引用freemarker工具包

步骤:

  1. 引入freemarker maven依赖 或者直接导入jar包
<dependency>
     <groupId>freemarker</groupId>
     <artifactId>freemarker</artifactId>
     <version>2.3.8</version>
</dependency>
  1. 编写word模板,编写完后缀是.docx,另存为xml后缀文件,修改文件内容
    ${XXX} ,调整完修改后缀为.ftl
    几个注意点:
    (1)生成word文档时,需注意ftl模板中替换字段格式错误,el表达式正确格式:${field},中间不能有标签隔开
    (2)若为表格输出,list循环,则在列名之后的数据行进行循环<#list myListData as tmp>...</#list>,如
<#list myListData as tmp>
	<w:tr wsp:rsidR="008F01FC" wsp:rsidRPr="008F01FC" wsp:rsidTr="008F01FC">
		<w:tc>
			<w:tcPr>
				<w:tcW w:w="2074" w:type="dxa"/>
				<w:shd w:val="clear" w:color="auto" w:fill="auto"/>
			</w:tcPr>
			<w:p wsp:rsidR="001D38F7" wsp:rsidRPr="008F01FC" wsp:rsidRDefault="001D38F7">
				<w:pPr>
					<w:rPr>
						<w:rFonts w:hint="fareast"/>
					</w:rPr>
				</w:pPr>
				<w:r wsp:rsidRPr="008F01FC">
					<w:rPr>
						<w:rFonts w:hint="fareast"/>
					</w:rPr>
					<w:t>${tmp.name}</w:t>
				</w:r>
			</w:p>
		</w:tc>
		<w:tc>
			<w:tcPr>
				<w:tcW w:w="2074" w:type="dxa"/>
				<w:shd w:val="clear" w:color="auto" w:fill="auto"/>
			</w:tcPr>
			<w:p wsp:rsidR="001D38F7" wsp:rsidRPr="008F01FC" wsp:rsidRDefault="001D38F7">
				<w:pPr>
					<w:rPr>
						<w:rFonts w:hint="fareast"/>
					</w:rPr>
				</w:pPr>
				<w:r wsp:rsidRPr="008F01FC">
					<w:rPr>
						<w:rFonts w:hint="fareast"/>
					</w:rPr>
					<w:t>${tmp.tel}</w:t>
				</w:r>
			</w:p>
		</w:tc>
	</w:tr>
</#list>

我的模板样例如下:
在这里插入图片描述
3. 工具类如下:

import freemarker.template.Configuration;
import freemarker.template.Template;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;

public class TestJava2Word2 {

    public static void main(String[] args) throws IOException {
        Map<String,Object> dataMap = new HashMap<String, Object>();
        try {
            /*===== 1.组装数据 dataMap =====*/
            //插入文本示例
            dataMap.put("username","张三");
            dataMap.put("address","深圳市南山区西丽镇");
            dataMap.put("num", Math.floor(Math.random()*10));
            SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日");
            dataMap.put("date",sdf.format(new Date()));
            dataMap.put("content","华语天王周杰伦在北京召开周杰伦战略发布会。在发布会上,推出时尚与专业结合的:引起了广泛的关注和讨论。现在终于正式与大家见面,天猫、官网同时上线,首卖期间还有好礼相赠!买就送,就是这么任性!");
            List<Map<String, Object>> newsList=new ArrayList<Map<String,Object>>();
            //插入表格示例
            for(int i=1;i<=10;i++){
                Map<String, Object> map=new HashMap<String, Object>();
                map.put("name", "王"+i);
                map.put("tel", "137"+i);
                map.put("age", "年龄"+i);
                map.put("address", System.currentTimeMillis());
                newsList.add(map);
            }
            dataMap.put("myListData",newsList);
            //插入图片示例
            String myPic1 = "";
            String myPic2 = "";
            try {
                myPic1 = WordUtil.getImageString("D:\\testword\\11.png");
                myPic2 = WordUtil.getImageString("D:\\testword\\11.png");
            } catch (IOException e) {
                e.printStackTrace();
            }
            dataMap.put("myPic1", myPic1);
            dataMap.put("myPic2", myPic2);
//            System.out.println("******"+dataMap.toString());

            /*===== 2.Configuration 用于读取ftl文件 =====*/
            Configuration configuration = new Configuration();//创建配置实例
            configuration.setDefaultEncoding("utf-8");
            //以下是两种指定ftl文件所在目录路径的方式,注意这两种方式都是:指定ftl文件所在目录的路径,而不是ftl文件的路径
            //指定路径的第一种方式(根据某个类的相对路径,例:/com/wordtemplate)
            // configuration.setClassForTemplateLoading(TestJava2Word2.class, "wordtemplate");
            //指定路径的第二种方式,配置文件在系统中的绝对路径,示例路径是D:/testword/testTemplate.ftl
            configuration.setDirectoryForTemplateLoading(new File("D:\\testword\\"));

            /*===== 3.输出文档路径及名称 =====*/
            File outFile = new File("D:\\testword\\测试报告导出.doc");
            // 如果输出目标文件夹不存在,则创建
            if (!outFile.getParentFile().exists()) {
                outFile.getParentFile().mkdirs();
            }
            if(outFile .exists()) {
                outFile = new File("D:\\testword\\测试报告导出("+1+").doc");
            }

            /*===== 4.以utf-8的编码读取ftl文件 =====*/
            Template template = configuration.getTemplate("testTemplate2.ftl", "utf-8");
            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "utf-8"), 10240);
            template.process(dataMap, out);
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

其中,生成文件的那一段写的不够好,后面又进行了优化,实现如下功能:若目录文件存在,文件名后缀加(1)、(2)依此类推,将在下一篇博客中分享,欢迎阅读。

这里需要阐述的一点是:
configuration.setClassForTemplateLoading()的使用。使用中这是个坑,需要深入理解,尤其是那个基于class类路径的方法,需要理解实际指定的路径位置,不然会导致看着磁盘上有文件但项目就是报错找不到文件,然后一脸懵,实际是路径不一致啦~~
Freemarker提供了3种加载模板目录的方法。它使用Configuration类加载模板,共有3种使用方法:

方 法说 明
public void setClassForTemplateLoading(Class clazz, String pathPrefix);第一种基于当前class路径加载,需要注意的是,路径是编译以后的class的路径。例如:configuration.setClassForTemplateLoading(TestJava2Word2.class, “wordtemplate”); 实际指定的路径是:TestJava2Word2.class类的包路径 + wordtemplate,可以用System.out.println("******"+TestJava2Word2.class.getResource("").getPath());验证路径。
public void setDirectoryForTemplateLoading(File dir) throws IOException;第二种基于文件绝对路径加载。 比如加载/home/user/template下的模板文件。
public void setServletContextForTemplateLoading(Object servletContext, String path);第三种基于web项目服务器路径加载。 第二个参数是基于WebRoot下的。比如: setServletContextForTemplateLoading(context, “/ftl”) 就是 /WebRoot/ftl目录。

(建议方案:在服务器上建个目录专门放模板,然后模板加载的路径指定到该目录下。)

图片转换字节代码 WordUtil.java:

import sun.misc.BASE64Encoder;
......
 /**
  * 将图片转换为BASE64为字符串
  * @param filename
  * @return
  * @throws IOException
  */
 public static String getImageString(String filename) throws IOException {
     InputStream in = null;
     byte[] data = null;
     try {
         in = new FileInputStream(filename);
         data = new byte[in.available()];
         in.read(data);
         in.close();
     } catch (IOException e) {
         throw e;
     } finally {
         if(in != null) in.close();
     }
     BASE64Encoder encoder = new BASE64Encoder();
     return data != null ? encoder.encode(data) : "";
 }
  1. 输出文件样例:
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值