欢迎访问 生活随笔!

生活随笔

当前位置: 首页 >

使用Velocity作为邮件的模板

发布时间:2024/3/26 55 豆豆
生活随笔 收集整理的这篇文章主要介绍了 使用Velocity作为邮件的模板 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

Velocity 是一个基于Java的模板引擎。它允许任何人使用一种简单但强大的模板语言去引用Java代码中定义的对象。

Velocity的基本常用语法:https://www.cnblogs.com/xiohao/p/5788932.html

最近在做ESL的邮件报警功能,邮件内容包含两个表格,分别填充两种报警内容,需要根据系统的语言设置显示不一样的表头。

核心做法:

package com.zk.mail;import lombok.extern.slf4j.Slf4j; import org.apache.velocity.app.Velocity; import org.apache.velocity.app.VelocityEngine; import org.springframework.stereotype.Component; import org.springframework.ui.velocity.VelocityEngineUtils; import org.springframework.util.StringUtils;import javax.activation.DataHandler; import javax.activation.DataSource; import javax.mail.*; import javax.mail.internet.*; import javax.mail.util.ByteArrayDataSource; import java.io.*; import java.util.*;@Slf4j @Component(value = "mailUtils") public class MailUtils {public static final String HTML_CONTENT = "text/html;charset=UTF-8";public static final String ATTACHMENT_CONTENT = "text/plain;charset=gb2312";private static VelocityEngine velocityEngine = new VelocityEngine();static {Properties properties = new Properties();String basePath = "src/main/resources/mailTemplate/";// 设置模板的路径properties.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, basePath);// 初始花velocity 让设置的路径生效velocityEngine.init(properties);}public <T extends List> void sendEmail(T t1, T t2, String title, String[] to, String[] bcc, String templateName, EmailServerConfig config, Map<String, String> content) {Map map = new HashMap();map.put("priceTagDatas", t1);map.put("ApDatas", t2);map.put("merchantName", content.get("merchantName"));map.put("storeName", content.get("storeName"));map.put("alarmStartTime", content.get("alarmStartTime"));map.put("alarmEndTime", content.get("alarmEndTime"));Email email = new Email.Builder(title, to, null).model(map).templateName(templateName).bcc(bcc).build();sendEmail(email, config);}private void sendEmail(Email email, EmailServerConfig config) {Long startTime = System.currentTimeMillis();// 发件人try {MimeMessage message = this.getMessage(email, config);// 新建一个存放信件内容的BodyPart对象Multipart multiPart = new MimeMultipart();MimeBodyPart mdp = new MimeBodyPart();// 给BodyPart对象设置内容和格式/编码方式setContent(email);mdp.setContent(email.getContent(), HTML_CONTENT);multiPart.addBodyPart(mdp);// 新建一个MimeMultipart对象用来存放BodyPart对象(事实上可以存放多个)if (null != email.getData()) {MimeBodyPart attchment = new MimeBodyPart();ByteArrayInputStream in = new ByteArrayInputStream(email.getData());DataSource fds = new ByteArrayDataSource(in, email.getFileType());attchment.setDataHandler(new DataHandler(fds));attchment.setFileName(MimeUtility.encodeText(email.getFileName()));multiPart.addBodyPart(attchment);if (in != null) {in.close();}}message.setContent(multiPart);message.saveChanges();Transport.send(message);Long endTime = System.currentTimeMillis();log.info("Email sent successfully, consume time:" + (endTime - startTime) / 1000 + "s");} catch (Exception e) {log.error("Error while sending mail.", e);}}private Email setContent(Email email) {if (StringUtils.isEmpty(email.getContent())) {email.setContent("");}if (!StringUtils.isEmpty(email.getTemplateName()) && null != email.getModel()) {String content = VelocityEngineUtils.mergeTemplateIntoString(velocityEngine, email.getTemplateName(), "UTF-8", email.getModel());email.setContent(content);}return email;}private MimeMessage getMessage(Email email, EmailServerConfig config) {MimeMessage message = null;try {if (email.getTo() == null || email.getTo().length == 0 || StringUtils.isEmpty(email.getSubject())) {throw new Exception("Recipient or subject is empty.");}Properties props = new Properties();props.setProperty("mail.smtp.host", config.getMailSmtpHost());props.setProperty("mail.smtp.socketFactory.class", config.getMailSmtpSocketFatoryClass());props.setProperty("mail.smtp.socketFactory.fallback", config.getMailSmtpSocketFatoryFallback());props.setProperty("mail.smtp.port", config.getMailSmtpPort());props.setProperty("mail.smtp.socketFactory.port", config.getMailSmtpSocketFatoryPort());props.setProperty("mail.smtp.auth", config.getMailSmtpAuth());//解决553的问题,用Session.getInstance取代Session.getDefaultInstance // Session mailSession = Session.getDefaultInstance(props, new Authenticator() { // protected PasswordAuthentication getPasswordAuthentication() { // return new PasswordAuthentication(config.getMailSmtpFromAddress(), //config.getMailSmtpAuthPass()); // } // });Session mailSession = Session.getInstance(props, new Authenticator(){@Overrideprotected PasswordAuthentication getPasswordAuthentication() {return new PasswordAuthentication(config.getMailSmtpFromAddress(), config.getMailSmtpAuthPass());}});message = new MimeMessage(mailSession);message.setFrom(new InternetAddress(config.getMailSmtpFromAddress()));for (String mailTo : email.getTo()) {message.addRecipient(Message.RecipientType.TO, new InternetAddress(mailTo));}List<InternetAddress> ccAddress = new ArrayList<>();if (null != email.getBcc()) {for (String mailCC : email.getBcc()) {ccAddress.add(new InternetAddress(mailCC));}message.addRecipients(Message.RecipientType.CC,ccAddress.toArray(new InternetAddress[email.getBcc().length]));}message.setSentDate(new Date());message.setSubject(email.getSubject());} catch (Exception e) {log.error("Error while sending mail." + e.getMessage(), e);}return message;} }

Velocity的模板, 提供不同语言的模板,模板名称带上语言后缀(中文模板:mail_cn.vm)如:

<!DOCTYPE html> <html lang="zh"> <head><META http-equiv=Content-Type content='text/html; charset=UTF-8'><title>Title</title><style type="text/css">table.reference, table.tecspec {border-collapse: collapse;width: 100%;margin-bottom: 4px;margin-top: 4px;}table.reference tr:nth-child(even) {background-color: #fff;}table.reference tr:nth-child(odd) {background-color: #f6f4f0;}table.reference th {color: #fff;background-color: #555;border: 1px solid #555;font-size: 12px;padding: 3px;vertical-align: top;}table.reference td {line-height: 2em;min-width: 24px;border: 1px solid #d4d4d4;padding: 5px;padding-top: 7px;padding-bottom: 7px;vertical-align: top;}.article-body h3 {font-size: 1.8em;margin: 2px 0;line-height: 1.8em;}</style> </head> <body> <h3 style=";">ESL系统报警信息</h3> <div><div>时间: $alarmStartTime 至 $alarmEndTime</div><div>商家名称: $merchantName</div><div>门店名称: $storeName</div><div>报警内容:</div>#if ($priceTagDatas.size() > 0)<table class="reference"><tbody><tr>价签报警</tr><tr><th>价签条码</th><th>商品条码</th><th>商品名称</th><th>报警类型</th><th>报警时间</th></tr>#foreach($element in $priceTagDatas)<tr><td>#if($element.getDeviceMac())$element.getDeviceMac()#end</td><td>#if($element.getItemBarCode())$element.getItemBarCode()#end</td><td>#if($element.getItemName())$element.getItemName()#end</td><td>#if($element.getFaultType())$element.getFaultType()#end</td><td>#if($element.getCreatedTime())$element.getCreatedTime()#end</td></tr>#end</tbody></table>#end#if ($ApDatas.size() > 0)<table class="reference"><tbody><tr>基站报警</tr><tr><th>基站名称</th><th>基站MAC</th><th>报警类型</th><th>报警时间</th><th>状态</th></tr>#foreach($element in $ApDatas)<tr><td>#if($element.getDeviceMac())$element.getDeviceMac()#end</td><td>#if($element.getDeviceMac())$element.getDeviceMac()#end</td><td>#if($element.getFaultType())$element.getFaultType()#end</td><td>#if($element.getCreatedTime())$element.getCreatedTime()#end</td><td>#if($element.getProcessStatus())$element.getProcessStatus()#end</td></tr>#end</tbody></table>#end<div style="float: left; margin-top: 300px;;"><p>系统邮件(请勿回复) | ESL 报警中心</p></div> </div> </body> </html>

发送邮件是在一个定时任务中,定时任务的代码如:

package com.zk.quartz;import com.zk.dao.*; import com.zk.mail.AlarmEmailTitle; import com.zk.mail.EmailServerConfig; import com.zk.mail.MailUtils; import com.zk.model.*; import com.zk.service.MailSenderService; import com.zk.util.DateUtils; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.data.jpa.domain.Specification;import javax.annotation.Resource; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import java.util.*; import java.util.stream.Collectors;/*** Created by zk on 2019/3/28.*/ @Slf4j public class AlarmJob implements Job {@Resourceprivate StoreRepository storeRepository;@Resourceprivate MerchantRepository merchantRepository;@Resourceprivate AgencyAlarmConfigRepository agencyAlarmConfigRepository;@Resourceprivate AlarmRepository alarmRepository;@Resourceprivate MailUtils mailUtils;@Overridepublic void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();List faultTypeList = (List) jobDataMap.get("faultTypeList");String merchantId = (String) jobDataMap.get("merchantId");String storeId = (String) jobDataMap.get("storeId");String sendTO = (String) jobDataMap.get("sendTo");String language = (String) jobDataMap.get("language");String templateName = "mail_" + language + ".vm";List<Alarm> alarmList = alarmRepository.findAll(getSpecification(merchantId, storeId, faultTypeList));if (alarmList.size() == 0) {log.info("Alarm job run without alarms for storeId: " + storeId);return;}String merchantName = merchantRepository.findByMerchantIdAndFlag(merchantId, 1).getMerchantName();String storeName = storeRepository.findByStoreIdAndFlag(storeId, 1).getStoreName();List<Alarm> priceTagAlarmList = alarmList.stream().filter(alarm -> "2".equals(alarm.getAlarmType())).collect(Collectors.toList());List<Alarm> apAlarmList = alarmList.stream().filter(alarm -> "1".equals(alarm.getAlarmType())).collect(Collectors.toList());Date alarmStartTime = alarmList.stream().map(alarm -> DateUtils.stringToDateTime(alarm.getCreatedTime())).min(Comparator.naturalOrder()).get();Date alarmEndTime = alarmList.stream().map(alarm -> DateUtils.stringToDateTime(alarm.getCreatedTime())).max(Comparator.naturalOrder()).get();Map<String, String> content = new HashMap<>(4);content.put("merchantName", merchantName);content.put("storeName", storeName);content.put("alarmStartTime", DateUtils.format(alarmStartTime));content.put("alarmEndTime", DateUtils.format(alarmEndTime));AgencyAlarmConfig agencyAlarmConfig = agencyAlarmConfigRepository.findConfigByAgencyId(merchantId);agencyAlarmConfig.setTestMail(sendTO);String[] toArr = sendTO.split(",");EmailServerConfig config = getEmailServerConfig(agencyAlarmConfig);mailUtils.sendEmail(priceTagAlarmList, apAlarmList, AlarmEmailTitle.getTitleFromLanguage(language), toArr, null, templateName, config, content);for(Alarm alarm : alarmList) {alarm.setHasSent(true);alarmRepository.save(alarm);}}private EmailServerConfig getEmailServerConfig(AgencyAlarmConfig agencyAlarmConfig) {EmailServerConfig config = new EmailServerConfig();config.setMailSmtpHost(agencyAlarmConfig.getSendServer());config.setMailSmtpSocketFatoryClass("javax.net.ssl.SSLSocketFactory");config.setMailSmtpSocketFatoryFallback("false");config.setMailSmtpPort("465");config.setMailSmtpSocketFatoryPort("465");config.setMailSmtpAuth("true");config.setMailSmtpFromAddress(agencyAlarmConfig.getAccount());config.setMailSmtpAuthPass(agencyAlarmConfig.getPassword());return config;}private Specification<Alarm> getSpecification(String merchantId, String storeId, List<String> typeList) {return new Specification<Alarm>() {@Overridepublic Predicate toPredicate(Root<Alarm> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {List<Predicate> predicates = new ArrayList<Predicate>();Predicate predicate = null;if (StringUtils.isNotBlank(merchantId)) {predicate = criteriaBuilder.equal(root.get("merchantId"), merchantId);predicates.add(predicate);}if (StringUtils.isNotBlank(storeId)) {predicate = criteriaBuilder.equal(root.get("storeId"), storeId);predicates.add(predicate);}if (typeList != null && typeList.size() > 0) {CriteriaBuilder.In<String> in = criteriaBuilder.in(root.get("faultType"));for (String type : typeList) {in.value(type);}predicates.add(in);}predicate = criteriaBuilder.isNull(root.get("hasSent"));predicates.add(predicate);return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));}};} }

 

后记:部署后遇到了两个坑

1)Velocity找不到模板文件

在我本地运行的时候并没有这种问题,试了很多种方法,最后只能使用绝对路径,修改MailUtils中velocityEngine的Velocity.FILE_RESOURCE_LOADER_PATH的值:

static {Properties properties = new Properties();// 将basePath修改为服务器上的绝对路径, 并将模板文件上传到该路径下。// String basePath = "src/main/resources/mailTemplate/";String basePath = "/usr/local/esl/";properties.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, basePath);velocityEngine.init(properties);}

问题解决。

2)使用了163邮箱作为测试服务器,遇到了邮件被认为是垃圾邮件的问题:

解决方法:将邮件抄送一份给发送账号,在MailUtils的getMessage方法中,添加以下代码:

List<InternetAddress> ccAddress = new ArrayList<>();// if (null != email.getBcc()) { // for (String mailCC : email.getBcc()) { // ccAddress.add(new InternetAddress(mailCC)); // } // message.addRecipients(Message.RecipientType.CC, // ccAddress.toArray(new InternetAddress[email.getBcc().length])); // }ccAddress.add(new InternetAddress(config.getMailSmtpFromAddress())); message.addRecipients(Message.RecipientType.CC, ccAddress.toArray(new InternetAddress[1]));

成功解决554 DT:SPM问题!

后记2:解决邮件发送中出现553问题

在本地用单测进行邮件发送,都没有问题。但是部署之后,通过前端调用接口的方式,经常会出现553的问题,如:

553意味着mail from和登录的邮箱账号存在不一致的情况,考虑到部署后首次发送是成功的,想到会不会是前一次登录的账号信息被保留下来了,观察代码,mail from和account的信息分别设置如:

Session mailSession = Session.getDefaultInstance(props, new Authenticator() {protected PasswordAuthentication getPasswordAuthentication() {return new PasswordAuthentication(config.getMailSmtpFromAddress(), config.getMailSmtpAuthPass());}});message = new MimeMessage(mailSession);message.setFrom(new InternetAddress(config.getMailSmtpFromAddress()));

跟进到Session.getDefaultInstance的代码发现,defaultSession是一个类静态变量,首次登录一个邮箱后这个session就会被保留下来,导致和后续的测试账户不匹配从而报错553。找到原因之后,使用Session.getInstance()方法取代Session.getDefaultInstance()去重新new一个session,问题得到解决。

 

 

转载于:https://my.oschina.net/u/4042451/blog/3066884

总结

以上是生活随笔为你收集整理的使用Velocity作为邮件的模板的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。