Freemarker操作word文档

一、文章介绍

需求:

        ①某单位有多个部门,要求导出一个word文档,每个部门生成如下申报表格

        ②干工程的一个小伙伴因为换领导,要把40个月*30天的日报的内容从旧模板更换成新的模板

两个的基本思路一样,获得数据,塞到模板里,都可以使用使用freemarker,在这里记录一下主要内容。

 

二、操作步骤

1.引入maven依赖

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

2.编写工具类

其中

exportMillCertificateWord是以浏览器下载的方式生成文件

exportSaveMillCertificateWord是导出到本地的方式生成文件

public class FreeMarkerWord {
    private static Configuration configuration = null;  
    //获取模板文件的服务器位置  
    //private static final String templateFolder = WordUtils.class.getClassLoader().getResource("../../").getPath() + "WEB-INF/templetes/";  
    private static final String templateFolder = "H:/model";  
    static {  
        configuration = new Configuration();  
        configuration.setDefaultEncoding("utf-8");  
        try {  
        	//根据位置加载模板
            configuration.setDirectoryForTemplateLoading(new File(templateFolder));  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
    }  
    private FreeMarkerWord() {  
        throw new AssertionError();  
    }  
    //浏览器下载方式 map:数据;title:生成的文件名称;ftlFile:模板文件名称;
    public static void exportMillCertificateWord(HttpServletRequest request, HttpServletResponse response, Map map,String title,String ftlFile) throws IOException {  
        //根据文件名在模板文件夹路径中找到模板
    	Template freemarkerTemplate = configuration.getTemplate(ftlFile);  
        File file = null;  
        InputStream fin = null;  
        ServletOutputStream out = null;  
        try {  
            // 调用工具类的createDoc方法生成Word文档
            file = createDoc(map,freemarkerTemplate);  
            fin = new FileInputStream(file);  
  
            response.setCharacterEncoding("utf-8");  
            response.setContentType("application/msword");  
            String fileName = title+MyUtils.getTimestampNow() + ".doc";
            //Content-Disposition 设置浏览器以下载的方式处理该文件名  
            response.setHeader("Content-Disposition", "attachment;filename="  
                    .concat(String.valueOf(URLEncoder.encode(fileName, "UTF-8"))));
  
            out = response.getOutputStream();  
            byte[] buffer = new byte[512];// 缓冲区
            int bytesToRead = -1;  
            // 通过循环将读入的Word文件的内容输出到浏览器中  
            while((bytesToRead = fin.read(buffer)) != -1) {  
                out.write(buffer, 0, bytesToRead);  
            }  
        } finally {  
            if(fin != null) fin.close();  
            if(out != null) out.close();  
            if(file != null) file.delete(); // 删除临时文件  
        }  
    }
    //导出导本地方式
    public static void exportSaveMillCertificateWord(Map map,String title,String ftlFile) throws IOException {  
        //根据文件名在模板文件夹路径中找到模板
    	Template freemarkerTemplate = configuration.getTemplate(ftlFile);  
        File file = null;  
        InputStream fin = null;  
		FileOutputStream fos = null;
		//导出的文件.doc
		String newTitle = ("H:/newFile/"+title).substring(0,("H:/newFile/"+title).length()-1);
        try {  
            //根据模板生成临时文件
            file = createDoc(map,freemarkerTemplate);
            //读取生成的文件
            fin = new FileInputStream(file);
            int tempByte;
			byte[] buffer = new byte[512];//缓冲区
			
			fos = new FileOutputStream(newTitle);
			
			//将读取的内容放到缓冲区并且写到新文件中去
			while((tempByte = fin.read(buffer))!=-1){
				fos.write(buffer,0,tempByte);//数组,开始位置,长度
			}
        } finally {  
            if(fin != null) fin.close();  
            if(fos != null) fos.close();  
            if(file != null) file.delete(); // 删除临时文件  
        }  
    } 
    //创建word文档
    private static File createDoc(Map<?, ?> dataMap, Template template) {  
        String name =  "sellPlan.doc";  
        File f = new File(name);  
        Template t = template;  
        try {  
            // 这个地方不能使用FileWriter因为需要指定编码类型否则生成的Word文档会因为有无法识别的编码而无法打开  
            Writer w = new OutputStreamWriter(new FileOutputStream(f), "utf-8");  
            t.process(dataMap, w);  
            w.close();  
        } catch (Exception ex) {  
            ex.printStackTrace();  
            throw new RuntimeException(ex);  
        }  
        return f;  
    }  
}

3.获取需要写入的内容

通过查询数据库方式获取数据,简单写一下map的拼装,在模板中key匹配的位置,塞入对应的value值,循环的结构用list,在word文档中循环输出。

//业务要求:一个word文件中,每个部门为单独的【标题】+【table】
List<HashMap> resultList =  new ArrayList<HashMap>();
for(循环其中一个table的所有行){
    //行的其他的数据(sql查询封装好的,否则跟下面的目标名称一样,put每个字段)
    resultOneOne.put("goalList", goalList);
    //行的【目标名称】
	resultOneOne.put("targetName", map2.get("name"));
	targetList.add(resultOneOne);
}
//每个部门的标题
resultOne.put("title", year + "年度" +map.get("text")+ "单位业务职能目标完成情况申报表");
//每个部门的table
resultOne.put("targetList", targetList);
//所有部门的集合
resultList.add(resultOne);
	

通过读取word文档的方式获取数据,封装到list中,最后循环list生成list.size()个文件。


File dir = new File("H:/oldFile");
        File[] files = dir.listFiles();
        List<String> filNameList = new ArrayList();
        if (files != null) {
            for (int i = 0; i < files.length; i++) {
                String fileName = files[i].getName();
                filNameList.add(fileName);
            }

        }
        List<HashMap> contentMapList = new ArrayList<HashMap>();
        List<String> troubleList = new ArrayList();
		for(int i = 0; i < filNameList.size(); i++){
			String fileName = filNameList.get(i);
            //读取旧版文档
			String path = "H:/oldFile/"+fileName;
			String buffer = "";
			HashMap resultOne = new HashMap();
			HashMap map = new HashMap();
			try {
				if (path.endsWith("docx")) {
					OPCPackage opcPackage = POIXMLDocument.openPackage(path);
					POIXMLTextExtractor extractor = new XWPFWordExtractor(opcPackage);
                    //获取到所有内容(没有格式)				
                    buffer = extractor.getText();
                    //处理内容并封装成map
                    String month = buffer.substring(buffer.indexOf("年")+1,buffer.indexOf("月"));
				    map.put("month", month);

					extractor.close();
				} else {
					System.out.println("此文件不是word文件!");
				}
            } catch (Exception e) {
			    //记录转换错误的文件 System.out.println("=================================================="+fileName);
				troubleList.add(fileName);
				e.printStackTrace();
			}

4.整理模板文件

在word中整理好格式,另存为xml格式,并保存到代码中对应的路径下,使用foxe_CHS打开并按F8格式化,对需要插入数据的内容,左侧Wbody下面是主要内容标签,右侧${}内是map的key,输出时显示的是value的内容,<#list>循环的标签,类似于for循环,例子中第一层循环标识:每个部门循环生成【标题】+【table】,第二层循环表示每个【table】中循环的条目;_index类似于for循环中的下表,<w:vMerge/>表示合并单元格等等

5.运行程序,完成功能

调用工具类的方法,FreeMarkerWord.exportMillCertificateWord(request,response,map,title,modelFile);   

map:为上面拼装的数据;title:生成的目标文件名称;ftlFile:模板文件名称;

三、注意事项

在实际开发中遇到的几个小问题

1、数据中包含<>标签,会解析错误,插入<#escape x as x?html> 使其作为文本输出,可以将特殊字符转移。大小比较符号使用lt、lte、gt和gte来替代<、<=、>和>= 也可以使用括号<#if (x>y)> 可以用于比较数字和日期 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值