<!--word模板工具包-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
<!--打包压缩工具包-->
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.10.5</version>
</dependency>
二:根据word文档生成我们需要的ftl模板文件
打开word文档-->另存为--->'将word文档保存类型选择xml类型'-->保存就得到一个名为***.xml的模板文件
将生成的**.xml文件重命名,将后缀的“xml”改为“ftl”的格式:
然后打开这个后缀为ftl的word模板文件,将我们需要通过后台生成的内容替换成${***}的形式
解释一下:${***}是ftl模板的占位符,我们可以通过这个占位符来进行对模板内容进行赋值,生成我们想要的word文档。
这里我打开ftl模板用的是工具是Edit with Notepad++
(可以用其他的,文件编辑器也可以)。
搜索我们需要替换的内容信息:这里我将“我的姓名”改为占位符的形式
下面按照这种方式挨个将我们需要修改的内容全部替换掉。
注意: 占位符中${studentname}的studentname是自己命名的名字,自己按需要起名。
将修改完成的模板保存起来备用。
三:将word需要的数据存入一个map中
Map<String, Object> dataMap = new HashMap<String, Object>();
String studentName = "张三";
dataMap.put("studentname", studentName); // 姓名
dataMap.put("gender", "男"); // 性别
dataMap.put("hometown", "江苏省-苏州市"); // 籍贯: **省-**市
dataMap.put("birthday", "1996-04-05"); // 出生年月
dataMap.put("classname", "三年级一班"); // 班级
dataMap.put("subjectname", "英语"); // 学科
dataMap.put("fraction", "98"); // 分数
dataMap.put("rank", "3"); // 排名
String phoneImage = downPicture("C:/Users/wangxk/Pictures/question.jpg");
dataMap.put("imagephone", phoneImage); // 照片
这里我们用到了一个方法downPicture("")
,该方法时将指令路径下的图片处理成base64的格式,然后返回处理后的字符串:
* 图片转码处理
public String downPicture(String picture) {
InputStream in = null;
byte[] data = null;
try {
String fileName = picture;
in = new FileInputStream(fileName);
data = new byte[in.available()];
in.read(data);
in.close();
} catch (Exception e) {
e.printStackTrace();
BASE64Encoder encoder = new BASE64Encoder();
String address = encoder.encode(data);
return address;
注意:一般图片转换成base64格式时,有一个特定的开头:data:image/jpeg;base64,
,如果我们需要用base64还原图片时,需要将格式头加上
四:编写文档生成工具类:FreeMarkerFileUtils
package com.blog.web.controller.tool;
import com.blog.framework.config.properties.BlogConfig;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.commons.io.FileUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import java.io.*;
import java.util.Map;
* word文档模板支持工具
* @author Aaron Wang
* @data 2020/7/14
public class FreeMarkerFileUtils {
public static String documentDataFilling(Map<String,Object> dataMap, String fileNmae){
try{
Configuration configuration = new Configuration();
configuration.setDefaultEncoding("UTF-8");
* 以下是两种指定ftl文件所在目录路径的方式,注意这两种方式都是
* 指定ftl文件所在目录的路径,而不是ftl文件的路径
File templeteFile = new File(BlogConfig.getProfile() + "laborTemplete/");
if (!templeteFile.exists()) {
templeteFile.mkdirs();
configuration.setDirectoryForTemplateLoading(templeteFile);
Resource resource = new ClassPathResource("wordTemplete/word模板.ftl");
File inuModel = new File(BlogConfig.getProfile() + "/laborTemplete/laborFile.ftl");
FileUtils.copyToFile(resource.getInputStream(), inuModel);
File fileLeave = new File(BlogConfig.getProfile() + "laborFIle/");
if (!fileLeave.exists()) {
fileLeave.mkdirs();
String wordName = fileNmae + ".doc";
File outFile = new File(BlogConfig.getProfile() + "/laborFIle/" + new String( wordName.getBytes("utf-8") , "utf-8"));
Template template = configuration.getTemplate("laborFile.ftl");
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "utf-8"), 10240);
template.process(dataMap, out);
out.close();
return BlogConfig.getProfile() + "/laborFIle/";
}catch (Exception e){
e.printStackTrace();
return BlogConfig.getProfile() + "/laborFIle/";
注意:代码中引用了一个BlogConfig.getProfile()
的方法,这是我自己定义的一个方法,用于获取生成的word文档的存储目录,可以直接指定我们自己电脑下的目录,如:D:/profile
五:编写文件压缩工具类:CompressUtil
package com.blog.web.controller.tool;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipOutputStream;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
* 文件压缩工具
* @author Aaron Wang
* @data 2020/7/25
public class CompressUtil {
* @param sourcePath 要压缩的文件路径
* @param suffix 生成的格式后最(zip、rar)
public static void generateFile(String sourcePath, String suffix) throws Exception {
File file = new File(sourcePath);
if (!file.exists()) {
throw new Exception("路径 " + sourcePath + " 不存在文件,无法进行压缩...");
String generateFile = file.getParent() + File.separator +"CompressFile";
File compress = new File(generateFile);
if( !compress.exists() ){
compress.mkdirs();
String generateFileName = compress.getAbsolutePath() + File.separator + "AAA" + file.getName() + "." + suffix;
FileOutputStream outputStream = new FileOutputStream(generateFileName);
ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(outputStream));
generateFile(zipOutputStream,file,"");
System.out.println("源文件位置:" + file.getAbsolutePath() + ",目的压缩文件生成位置:" + generateFileName);
zipOutputStream.close();
* @param out 输出流
* @param file 目标文件
* @param dir 文件夹
* @throws Exception
private static void generateFile(ZipOutputStream out, File file, String dir) throws Exception {
if (file.isDirectory()) {
File[] files = file.listFiles();
out.putNextEntry(new ZipEntry(dir + "/"));
dir = dir.length() == 0 ? "" : dir + "/";
for (int i = 0; i < files.length; i++) {
generateFile(out, files[i], dir + files[i].getName());
} else {
FileInputStream inputStream = new FileInputStream(file);
out.putNextEntry(new ZipEntry(dir));
int len = 0;
byte[] bytes = new byte[1024];
while ((len = inputStream.read(bytes)) > 0) {
out.write(bytes, 0, len);
inputStream.close();
* 递归压缩文件
* @param output ZipOutputStream 对象流
* @param file 压缩的目标文件流
* @param childPath 条目目录
private static void zip(ZipOutputStream output,File file,String childPath){
FileInputStream input = null;
try {
if (file.isDirectory()) {
File list[] = file.listFiles();
childPath = childPath + (childPath.length() == 0 ? "" : "/")
+ file.getName();
for (File f : list) {
zip(output, f, childPath);
} else {
childPath = (childPath.length() == 0 ? "" : childPath + "/")
+ file.getName();
output.putNextEntry(new ZipEntry(childPath));
input = new FileInputStream(file);
int readLen = 0;
byte[] buffer = new byte[1024 * 8];
while ((readLen = input.read(buffer, 0, 1024 * 8)) != -1) {
output.write(buffer, 0, readLen);
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (input != null) {
try {
input.close();
} catch (IOException ex) {
ex.printStackTrace();
* 压缩文件(文件夹)
* @param path 目标文件流
* @param format zip 格式 | rar 格式
* @throws Exception
public static String zipFile(File path,String format) throws Exception {
String generatePath = "";
if( path.isDirectory() ){
generatePath = path.getParent().endsWith("/") == false ? path.getParent() + File.separator + path.getName() + "." + format: path.getParent() + path.getName() + "." + format;
}else {
generatePath = path.getParent().endsWith("/") == false ? path.getParent() + File.separator : path.getParent();
generatePath += path.getName().substring(0,path.getName().lastIndexOf(".")) + "." + format;
FileOutputStream outputStream = new FileOutputStream( generatePath );
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(outputStream));
zip(out, path,"");
out.flush();
out.close();
return generatePath;
* 递归删除目录下的所有文件及子目录下所有文件
* @param dir 将要删除的文件目录
public static boolean deleteDir(File dir) {
if (dir.isDirectory()) {
String[] children = dir.list();
for (int i=0; i<children.length; i++) {
boolean success = deleteDir(new File(dir, children[i]));
if (!success) {
return false;
return dir.delete();
public static void main(String[] args) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String sd = sdf.format(new Date());
System.out.println(sd);
String path = "D:\\profile\\laborFIle";
try {
System.out.println(deleteDir(new File(path)));
} catch (Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
五:编写访问的controller方法:
* 下载生成的word文档
* @param zjhms 勾选的民工证件号码集合
* @param prjnumber 项目合同备案号
* @return
* @throws IOException
@GetMapping("/downnloadFiles")
@ResponseBody
public AjaxResult downnloadOtherFiles(String zjhms, String prjnumber) {
try {
return downFile(zjhms, prjnumber);
}catch (Exception e){
e.printStackTrace();
return AjaxResult.error("下载失败,请联系管理员!");
* 下载档案文件
* @param zjhms 员工证件号码集合
* @return
public AjaxResult downFile(String zjhms, String prjnumber){
Map<String, Object> dataMap = new HashMap<String, Object>();
String studentName = "张三";
dataMap.put("studentname", studentName);
dataMap.put("gender", "男");
dataMap.put("hometown", "江苏省-苏州市");
dataMap.put("birthday", "1996-04-05");
dataMap.put("classname", "三年级一班");
dataMap.put("subjectname", "英语");
dataMap.put("fraction", "98");
dataMap.put("rank", "3");
String phoneImage = downPicture("C:/Users/wangxk/Pictures/question.jpg");
dataMap.put("imagephone", phoneImage);
String wordName = studentName + "_" + "成绩单";
String wordsPath = FreeMarkerFileUtils.documentDataFilling(dataMap, wordName);
try {
String path = CompressUtil.zipFile(new File(wordsPath), "zip");
boolean delete = CompressUtil.deleteDir(new File(wordsPath));
return AjaxResult.success("laborFIle.zip");
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.error("下载失败。。。");
我们指定了前端访问的路径为:***/downnloadFiles
的形式,当我们通过网页请求到该路径时,我们就可以下载生成的word文档文件压缩包
六:前端代码编写
<a class="btn btn-outline btn-info btn-rounded" onclick="downnloadOtherFiles()">
<i class="fa fa-download"></i> word文档下载
<script th:inline="javascript">
var prefix = ctx + "blogback";
function downnloadOtherFiles() {
$.modal.loading("正在下载,请稍后。。。");
var url = prefix + "/downnloadFiles";
var param = "?zjhms=" + "411521185477412587" + "&prjnumber=" + "3256987422114";
console.log("建筑民工登记表的url = " + url + param);
$.get(url + param, function (result) {
if (result.code == web_status.SUCCESS) {
window.location.href = ctx + "laborerFileMess/downloadZip?fileName=" + encodeURI(result.msg) + "&delete=" + true;
$.modal.closeLoading();
} else if (result.code == web_status.WARNING) {
$.modal.closeLoading();
$.modal.alertWarning(result.msg)
} else {
$.modal.alertError(result.msg);
</script>
点击按钮直接请求到后台我们指定的方法进行word文档的下载。laborerFileMess/downloadZip
这是一个下载的方法,比较简单,下面试方法的代码。
七:编写下载word文档压缩包的方法:
* 下载压缩包
* @param fileName 文件名称
* @param delete 是否删除
@GetMapping("laborerFileMess/downloadZip")
public void downloadZip(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request) {
try {
String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
String filePath = BlogConfig.getProfile() + fileName;
response.setCharacterEncoding("utf-8");
response.setContentType("multipart/form-data");
response.setHeader("Content-Disposition",
"attachment;fileName=" + setFileDownloadHeader(request, realFileName));
FileUtils.writeBytes(filePath, response.getOutputStream());
if (delete) {
FileUtils.deleteFile(filePath);
} catch (Exception e) {
System.out.println("压缩包下载失败。。");
e.printStackTrace();
public String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException {
final String agent = request.getHeader("USER-AGENT");
String filename = fileName;
if (agent.contains("MSIE")) {
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
} else if (agent.contains("Firefox")) {
filename = new String(fileName.getBytes(), "ISO8859-1");
} else if (agent.contains("Chrome")) {
filename = URLEncoder.encode(filename, "utf-8");
} else {
filename = URLEncoder.encode(filename, "utf-8");
return filename;
这里还用到了一个文件处理类:FileUtils
package com.blog.common.utils.file;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import javax.servlet.http.HttpServletRequest;
* 文件处理工具类
public class FileUtils
public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";
* 输出指定文件的byte数组
* @param filePath 文件路径
* @param os 输出流
* @return
public static void writeBytes(String filePath, OutputStream os) throws IOException
FileInputStream fis = null;
File file = new File(filePath);
if (!file.exists())
throw new FileNotFoundException(filePath);
fis = new FileInputStream(file);
byte[] b = new byte[1024];
int length;
while ((length = fis.read(b)) > 0)
os.write(b, 0, length);
catch (IOException e)
throw e;
finally
if (os != null)
os.close();
catch (IOException e1)
e1.printStackTrace();
if (fis != null)
fis.close();
catch (IOException e1)
e1.printStackTrace();
* 删除文件
* @param filePath 文件
* @return
public static boolean deleteFile(String filePath)
boolean flag = false;
File file = new File(filePath);
if (file.isFile() && file.exists())
file.delete();
flag = true;
return flag;
* 文件名称验证
* @param filename 文件名称
* @return true 正常 false 非法
public static boolean isValidFilename(String filename)
return filename.matches(FILENAME_PATTERN);
* 下载文件名重新编码
* @param request 请求对象
* @param fileName 文件名
* @return 编码后的文件名
public static String setFileDownloadHeader(HttpServletRequest request, String fileName)
throws UnsupportedEncodingException
final String agent = request.getHeader("USER-AGENT");
String filename = fileName;
if (agent.contains("MSIE"))
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
else if (agent.contains("Firefox"))
filename = new String(fileName.getBytes(), "ISO8859-1");
else if (agent.contains("Chrome"))
filename = URLEncoder.encode(filename, "utf-8");
filename = URLEncoder.encode(filename, "utf-8");
return filename;
八:下面是下载的结果
将下载的压缩包解压后就得到我们想生成的成绩单的word文档文件。
九:批量生成多个word文档,统一打包到一个压缩包中
其实这个想实现很简单,在文档的第五步,就是根据信息生成一个word文档,然后将该文档所在的目录打包成压缩文件供我们下载,想要实现生成多个word文档然后在同一打包压缩,我们只需要先循环定义我们生成word文档的Map集合信息,生成多个文档存放在我们指定的目录下,最后再打包压缩该目录即可。
文章到这里就告一段落了,如果有什么错误的地方欢迎大家指正。我们一起学习。
欢迎大家评论留言。我都会一一回复。
项目下载源码