gradle:
implementation(group: 'org.freemarker', name: 'freemarker', version: '2.3.32');
//根据springboot版本来
implementation(group:'org.springframework.boot',name:'spring-boot-starter-freemarker',version: '3.1.5');
maven:
org.freemarker
freemarker
2.3.32
org.springframework.boot
spring-boot-starter-freemarker
package com.example.demo; import freemarker.template.Configuration; import freemarker.template.Template; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; import org.springframework.ui.freemarker.FreeMarkerTemplateUtils; import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; import java.io.File; @Component public class FreemarkerComponent { protected static Logger LOG = LoggerFactory.getLogger(FreemarkerComponent.class); /** * */ private final FreeMarkerConfigurer configurer; private final ApplicationContext applicationContext; public FreemarkerComponent(FreeMarkerConfigurer configurer, ApplicationContext applicationContext) { this.configurer = configurer; this.applicationContext = applicationContext; } /** * @param templateName 模板名称 * @param t 对象 */ publicString formatTemplate(String templateName, T t) { try { Template template = configurer.getConfiguration().getTemplate(templateName); String txt = FreeMarkerTemplateUtils.processTemplateIntoString(template, t); if (LOG.isDebugEnabled()) { LOG.debug(txt); } return txt; } catch (Exception e) { LOG.error("freemarker format exception", e); } return null; } public String findTemplateByAbsolutePathAndTemplateName(String absolutePath,String templateName,T t) { try { FreemarkerTemplateAbsolutePathLoader freemarkerTemplateAbsolutePathLoader = applicationContext.getBean(FreemarkerTemplateAbsolutePathLoader.class); File source = freemarkerTemplateAbsolutePathLoader.findTemplateSource(absolutePath); if(source != null){ Configuration configuration = configurer.getConfiguration(); configuration.setDirectoryForTemplateLoading(source); Template template = configuration.getTemplate(templateName); String text = FreeMarkerTemplateUtils.processTemplateIntoString(template, t); if (LOG.isDebugEnabled()) { LOG.debug(text); } return text; } }catch (Exception e){ LOG.error("freemarker format exception", e); } return null; } }
package com.aoyuntek.lendlease.components; import freemarker.cache.TemplateLoader; import org.springframework.stereotype.Component; import java.io.*; /** * freemarker根据绝对路径加载文件 * @author hulei */ @Component public class FreemarkerTemplateAbsolutePathLoader implements TemplateLoader { public File findTemplateSource(String absolutePath) { return new File(absolutePath); } public long getLastModified(Object templateSource) { return ((File) templateSource).lastModified(); } public Reader getReader(Object templateSource, String encoding) throws IOException { if (!(templateSource instanceof File)) { throw new IllegalArgumentException("templateSource is a: " + templateSource.getClass().getName()); } return new InputStreamReader(new FileInputStream((File) templateSource), encoding); } @Override public void closeTemplateSource(Object templateSource) { } }
根据目录可以看到ftl模板(内容是html)位于resources/templates/email和resources/templates/pdf两个文件夹下
导出PDF用到的是pdf下的两个模板,这里主要是想说明下,yaml中freemarker配置怎么配,多个文件夹下的模板加载
注意freemarker缩进,位于spring层级下
freemarker: suffix: .ftl template-loader-path: classpath:/templates/pdf,classpath:/templates/email #自定义 pdf 绝对路径,可以注释 template-pdf-absolute-loader-path: D:/code-tunnel-web/tunnel-web/src/main/resources/templates/pdf #自定义 email 绝对路径,可以注释 template-email-absolute-loader-path: D:/code-tunnel-web/tunnel-web/src/main/resources/templates/email charset: utf-8
这个配置主要是让freemarker去这两个文件夹下加载所有的模板,具体用到哪个模板需要根据名称来确定,绝对路径有则从绝对路径加载(文末代码中有判断)
yaml中还有配置,即把各个模板的名称都自定义配置到了yml中
需要传入模板名称时直接通过@Value注解从yaml配置中获取即可,如下图
当然也可以不这么写,直接在用到的时候写模板名称即可,在配置里写是为了方便模板改名,看如下代码
打勾的地方是判断有没有从yml获取到绝对路径配置,有则用绝对路径,调用绝对路径查找模板方法
没有则用相对路径加载模板,调用相对路径查找模板方法
downloadPDF方法解释:界面勾选一条,则导出一个pdf,勾选超过一条,则把多个pdf压缩成zip
@Override @SuppressWarnings("unchecked") public void downloadPDF(HttpServletRequest request, HttpServletResponse response, Listids) throws Exception { Map > configMap = (Map >) this.getReviewConfig().getReturnObj(); if(!Objects.equals(1, ids.size())){ pdfZipHandle(ids,response,configMap); }else if(ids.size() == 1){ PTTDetail detail = (PTTDetail) this.get(ids.get(0)).getReturnObj(); response.setContentType("application/octet-stream;charset=utf-8"); response.setHeader("Content-Disposition", "attachment;filename=" + new String((detail.getNumber() + ".pdf").getBytes(), StandardCharsets.ISO_8859_1)); String result = createPDFString(configMap,detail); HtmlConverter.convertToPdf(result, response.getOutputStream()); } } private String createPDFString(Map > configMap,PTTDetail detail) throws Exception { Map map = new HashMap<>(); this.reBuildTableValue(detail, configMap); map.put("sign", this.groupSign(detail)); map.put("ptt", detail); map.put("orj", JsonUtil.jsonToBean(detail.getOrJson(), ORDetail.class)); map.put("reviewMap", detail.getReviews().stream().collect(Collectors.groupingBy(Review::getType))); map.put("dateTime", this.createPDFDate(detail)); map.put("attendees", this.createAttendees(detail)); if(StringUtils.isEmpty(absolutePath)){ return freemarkerComponent.formatTemplate(this.pttDetailNewTemplate, map); } return freemarkerComponent.findTemplateByAbsolutePathAndTemplateName(absolutePath,this.pttDetailNewTemplate, map); } //pdf压缩zip private void pdfZipHandle(List selectIds,HttpServletResponse response,Map > configMap) throws IOException { response.setHeader("Content-Disposition", "attachment;filename=" + new String(( "PTT.zip").getBytes(), StandardCharsets.ISO_8859_1)); ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream()); for (String id : selectIds) { try { PTTDetail detail = (PTTDetail) this.get(id).getReturnObj(); // html字符串 String result = createPDFString(configMap,detail); ZipEntry zipEntry = new ZipEntry(String.format("%s.pdf", detail.getNumber())); zipOutputStream.putNextEntry(zipEntry); PdfDocument pdfDocument = new PdfDocument(new PdfWriter(zipOutputStream), new DocumentProperties()); //设置流不关闭 pdfDocument.setCloseWriter(false); HtmlConverter.convertToPdf(result, pdfDocument, null); } catch (Exception e) { LOGGER.error("down zip pdf error"); } } zipOutputStream.close(); }