简介
本文为springboot使用freemarker技术导出word文档的过程记录。
内容
1、代码部分
springboot项目创建
<u>开发工具:idea</u>
<u>java版本:java8</u>
<u>springboot版本:2.1.6.RELEASE</u>
创建一个maven项目,项目名称自定义,例如:ssqxx-word
创建完父类项目之后,删除生成的java文件夹和resources文件夹,新建一个子模块,模块命名自定义,例如:test-export-word-manage
springboot导包
父类pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>ssqxx-word</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<description>导出word测试项目</description>
<!-- 模块 -->
<modules>
<module>test-export-word-manage</module>
</modules>
<!-- 属性定义和公共版本定义 -->
<properties>
<springboot.version>2.1.6.RELEASE</springboot.version>
</properties>
<!-- 管理jar包版本,Maven会沿着父子层次向上走,直到找到一个拥有dependencyManagement元素的项目,然后它就会使用在这个dependencyManagement元素中指定的版本号 -->
<dependencyManagement>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${springboot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
模块pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ssqxx-word</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>test-export-word-manage</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- springboot web 包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- springboot freemarker 包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- framework 包-->
<dependency>
<groupId>org.apache.avalon.framework</groupId>
<artifactId>avalon-framework-api</artifactId>
<version>4.3.1</version>
</dependency>
</dependencies>
</project>
核心导出代码
创建一个word导出工具类,导出word的所有步骤代码都放在这个类里
WordUtil.java
package com.ssqxx.util;
import freemarker.template.Configuration;
import freemarker.template.Template;
import sun.misc.BASE64Encoder;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
/**
* @ClassName: WordUtil
* @Description: word导出工具类
* @author ssqxx
* @version 1.0
* @date 2020-11-19
*/
public class WordUtil {
private static Configuration configuration = null;
//classLoader.getResource()只能获取相对路径的资源
// private static final String templateFolder = WordUtils.class.getClassLoader().getResource("template").getPath();
//class.getResource()可以获取绝对路径和相对路径
private static final String templateFolder = com.ssqxx.util.WordUtil.class.getResource("/templates").getPath();
static {
configuration = new Configuration();
configuration.setDefaultEncoding("utf-8");
try {
configuration.setDirectoryForTemplateLoading(new File(templateFolder));
} catch (IOException e) {
e.printStackTrace();
}
}
private WordUtil() {
throw new AssertionError();
}
/**
* 导出word文件到浏览器
* @param request
* @param response
* @param map 内容
* @param title 标题
* @param ftlFile 模板
* @throws IOException
*/
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");
//获取指定格式时间
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// 设置浏览器以下载的方式处理该文件名
String fileName = title + sdf.format(new Date()) + ".doc";
//文件名称乱码解决
String encodedFileName = fileName +";filename*=utf-8''"+URLEncoder.encode(fileName,"UTF-8");
response.setHeader("Content-Disposition", "attachment;filename="+encodedFileName+"");
response.setContentType("application/vnd.ms-excel;charset=UTF-8");
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
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(); // 删除临时文件
}
}
/**
* 创建的doc
* @param dataMap 数据内容
* @param template 模板
* @return
*/
private static File createDoc(Map dataMap, Template template) {
String name = "ssqxx.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;
}
/**
* 本地文件图片获取base64码
* @param src
* @return
*/
public static String getImageBase(String src){
if (src == null || src == "")
return "";
File file = new File(src);
if (!file.exists())
return "";
InputStream in = null;
byte[] data = null;
try {
in = new FileInputStream(file);
data = new byte[in.available()];
in.read(data);
in.close();
} catch (IOException e) {
e.printStackTrace();
}
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(data);
}
/**
* 获取Base64码根据文件字节流
* @param inputStream
* @return
*/
public static String getImageBaseByInputStream(InputStream inputStream){
if (inputStream == null)
return "";
byte[] data = null;
try {
data = new byte[inputStream.available()];
inputStream.read(data);
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(data);
}
}
业务导出代码
controller层
package com.ssqxx.controller;
import com.ssqxx.service.ExportWordService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @ClassName: ExportWordController
* @Description: word导出Controller类
* @author ssqxx
* @version 1.0
* @date 2020-11-19
*/
@RestController
@RequestMapping("exportWord")
public class ExportWordController {
@Autowired
ExportWordService exportWordService;
/**
* 导出word方法
* @param request
* @param response
*/
@GetMapping("export")
public void exportWord(HttpServletRequest request, HttpServletResponse response){
exportWordService.exportWord(request,response);
}
}
服务层
sevice类
package com.ssqxx.service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @ClassName: ExportWordService
* @Description: word导出服务类
* @author ssqxx
* @version 1.0
* @date 2020-11-19
*/
public interface ExportWordService {
void exportWord(HttpServletRequest request, HttpServletResponse response);
}
service实现类
package com.ssqxx.service.impl;
import com.ssqxx.service.ExportWordService;
import com.ssqxx.util.WordUtil;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName: ExportWordServiceImpl
* @Description: word导出服务实现类
* @author ssqxx
* @version 1.0
* @date 2020-11-19
*/
@Service("exportWordService")
public class ExportWordServiceImpl implements ExportWordService {
@Override
public void exportWord(HttpServletRequest request, HttpServletResponse response){
//添加测试数据
//声明一个数据Map
Map map = new HashMap();
map.put("name","迈克尔");
map.put("remark","暂无");
//测试列表数据
List list = new LinkedList();
list.add("数据一");
list.add("数据二");
list.add("数据三");
map.put("list",list);
//测试map数据
Map mapT = new HashMap();
mapT.put("one","mapOne");
mapT.put("two","mapTwo");
map.put("mapT",mapT);
//添加图片,地址为图片存放地址,需改成自己的文件地址
String imageOne = WordUtil.getImageBase("C:/U***s/1****3/D*****p/微信图片_20200915162314.jpg");
map.put("imageOne", imageOne);
try {
//将数据导出
WordUtil.exportMillCertificateWord(request,response,map,"导出word测试","exportWord.ftl");
} catch (IOException e) {
e.printStackTrace();
}
}
}
模块配置文件
server:
port: 7070
spring:
application:
name: export-word-manage-service
2、模板部分
模板创建
- 使用WPS或者微软word进行模板编制
- 写好模板样式,最后在需要填充数据的地方写上标识,方便之后改成导出模板,例如:“占位”来作为标识
- 模板编制完成后,另存为,保存格式为xml格式!
- 建议插入图片占位时尽量使用较小的图片!
例如:
模板修改
项目的模块部分添加存放模板文件夹,在resources文件夹下新建templates文件夹
将生成好的xml模板,导入到项目模块中新建的templates文件夹里
修改xml模板的文件名,更改为:exportWord.ftl(名称可以自定义,在代码里对应上即可,不然会报错找不到文件!)
打开exportWord.ftl,进行格式的调整和修改(ftl用idea打开格式较乱的话,可以用Notepad++进行xml格式调整,这个操作不会的话,之后的文章会出!)
将模板中“占位”数据更改为传入map的key,例如:${name}
模板中list的数据更改,需要加上<#list 原名 as 作用名></#list>
模板中Map的数据更改,直接写map的值加上key,例如${map.key}
模板中图片打印,先找到<w:binData></w:binData>,然后将中间的Base64位字符删除,更换为自己的数据名称,注意格式,不可以换行,否则图片打印不出来。例如${image}
3、导出效果
完成以上步骤之后,就可以进行word导出测试了,我使用postman进行导出接口测试
最终的效果为
数据都已经打印出来了,图片也成功展示,word导出完成!
结论
word导出的开发过程记录是在工作中项目需要用到word导出这个功能,然后去学习和寻找方法的,其实关键的地方在于模板的编制和导出核心代码。
这就是一个简单的springboot使用freemarker进行word文件导出的开发过程记录,希望能给你们的开发提供一些些帮助!
谢谢支持!