共计 5238 个字符,预计需要花费 14 分钟才能阅读完成。
通过 maven 引入须要的依赖
<dependency> | |
<groupId>org.springframework.boot</groupId> | |
<artifactId>spring-boot-starter-freemarker</artifactId> | |
</dependency> |
创立导出模版
1、首先创立一个 word 模版文档,模版如下图
word 模版地址:http://file.xiaoxiaofeng.site/files/word.doc
留神 word 中的占位符的格局,就是 freemaker 的格局
具体解释如下:
- 文字处理:
间接用 ${} 两头为替换的字段名。
如果间接在 word 外面定义 ${title},在转换成 xml 的时候有可能会被一些编码隔开,这个时候只须要用 word 关上 xml,将这些内容从新输出一遍。
强烈建议用 IDE 关上,而后格式化下,在进行查看解决,能用搜寻尽量搜寻,xml 的格局外面有太多的代码,不要硬刚😂
- 图片解决:
须要在 word 文档模版中插入图片
将 word 转换成 xml 后,关上 xml,会将咱们的图片转换成长长的一段 base64。
咱们把 base64 换成咱们的 ${pic}就能够了,pic 为字段名,可任意替换
- 列表解决:
须要在 word 文档模版中插入表格
找到第二个<w:tr>
,第一行是咱们的题目,在其后面增加 <#list peopleList as list> 其中 peopleList 是传入 list 的汇合名称 list 是别名。
参数取值为:${list.name}这样。
在与 <w:tr>
配对的 </w:tr> 前面增加 </#list>。语法同 freemaker 的 for 循环语法
创立 ftl 模板
将上述 word 文档另存为 test.xml 格局,另存完之后能够用浏览器关上 test.xml 文件,查看咱们的占位符是否规范
留神:占位符有时候会产生被隔开的状况,如下图:
图片的替换同下面的阐明,图片为一串长长的 base64,如下图所示:
而后将文件搁置 resources/templates 目录下。
搁置在 resource 目录下即可,剩下的目录依据本人需要定制。留神要与 WordUtil.java 中的目录对应。
编写程序
通用的导出工具类
在 util 包下创立 WordUtil.java
工具类,👇
package com.maple.demo.util; | |
import freemarker.template.Configuration; | |
import freemarker.template.Template; | |
import freemarker.template.TemplateException; | |
import java.io.*; | |
import java.nio.charset.StandardCharsets; | |
import java.util.Map; | |
import static freemarker.template.Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS; | |
/** | |
* @author 笑小枫 | |
* @date 2022/7/27 | |
* @see <a href="https://www.xiaoxiaofeng.com">https://www.xiaoxiaofeng.com</a> | |
*/ | |
public class WordUtil { | |
private final Configuration configuration; | |
public WordUtil() {configuration = new Configuration(DEFAULT_INCOMPATIBLE_IMPROVEMENTS); | |
configuration.setDefaultEncoding("UTF-8"); | |
} | |
public void createWord(Map<String, Object> dataMap, String templateName, String fileName) { | |
// 模板文件所在门路 | |
configuration.setClassForTemplateLoading(this.getClass(), "/templates"); | |
Template t = null; | |
try { | |
// 获取模板文件 | |
t = configuration.getTemplate(templateName, "UTF-8"); | |
} catch (IOException e) {e.printStackTrace(); | |
} | |
// 导出文件 | |
File outFile = new File(fileName); | |
try (Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), StandardCharsets.UTF_8))) {if (t != null) { | |
// 将填充数据填入模板文件并输入到指标文件 | |
t.process(dataMap, out); | |
} | |
} catch (IOException | TemplateException e1) {e1.printStackTrace(); | |
} | |
} | |
} |
申请接口
在 controller
包下创立TestWordController.java
👇
其中用到了之前文章的文件转 base64,参考文章[15.Base64 与 File 互转]()
package com.maple.demo.controller; | |
import com.maple.demo.util.Base64Util; | |
import com.maple.demo.util.WordUtil; | |
import io.swagger.annotations.Api; | |
import lombok.AllArgsConstructor; | |
import org.springframework.web.bind.annotation.GetMapping; | |
import org.springframework.web.bind.annotation.RequestMapping; | |
import org.springframework.web.bind.annotation.RestController; | |
import javax.servlet.ServletOutputStream; | |
import javax.servlet.http.HttpServletRequest; | |
import javax.servlet.http.HttpServletResponse; | |
import java.io.File; | |
import java.io.FileInputStream; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.net.URLEncoder; | |
import java.util.*; | |
/** | |
* @author 笑小枫 | |
* @date 2022/7/27 | |
* @see <a href="https://www.xiaoxiaofeng.com">https://www.xiaoxiaofeng.com</a> | |
*/ | |
@RestController | |
@AllArgsConstructor | |
@RequestMapping("/example") | |
@Api(tags = "实例演示 - 依据模板生成 word") | |
public class TestWordController { | |
/** | |
* 文字处理:* 间接用 ${} 两头为替换的字段名。* 如果间接在 word 外面定义 ${title},在转换成 xml 的时候有可能会被一些编码隔开,这个时候只须要用 word 关上 xml,将这些内容从新输出一遍。* 能够用浏览器关上 xml,检出本人定义的 ${}的内容是否都在一起,是否有被编码隔开的状况。* 图片解决:* 须要在 word 文档模版中插入图片 | |
* 将 word 转换成 xml 后,关上 xml,会将咱们的图片转换成长长的一段 base64。* 咱们把 base64 换成咱们的 ${pic}就能够了,pic 为字段名,可任意替换 | |
* 列表解决:* 须要在 word 文档模版中插入表格 | |
* 找到第二个 <w:tr>,在其后面增加 <#list peopleList as list> 其中 peopleList 是传入 list 的汇合名称 list 是别名。* 参数取值为:${list.name}这样。* 在与 <w:tr> 配对的 </w:tr> 前面增加 </#list>。语法同 freemaker 的 for 循环语法 | |
*/ | |
@GetMapping("/exportWord") | |
public void exportWord(HttpServletRequest request, HttpServletResponse response) throws IOException { | |
// 1. 创立长期文件夹 | |
String rootPath = request.getSession().getServletContext().getRealPath("/"); | |
String fileName = UUID.randomUUID().toString().replace("-", ""); | |
// 解决图片信息,将图片转为 base64 字符串 | |
File imageFile = new File("D:\\xiaoxiaofeng.jpg"); | |
String base64 = Base64Util.fileToBase64(imageFile); | |
// 解决表格的数据信息 | |
List<Map<String, Object>> list = new ArrayList<>(); | |
int size = 5; | |
for (int i = 0; i < size; i++) {Map<String, Object> map = new HashMap<>(16); | |
map.put("nickName", "笑小枫" + i); | |
map.put("visitTime", "2022-07-27"); | |
map.put("isLike", "是"); | |
map.put("isAttention", "是"); | |
list.add(map); | |
} | |
// 加载 word 中的数据信息 | |
WordUtil word = new WordUtil(); | |
Map<String, Object> dataMap = new HashMap<>(16); | |
dataMap.put("title", "记得关注我哟"); | |
dataMap.put("email", "1150640979@qq.com"); | |
dataMap.put("name", "笑小枫"); | |
dataMap.put("createDate", "2022-07-27"); | |
dataMap.put("pic", base64); | |
dataMap.put("peopleList", list); | |
word.createWord(dataMap, "word.xml", rootPath + "/" + fileName + ".doc"); | |
String exportName = "测试 word"; | |
response.setCharacterEncoding("utf-8"); | |
response.setContentType("application/msword"); | |
// 设置浏览器以下载的形式解决该文件名 | |
response.setHeader("Content-Disposition", "attachment;filename=".concat(String.valueOf(URLEncoder.encode(exportName + ".doc", "UTF-8")))); | |
File file = new File(rootPath + "/" + fileName + ".doc"); | |
try (InputStream fin = new FileInputStream(file); | |
ServletOutputStream out = response.getOutputStream()) { | |
// 缓冲区 | |
byte[] buffer = new byte[512]; | |
int bytesToRead; | |
// 通过循环将读入的 Word 文件的内容输入到浏览器中 | |
while ((bytesToRead = fin.read(buffer)) != -1) {out.write(buffer, 0, bytesToRead); | |
} | |
} catch (IOException e) {e.printStackTrace(); | |
} finally { | |
// 删除临时文件 | |
file.delete();} | |
} | |
} |
测试
在浏览器输出 http://localhost:6666/example/exportWord 便能够看到你的 word 进去了呦。
导出效果图如下:
对于笑小枫💕
本章到这里完结了,喜爱的敌人关注一下我呦😘😘,大伙的反对,就是我保持写下去的能源。
微信公众号:笑小枫
笑小枫集体博客:https://www.xiaoxiaofeng.com
本文源码:https://github.com/hack-feng/maple-demo