前言
最近遇到项目需求需要将数据库中的部分数据导出到 word 中,具体是在一个新闻列表中将选中的新闻导出到一个 word 中。参考了网上一些教程,实现了该功能,在此记录下来。
导出结果如下:
图中为导出的其中两条新闻。
搜索网上导出 word 的方式有很多种,但是很多都是一笔带过,有示例代码的只找到了 POI 导出,和通过 FreeMarker 方式导出,但是只是具有参考意义。本文采取使用 FreeMark 方式。
实现步骤
- Maven 工程引入FreeMarker 的依赖,非 Maven 工程添加 jar 包
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.26-incubating</version>
</dependency>
-
创建 word 模板
-
新建 word 替换内容为占位符
-
另存模板为 XML 文件
-
使用 NotePad++ 打开 xml 文件
-
选中全部内容,到这里进行格式化
-
将原内容替换为格式化后的内容
-
因为我的 word 的内容是一个列表,所以需要添加一个 freemarer 标签标识
-
找到
<w:document>
元素下面的<w:body>
元素,添加<#list newsList news>
, 并在</w:body>
结束标签之前闭合</#list>
, 此处的 newsList 为后台读取模板时需要需要渲染数据map集合的key, 其所对应的是一个list集合。
-
保存为 FreeMarker 的模板文件,后缀为 ftl 格式,拷贝到项目中
-
-
编写代码
- 从数据中查询数据集合放入Map中, 调用工具方法,返回流
Map<String, Object> root = new HashMap<String, Object>(); root.put("newsList", newsList);//newsList为新闻对象集合 String template = "/temp.ftl"; //模板文件的地址 ByteArrayOutputStream outputStream = WordUtil.process(root, template); return outputStream;
- 调用下载工具类进行下载即可。
DownloadUtil.download(byteArrayOutputStream, response, returnname);
注:在实现功能的时候,由于采取的是 ajax 请求方式,导致只是将流写入 Response 时, Response 为 xml 格式的数据。但是想要实现的效果是弹出下载框,下载 word 文档。最后查询资料,修改ajax请求为form表单提交方式(ajax form),才弹出下载框实现了功能。
文章涉及工具类
//WordUtil.java
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Map;
import freemarker.template.Configuration;
import freemarker.template.Template;
public final class WordUtil {
private static Configuration configuration = null;
private WordUtil() {
throw new AssertionError();
}
/**
* 根据模板生成相应的文件
* @param root 保存数据的map
* @param template 模板文件的地址
* @param path 生成的word文档输出地址
* @return
*/
public static synchronized ByteArrayOutputStream process(Map<?, ?> root, String template) {
if (null == root ) {
throw new RuntimeException("数据不能为空");
}
if (null == template) {
throw new RuntimeException("模板文件不能为空");
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
String templatePath = template.substring(0, template.lastIndexOf("/"));
String templateName = template.substring(template.lastIndexOf("/") + 1, template.length());
if (null == configuration) {
configuration = new Configuration(Configuration.VERSION_2_3_23); // 这里Configurantion对象不能有两个,否则多线程访问会报错
configuration.setDefaultEncoding("utf-8");
configuration.setClassicCompatible(true);
}
configuration.setClassForTemplateLoading(WordUtil.class, templatePath);
Template t = null;
try {
t = configuration.getTemplate(templateName);
Writer w = new BufferedWriter(new OutputStreamWriter(outputStream, "utf-8"));
t.process(root, w); // 这里w是一个输出地址,可以输出到任何位置,如控制台,网页等
w.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
return outputStream;
}
}
//DownloadUtil.java
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.FileUtils;
public class DownloadUtil {
/**
* @param byteArrayOutputStream 将文件内容写入ByteArrayOutputStream
* @param response HttpServletResponse 写入response
* @param returnName 返回的文件名
*/
public static void download(ByteArrayOutputStream byteArrayOutputStream, HttpServletResponse response, String returnName) throws IOException{
response.setContentType("application/msword");
response.setHeader("Content-Disposition", "attachment; filename=" + returnName);
response.setContentLength(byteArrayOutputStream.size());
OutputStream outputstream = response.getOutputStream(); //取得输出流
byteArrayOutputStream.writeTo(outputstream); //写到输出流
byteArrayOutputStream.close(); //关闭
outputstream.flush(); //刷数据
}
}