sprinboot使用freemarker导出word文档过程记录

sprinboot使用freemarker导出word文档过程记录

简介

本文为springboot使用freemarker技术导出word文档的过程记录。

内容

1、代码部分

springboot项目创建

开发工具:idea

java版本:java8

springboot版本:2.1.6.RELEASE

创建一个maven项目,项目名称自定义,例如:ssqxx-word

创建完父类项目之后,删除生成的java文件夹和resources文件夹,新建一个子模块,模块命名自定义,例如:test-export-word-manage

springboot导包

父类pom文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?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文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
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层
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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();
}
}
}
模块配置文件
1
2
3
4
5
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文件导出的开发过程记录,希望能给你们的开发提供一些些帮助!

谢谢支持!

-------------本文结束感谢您的阅读-------------

本文标题:sprinboot使用freemarker导出word文档过程记录

文章作者:ssqxx

发布时间:2020年11月19日 - 17:13:08

最后更新:2020年11月19日 - 18:38:21

原始链接:http://yoursite.com/2020/11/19/work-export-word-springboot/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。