Java使用FreeMarker来实现Word自定义导出功能
作者:保加利亚的风
前言
在对一些特定导出功能,使用常规Excel无法解决的,通常使用Word来实现导出功能,这篇介绍下如何在Java中使用FreeMarker模板注入方式来实现Word导出功能
导出案例(已作打码处理)
或者:
准备工作
第一步:maven依赖库
<!-- freemarker (用于Word导出)--> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>${freemarker.version}</version> </dependency>
第二步:Word导出工具类
import com.zrxt.common.config.RuoYiConfig; import com.zrxt.common.core.text.CharsetKit; import freemarker.cache.ClassTemplateLoader; import freemarker.template.Configuration; import freemarker.template.Template; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Map; /** * @ClassName WordUtil * @Description 使用Freemarker生成Word文档工具类 * @Author * @Date 2023/12/14 **/ public class WordUtil { /** * 使用Freemarker自动生成Word文档(磁盘路径方法) * * @param dataMap 保存Word文档中所需要的数据 * @param templatePath 模板文件的绝对路径 * @param templateFile 模板文件的名称 * @throws Exception */ public static void CreateWord(HttpServletResponse response, Map<String, Object> dataMap, String templatePath, String templateFile) throws Exception { Writer out = null; try { // 设置FreeMarker的版本 Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS); // 设置Freemarker的编码格式 configuration.setDefaultEncoding(CharsetKit.UTF_8); // 设置FreeMarker生成Word文档所需要的模板的路径 configuration.setDirectoryForTemplateLoading(new File(templatePath)); // 设置FreeMarker生成Word文档所需要的模板名称 Template t = configuration.getTemplate(templateFile, CharsetKit.UTF_8); // 创建一个Word文档的输出流 out = new BufferedWriter(new OutputStreamWriter(response.getOutputStream(), StandardCharsets.UTF_8)); //FreeMarker使用Word模板和数据生成Word文档 t.process(dataMap, out); }catch (Exception e){ e.printStackTrace(); }finally { assert out != null; out.flush(); out.close(); } } /** * 使用Freemarker自动生成Word文档 * * @param dataMap 保存Word文档中所需要的数据 * @param templatePath 模板文件的路径(绝对) * @param templateFile 模板文件的名称 * @throws Exception */ public static void GeneratorWord(HttpServletResponse response, Map<String, Object> dataMap, String templatePath, String templateFile) throws Exception { Writer out = null; try { // 设置FreeMarker的版本 Configuration configuration = new Configuration(Configuration.VERSION_2_3_28); // 设置Freemarker的编码格式 configuration.setDefaultEncoding(CharsetKit.UTF_8); //相对路径加载模板方法 configuration.setTemplateLoader(new ClassTemplateLoader(WordUtil.class,templatePath)); // 设置FreeMarker生成Word文档所需要的模板名称 Template template = configuration.getTemplate(templateFile, CharsetKit.UTF_8); // 创建一个Word文档的输出流 out = new BufferedWriter(new OutputStreamWriter(response.getOutputStream(), StandardCharsets.UTF_8)); //FreeMarker使用Word模板和数据生成Word文档 template .process(dataMap, out); }catch (Exception e){ e.printStackTrace(); }finally { assert out != null; out.flush(); out.close(); } } /** * 下载文件 * * @param path 文件的位置 * @param fileName 自定义下载文件的名称 * @param response http响应 * @param request http请求 */ public static void downloadFile(String path, String fileName, HttpServletResponse response, HttpServletRequest request) { try { File file = new File(path); // 中文乱码解决 String type = request.getHeader("User-Agent").toLowerCase(); if (type.indexOf("firefox") > 0 || type.indexOf("chrome") > 0) { // 谷歌或火狐 fileName = new String(fileName.getBytes(StandardCharsets.UTF_8), "iso8859-1"); } else { // IE fileName = URLEncoder.encode(fileName, CharsetKit.UTF_8); } // 设置响应的头部信息 response.setHeader("content-disposition", "attachment;filename=" + fileName); // 设置响应内容的类型 response.setContentType(getFileContentType(fileName) + "; charset=" + CharsetKit.UTF_8); // 设置响应内容的长度 response.setContentLength((int) file.length()); // 输出 outStream(new FileInputStream(file), response.getOutputStream()); } catch (Exception e) { e.printStackTrace(); } } /** * 文件的内容类型 */ private static String getFileContentType(String name) { String result = ""; String fileType = name.toLowerCase(); if (fileType.endsWith(".png")) { result = "image/png"; } else if (fileType.endsWith(".gif")) { result = "image/gif"; } else if (fileType.endsWith(".jpg") || fileType.endsWith(".jpeg")) { result = "image/jpeg"; } else if (fileType.endsWith(".svg")) { result = "image/svg+xml"; } else if (fileType.endsWith(".doc")) { result = "application/msword"; } else if (fileType.endsWith(".xls")) { result = "application/x-excel"; } else if (fileType.endsWith(".zip")) { result = "application/zip"; } else if (fileType.endsWith(".pdf")) { result = "application/pdf"; } else { result = "application/octet-stream"; } return result; } /** * 基础字节数组输出 */ private static void outStream(InputStream is, OutputStream os) { try { byte[] buffer = new byte[10240]; int length = -1; while ((length = is.read(buffer)) != -1) { os.write(buffer, 0, length); os.flush(); } } catch (Exception e) { e.printStackTrace(); } finally { try { os.close(); is.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 检查存储生成文件的路径是否存在,如果不存在则新建路径. * * @param directory the directory name, like '\dir-name' */ public static void CheckDownloadPath(String directory) { File path = new File(RuoYiConfig.getDownloadPath() + directory); if (!path.exists()) { path.mkdirs(); } } }
模板准备
第一步:首先要编写Word模板,可以参考文章开头的示例图片。
第二步:Word模板编写好后,点击另存为,然后选择Word 2003 XML文档
第三步:保存好后的文件打开,Ctrl+A
复制文档所有内容,然后在线搜索XML格式化在线工具
,我这边提供一个现成的XML格式化在线工具,将内容全部粘贴进去后点击格式化按钮。
创建ftl文件
上面所有工作准备好后,就可以在resource
目录下创建一个.ftl
文件,然后将格式化后的代码复制到文件中即可。
测试导出是否完整
在Controller
层编写测试代码,判断是否可以正常导出
/** * 导出(word)关键过程控制详细信息详细信息(xml版本) */ @PostMapping(value = "/exportWord") public void getInfo(MakeCriticalProcessControl param, HttpServletResponse response) { try { MakeCriticalProcessControl obj= makeCriticalProcessControlService.selectById(param.getId()); Map<String, Object> context = new HashMap<>(); context.put("obj", obj); WordUtil.GeneratorWord(response, context, "/wordDocumentFtl/make/", "MakeCriticalProcessControl.ftl"); } catch (Exception e) { e.printStackTrace(); throw new ServiceException("导出Word文件失败,请稍后重试!"); } }
WordUtil.GeneratorWord()方法
第二个参数context为数据库的数据,要往word中空白单元格所填充的。
第三个参数为templatePath – 模板文件的路径(相对路径)。
第四个参数为具体的模板的文件名称(带后缀)
注:测试后需检查格式是否混乱,是否有缺少单元格等情况,如果有则检查原Word模板并修复然后重新走一遍流程即可
如何将数据库数据填充到Word中
ftl文件中的内容是一行一行的顺序,也就是从第一行的第一个单元格开始,然后是第一行第二个单元格,第三个单元格…
然后第一行如果结束了,则是第二行第一个单元格、第二个单元格、第三个…
以此类推,所以优先找到对应标题的对应数据单元格在ftl文件的哪个位置。
如果是单个属性或对象类型的,可以在controller中使用Map<String, Object> context = new HashMap<>();
将数据put,然后key作为键,在ftl文件中可以把属性获取出来。例如下图
如果涉及到遍历,将一个list结果导出到word,请看以下案例
如果是遍历导出的话,则在word模板设置时,只需要设置一行表头,下方对应一行空的单元格即可,我们要循环空的单元格,然后将数据挨个写入。
只需要在标题下写一行即可
在controller中依旧是查询出对应list,然后put到map中
1:判断list是否为空
2:循环遍历list
3:一般<w:tr>
表示为行,所以这里的意思就是,list有多少数据,则就生成多少行
如何要获取值的话就可以${item.属性名!}
如果涉及到If语句或者是需要将数字转为对应的状态
以上就是Java使用FreeMarker来实现Word自定义导出功能的详细内容,更多关于Java FreeMarker Word自定义导出的资料请关注脚本之家其它相关文章!