Springboot 使用freemaker导出word文档
程序员文章站
2022-12-07 19:30:01
利用freemarker导出word文档,主要分为一下几部分,但是循环写入图片是其中最难的一点,尤其是从未使用freemaker导出word模板的新手。话不多说,开搞。1 找到需要导出的word模板,我的模板截图如下,其中涉及到了表格,文字以及图片(模板中只放了一张图片,事实是不固定数量的)2 将word文档另存为xml文件,这里建议使用office来操作,wps没有实践过,网上都是推荐使用office来制作模板,具体有兴趣的可以单独实践。另存为xml文件以后,使用其他工具打开xml文件.....
利用freemarker导出word文档,主要分为一下几部分,但是循环写入图片是其中最难的一点,尤其是从未使用freemaker导出word模板的新手。话不多说,开搞。
1 找到需要导出的word模板,我的模板截图如下,其中涉及到了表格,文字以及图片(模板中只放了一张图片,事实是不固定数量的)
2 将word文档另存为xml文件,这里建议使用office来操作,wps没有实践过,网上都是推荐使用office来制作模板,具体有兴趣的可以单独实践。另存为xml文件以后,使用其他工具打开xml文件,并格式化格式。其中将需要实时填充的地方用参数代替,比如我下图中 朝鲜战争需要实时传入,则用${title}代替,模板中的所有文字参数地方都可以使用此方法,然后将.xml后缀修改为.ftl,并将ftl文件放在项目指定位置。
3 模板中含有图片以及图片数量不固定的情况,则需要以下步骤,
3.1 在模板中 <w:body></w:body>标签中 把事先放入占位的图片换为循环展示 的id要和一致 入下图。
3.2 在 <pkg:xmlData>中定义图片id Relationships中循环定义图片id (注意图片id不能重复,重复会导致写入的图片无法显示)
3.3 找到事先放入的图片的字节流位置( pkg:binaryData中一大段字符串),替换为循环的base64,如上图中。其中需要将图片 转换成BASE64字符串,具体方法百度,很多工具类。
注意,以上三个地方图片id必须保持一致否则可能出现填充的图片无法显示的情况。
4 如果文字需要循环填充,则使用下图中的list循环填充即可,if是判断此参数是否存在的,一般来说添加不会出错,不添加则需要保证传入的参数一定要有此参数名称,否则报错
5 编写代码,将需要填充的参数放入一个map中,使用工具类即可生成报告。
6 工具类如下:
import freemarker.template.Configuration; import freemarker.template.Template; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.time.DateFormatUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.zip.ZipOutputStream; /** * 导出 * @author * @date */ @Component public class ExportUtil { private static Logger logger = LoggerFactory.getLogger(ExportUtil.class); @Autowired private Configuration freemarkerConfig; /** * 通过浏览器导出excel * * @param response * @param freemarkerTemplateName * @param map */ public void exportExcel(HttpServletResponse response, String freemarkerTemplateName, String excelName, HashMap<String, Object> map) { Writer out = null; try { excelName = excelName.replaceAll(" ", "_"); response.reset(); // 清空输出流 response.setContentType("application/msexcel;charset=utf-8"); response.setHeader("Content-disposition", "attachment; filename=" + excelName + ".xls");// 设定输出文件头 out = response.getWriter(); Template template = freemarkerConfig.getTemplate(freemarkerTemplateName); template.setEncoding("utf-8"); template.process(map, out); if (out != null) { out.flush(); out.close(); } } catch (Exception e) { logger.error("导出excel失败", e); if (out != null) { try { out.close(); } catch (IOException ignore) { } } } } /** * 通过浏览器导出word * * @param response * @param freemarkerTemplateName * @param map */ public void exportWord(HttpServletResponse response, String freemarkerTemplateName, String wordName, HashMap<String, Object> map) { Writer out = null; try { response.reset();// 清空输出流 response.setContentType("application/msword;charset=utf-8"); response.setHeader("Content-disposition", "attachment; filename=" + wordName + ".doc");// 设定输出文件头 out = response.getWriter(); Template template = freemarkerConfig.getTemplate(freemarkerTemplateName); template.setEncoding("utf-8"); template.process(map, out); if (out != null) { out.flush(); out.close(); } } catch (Exception e) { logger.error("导出word失败", e); if (out != null) { try { out.close(); } catch (IOException e1) { logger.error("导出word失败", e1); } } } } /** * 导出到指定目录 * @param directory 文件存放的目录 * @param freemarkerTemplateName * @param fileName 文件名带文件后缀 * @param map */ public void exportWord(String directory, String freemarkerTemplateName, String fileName, HashMap<String, Object> map) { OutputStreamWriter out = null; try { File dir = new File(directory); if(!dir.exists()){ if(!dir.mkdirs()){ logger.error("创建目录异常:{}",dir.toString()); return; } } if(!dir.isDirectory()){ FileUtils.deleteQuietly(dir); logger.error("目录错误"); return; } File file = new File(directory+File.separator+fileName); out = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8); Template template = freemarkerConfig.getTemplate(freemarkerTemplateName); template.setEncoding("utf-8"); template.process(map, out); out.flush(); out.close(); } catch (Exception e) { logger.error("导出word失败", e); if (out != null) { try { out.close(); } catch (IOException e1) { logger.error("导出word失败", e1); } } } } /** * 导出zip * @param response * @param files * @param path * @param exportName */ public void exportZip(HttpServletResponse response, List<File> files, String path, String exportName){ OutputStream out = null; String zipPath = path + exportName + ".zip"; File zipFile = new File(zipPath); try{ FileOutputStream fileOutputStream = new FileOutputStream(zipFile); ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream); ZipUtil.zip(files, zipOutputStream); zipOutputStream.close(); fileOutputStream.close(); //删除文件 files.forEach( file -> { file.delete(); } ); response.reset(); response.setContentType("application/x-zip-compressed;charset=utf-8"); response.setHeader("Content-disposition", "attachment; filename=" + exportName); InputStream in = new FileInputStream(zipFile); int len = 0; byte[] buffer = new byte[1024]; out = response.getOutputStream(); while((len=in.read(buffer))>0){ out.write(buffer,0,len); } in.close(); out.flush(); out.close(); }catch (Exception e){ logger.error("导出zip失败", e); if (out != null) { try { out.close(); } catch (IOException e1) { logger.error("导出zip失败", e1); } } } if (zipFile.exists()) { zipFile.delete(); } } public void exportZipPath(List<File> files, String path, String exportName){ OutputStream out = null; String zipPath = path + exportName + ".zip"; File zipFile = new File(zipPath); try{ FileOutputStream fileOutputStream = new FileOutputStream(zipFile); ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream); ZipUtil.zip(files, zipOutputStream); zipOutputStream.close(); fileOutputStream.close(); //删除文件 files.forEach( file -> { file.delete(); } ); }catch (Exception e){ logger.error("生成zip失败", e); if (out != null) { try { out.close(); } catch (IOException e1) { logger.error("导出zip失败", e1); } } } } @SuppressWarnings("unused") private String formatTime(Date date, String pattern) { if (date == null) { return ""; } else { return DateFormatUtils.format(date, pattern); } } /** * 项目名称:pscms_hlj_web_base * * @param content * @return 描述: 截取字符串,截取到最后一个句号,问号,或者感叹号,总字数不超过1000 创建人:yww 创建时间:2012-7-23 * 下午2:32:59 修改人:yww 修改时间:2012-7-23 下午2:32:59 修改备注: * @version */ private String truncateContent(String content) { int limit = 1000; String truncatedContent = content; if (content.endsWith("......")) { List<Integer> posNum = new ArrayList<Integer>(); posNum.add(truncatedContent.lastIndexOf("。")); posNum.add(truncatedContent.lastIndexOf("?")); posNum.add(truncatedContent.lastIndexOf("!")); posNum.add(truncatedContent.lastIndexOf("?")); posNum.add(truncatedContent.lastIndexOf("!")); Collections.sort(posNum); int finalPos = posNum.get(posNum.size() - 1) + 1; // 因为截取的时候不包含标点符号出现的位置,所以要+1 if (finalPos != 0 && finalPos <= limit) { // 如果找到,那么截取到当前符号的位置 truncatedContent = truncatedContent.substring(0, finalPos); } } if (content.length() > limit) { // 小与1000时直接返回 truncatedContent = content.substring(0, limit); } List<Integer> posNum = new ArrayList<Integer>(); posNum.add(truncatedContent.lastIndexOf("。")); posNum.add(truncatedContent.lastIndexOf("?")); posNum.add(truncatedContent.lastIndexOf("!")); posNum.add(truncatedContent.lastIndexOf(".")); posNum.add(truncatedContent.lastIndexOf("?")); posNum.add(truncatedContent.lastIndexOf("!")); Collections.sort(posNum); int finalPos = posNum.get(posNum.size() - 1) + 1; // 因为截取的时候不包含标点符号出现的位置,所以要+1 if (finalPos != 0 && finalPos <= limit) { // 如果找到,那么截取到当前符号的位置 truncatedContent = truncatedContent.substring(0, finalPos); } return truncatedContent; } }本文地址:https://blog.csdn.net/juanqiao7497/article/details/110231658
上一篇: 夷陵之战中,刘备的战术存在什么问题吗?
下一篇: Linux内核设备驱动地址映射笔记整理