freemarker导出docx文档(纯文本,使用浏览器输出)

制作模板及代码参考:https://blog.csdn.net/u014231523/article/details/88933319

          https://blog.csdn.net/action5362603/article/details/84546012

新建符合需要的docx文档,后缀名改为zip,解压之后使用占位符更改word文件夹下的document.xml文件中对应内容,参考如下:

<#list titleList as title>
</#list>
为集合循环取值
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" xmlns:wpsCustomData="http://www.wps.cn/officeDocument/2013/wpsCustomData" mc:Ignorable="w14 w15 wp14">
<w:body>
<w:p>
<w:pPr><w:keepLines w:val="0"/><w:spacing w:after="400"/><w:ind w:firstLine="160"/><w:jc w:val="center"/></w:pPr><w:r><w:rPr><w:b/><w:sz w:val="32"/></w:rPr><w:t>${theme}</w:t></w:r>
</w:p>
<w:p><w:pPr><w:rPr><w:b w:val="0"/><w:color w:val="0066FF"/><w:sz w:val="24"/></w:rPr></w:pPr></w:p>
<#list titleList as title>
<w:p><w:pPr><w:rPr><w:b/><w:sz w:val="32"/></w:rPr></w:pPr>
<w:r><w:rPr><w:rFonts w:hint="eastAsia" w:eastAsia="宋体"/><w:b w:val="0"/><w:color w:val="000000"/><w:sz w:val="24"/><w:lang w:val="en-US" w:eastAsia="zh-CN"/></w:rPr><w:t>${title.content}</w:t></w:r><w:r><w:rPr><w:b w:val="0"/><w:color w:val="000000"/><w:sz w:val="24"/></w:rPr><w:t xml:space="preserve">   </w:t></w:r><w:r><w:rPr><w:b w:val="0"/><w:color w:val="0066FF"/><w:sz w:val="24"/></w:rPr><w:t>[单选题]</w:t></w:r></w:p><w:p><w:pPr><w:rPr><w:b w:val="0"/><w:color w:val="0066FF"/><w:sz w:val="24"/></w:rPr></w:pPr></w:p>

<w:tbl>
<w:tblPr><w:tblStyle w:val="2"/><w:tblW w:w="3868" w:type="pct"/><w:tblInd w:w="0" w:type="dxa"/><w:tblBorders><w:top w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:left w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:bottom w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:right w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:insideH w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:insideV w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/></w:tblBorders><w:tblLayout w:type="autofit"/><w:tblCellMar><w:top w:w="0" w:type="dxa"/><w:left w:w="108" w:type="dxa"/><w:bottom w:w="0" w:type="dxa"/><w:right w:w="108" w:type="dxa"/></w:tblCellMar></w:tblPr><w:tblGrid><w:gridCol w:w="3507"/><w:gridCol w:w="3086"/></w:tblGrid>
<w:tr>
<w:tblPrEx><w:tblBorders><w:top w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:left w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:bottom w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:right w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:insideH w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:insideV w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/></w:tblBorders><w:tblCellMar><w:top w:w="0" w:type="dxa"/><w:left w:w="108" w:type="dxa"/><w:bottom w:w="0" w:type="dxa"/><w:right w:w="108" w:type="dxa"/></w:tblCellMar></w:tblPrEx><w:trPr><w:trHeight w:val="500" w:hRule="atLeast"/></w:trPr><w:tc><w:tcPr><w:shd w:val="clear" w:color="auto" w:fill="E0E0E0"/><w:vAlign w:val="center"/></w:tcPr><w:p><w:pPr><w:jc w:val="center"/></w:pPr><w:r><w:t>选项</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:shd w:val="clear" w:color="auto" w:fill="E0E0E0"/><w:vAlign w:val="center"/></w:tcPr><w:p><w:pPr><w:jc w:val="center"/></w:pPr><w:r><w:t>比例</w:t></w:r></w:p></w:tc>
</w:tr>
<w:tr>
<w:tblPrEx><w:tblBorders><w:top w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:left w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:bottom w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:right w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:insideH w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:insideV w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/></w:tblBorders><w:tblCellMar><w:top w:w="0" w:type="dxa"/><w:left w:w="108" w:type="dxa"/><w:bottom w:w="0" w:type="dxa"/><w:right w:w="108" w:type="dxa"/></w:tblCellMar></w:tblPrEx><w:trPr><w:trHeight w:val="500" w:hRule="atLeast"/></w:trPr>
<w:tc><w:tcPr><w:shd w:val="clear" w:color="auto" w:fill="FFFFFF"/><w:vAlign w:val="center"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/><w:rPr><w:rFonts w:hint="default" w:eastAsia="宋体"/><w:lang w:val="en-US" w:eastAsia="zh-CN"/></w:rPr></w:pPr><w:r><w:rPr><w:rFonts w:hint="eastAsia" w:eastAsia="宋体"/><w:lang w:val="en-US" w:eastAsia="zh-CN"/></w:rPr><w:t>${title.optionsA}</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:shd w:val="clear" w:color="auto" w:fill="FFFFFF"/><w:vAlign w:val="center"/></w:tcPr><w:p><w:pPr><w:jc w:val="center"/><w:rPr><w:rFonts w:hint="default" w:eastAsia="宋体"/><w:lang w:val="en-US" w:eastAsia="zh-CN"/></w:rPr></w:pPr><w:r><w:rPr><w:rFonts w:hint="eastAsia" w:eastAsia="宋体"/><w:lang w:val="en-US" w:eastAsia="zh-CN"/></w:rPr><w:t>${title.countA}</w:t></w:r></w:p></w:tc>
</w:tr>
<w:tr>
<w:tblPrEx><w:tblBorders><w:top w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:left w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:bottom w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:right w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:insideH w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:insideV w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/></w:tblBorders><w:tblCellMar><w:top w:w="0" w:type="dxa"/><w:left w:w="108" w:type="dxa"/><w:bottom w:w="0" w:type="dxa"/><w:right w:w="108" w:type="dxa"/></w:tblCellMar></w:tblPrEx><w:trPr><w:trHeight w:val="500" w:hRule="atLeast"/></w:trPr><w:tc><w:tcPr><w:shd w:val="clear" w:color="auto" w:fill="F9F9F9"/><w:vAlign w:val="center"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:rFonts w:hint="eastAsia" w:eastAsia="宋体"/><w:lang w:val="en-US" w:eastAsia="zh-CN"/></w:rPr><w:t>${title.optionsB}</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:shd w:val="clear" w:color="auto" w:fill="F9F9F9"/><w:vAlign w:val="center"/></w:tcPr><w:p><w:pPr><w:jc w:val="center"/><w:rPr><w:rFonts w:hint="default" w:eastAsia="宋体"/><w:lang w:val="en-US" w:eastAsia="zh-CN"/></w:rPr></w:pPr><w:r><w:rPr><w:rFonts w:hint="eastAsia" w:eastAsia="宋体"/><w:lang w:val="en-US" w:eastAsia="zh-CN"/></w:rPr><w:t>${title.countB}</w:t></w:r></w:p></w:tc>
</w:tr>
<w:tr>
<w:tblPrEx><w:tblBorders><w:top w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:left w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:bottom w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:right w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:insideH w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:insideV w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/></w:tblBorders><w:tblCellMar><w:top w:w="0" w:type="dxa"/><w:left w:w="108" w:type="dxa"/><w:bottom w:w="0" w:type="dxa"/><w:right w:w="108" w:type="dxa"/></w:tblCellMar></w:tblPrEx><w:trPr><w:trHeight w:val="500" w:hRule="atLeast"/></w:trPr><w:tc><w:tcPr><w:shd w:val="clear" w:color="auto" w:fill="FFFFFF"/><w:vAlign w:val="center"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:rFonts w:hint="eastAsia" w:eastAsia="宋体"/><w:lang w:val="en-US" w:eastAsia="zh-CN"/></w:rPr><w:t>${title.optionsC}</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:shd w:val="clear" w:color="auto" w:fill="FFFFFF"/><w:vAlign w:val="center"/></w:tcPr><w:p><w:pPr><w:jc w:val="center"/></w:pPr><w:r><w:rPr><w:rFonts w:hint="eastAsia" w:eastAsia="宋体"/><w:lang w:val="en-US" w:eastAsia="zh-CN"/></w:rPr><w:t>${title.countC}</w:t></w:r></w:p></w:tc>
</w:tr>
<w:tr>
<w:tblPrEx><w:tblBorders><w:top w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:left w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:bottom w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:right w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:insideH w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/><w:insideV w:val="single" w:color="D7D7D7" w:sz="4" w:space="0"/></w:tblBorders><w:tblCellMar><w:top w:w="0" w:type="dxa"/><w:left w:w="108" w:type="dxa"/><w:bottom w:w="0" w:type="dxa"/><w:right w:w="108" w:type="dxa"/></w:tblCellMar></w:tblPrEx><w:trPr><w:trHeight w:val="500" w:hRule="atLeast"/></w:trPr><w:tc><w:tcPr><w:shd w:val="clear" w:color="auto" w:fill="E0E0E0"/><w:vAlign w:val="center"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:rFonts w:hint="eastAsia" w:eastAsia="宋体"/><w:lang w:val="en-US" w:eastAsia="zh-CN"/></w:rPr><w:t>${title.optionsD}</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:shd w:val="clear" w:color="auto" w:fill="E0E0E0"/><w:vAlign w:val="center"/></w:tcPr><w:p><w:pPr><w:jc w:val="center"/></w:pPr><w:r><w:rPr><w:rFonts w:hint="eastAsia" w:eastAsia="宋体"/><w:lang w:val="en-US" w:eastAsia="zh-CN"/></w:rPr><w:t>${title.countD}</w:t></w:r></w:p></w:tc>
</w:tr>
</w:tbl>
<w:p><w:pPr><w:rPr><w:b w:val="0"/><w:color w:val="0066FF"/><w:sz w:val="24"/></w:rPr></w:pPr></w:p>
</#list>
<w:p><w:bookmarkStart w:id="0" w:name="_GoBack"/><w:bookmarkEnd w:id="0"/></w:p><w:sectPr><w:pgSz w:w="11906" w:h="16838"/><w:pgMar w:top="1440" w:right="1800" w:bottom="1440" w:left="1800" w:header="708" w:footer="708" w:gutter="0"/><w:cols w:space="708" w:num="1"/><w:docGrid w:linePitch="360" w:charSpace="0"/></w:sectPr>
</w:body></w:document>

更改完成后将document.xml拷贝至项目resource源下的templates目录下

package com.stylefeng.guns.core.util;

import java.io.*;
import java.util.Enumeration;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

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


/**
 * Word文档工具类
 * @Author xWang
 **/
public class WordUtil {
    
    public static Configuration getConfiguration(){
        //创建配置实例
        Configuration configuration = new Configuration(Configuration.VERSION_2_3_28);
        //设置编码
        configuration.setDefaultEncoding("utf-8");
        configuration.setClassForTemplateLoading(WordUtil.class, "/templates");//换成自己对应的目录
        return configuration;
    }

    /**
     * 获取模板字符串输入流
     * @param dataMap   参数
     * @param templateName  模板名称
     * @return
     */
    public static ByteArrayInputStream getFreemarkerContentInputStream(Map dataMap, String templateName) {
        ByteArrayInputStream in = null;
        try {
            //获取模板
            Template template = getConfiguration().getTemplate(templateName);
            StringWriter writer = new StringWriter();
            //生成文件
            template.process(dataMap, writer);

            in = new ByteArrayInputStream(writer.toString().getBytes("utf-8"));//这里一定要设置utf-8编码 否则导出的word中中文会是乱码
        } catch (Exception e) {
            System.err.println("模板生成错误");
        }
        return in;
    }

    /**
     * 生成docx文档
     * @param dataMap 数据参数
     * @param outputStream 输出流
     * @param document 模板xml文件名称
     * @param templatesZipPath docx后缀名改为zip文档的路径地址(路径加文件名全路径地址)
     * @return
     * @author xWang
     */
    public static void createDocx(Map dataMap, OutputStream outputStream,String document,String templatesZipPath) {
        ZipOutputStream zipout = null;
        try {

            //内容模板
            ByteArrayInputStream documentInput = WordUtil.getFreemarkerContentInputStream(dataMap, document);
            File docxFile = new File(templatesZipPath);
            if (!docxFile.exists()) {
                docxFile.createNewFile();
            }
            ZipFile zipFile = new ZipFile(docxFile);
            Enumeration<? extends ZipEntry> zipEntrys = zipFile.entries();
            zipout = new ZipOutputStream(outputStream);
            //开始覆盖文档
            int len = -1;
            byte[] buffer = new byte[1024];
            while (zipEntrys.hasMoreElements()) {
                ZipEntry next = zipEntrys.nextElement();
                InputStream is = zipFile.getInputStream(next);
                if (next.toString().indexOf("media") < 0) {
                    zipout.putNextEntry(new ZipEntry(next.getName()));
                    if ("word/document.xml".equals(next.getName())) {//如果是word/document.xml由我们输入
                        if (documentInput != null) {
                            while ((len = documentInput.read(buffer)) != -1) {
                                zipout.write(buffer, 0, len);
                            }
                            documentInput.close();
                        }
                    } else {
                        while ((len = is.read(buffer)) != -1) {
                            zipout.write(buffer, 0, len);
                        }
                        is.close();
                    }
                }
            }

        } catch (Exception e) {
            System.err.println("导出失败:"+e.getStackTrace());

        }finally {
            if(zipout!=null){
                try {
                    zipout.close();
                } catch (IOException e) {
                    System.err.println("io异常");

                }
            }
            if(outputStream!=null){
                try {
                    outputStream.close();
                } catch (IOException e) {
                    System.err.println("io异常");
                }
            }
        }
    }


}

测试

List<Integer> list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
Map<String,Object> map = new HashMap<>();
map.put("theme","测试");
map.put("titleList",list);

String output_file_name = "test.docx";
String templatesZipPath = "D/templates/templates.zip";
 
// 清空response
response.reset();
//设置ContentType字段值
response.setContentType("application/octet-stream");
response.setCharacterEncoding("UTF-8");

try {
//解决文件名为中文时的乱码问题
String downloadFileName = URLEncoder.encode(output_file_name,"UTF-8");
// 设置response的Header
response.addHeader("Content-Disposition", "attachment;filename=" + downloadFileName);
OutputStream outputStream = response.getOutputStream();
WordUtil.createDocx(map,outputStream,"document.xml",templatesZipPath);
}catch (FileNotFoundException e){

}catch (IOException e){

}
finally {

}

需要的依赖(maven坐标)

<!--生成word文档所需-->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.28</version>
        </dependency>

        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
原文地址:https://www.cnblogs.com/xiaowangxiao/p/13073678.html