Javax mail实现模板邮件发送

       最近,在开发工作中有这么的需求,使用模板技术加javax mail发送模板邮件,经历的一系列的蛋疼时刻终于搞定,在这里做下总结,以备以后在遇到类似问题,方便查找。

     使用VelocityEngine + Spring完成模板解析,VelocityTemplateMailProcessor.java:

    

   
/**
 * 处理velocity邮件模板,将template中参数替换为真实值
 * 
 * @author Arthur
 * 
 */
public class VelocityTemplateMailProcessor implements TemplateMailProcessor {

	private final static Logger LOGGER = Logger
			.getLogger(VelocityTemplateMailProcessor.class);

	/**
	 * 使用velocity模板引擎处理邮件模板,支持在velocity等模板文件中对时间和数字进行格式化支持
	 * 
	 * @param templateLocation
	 *            邮件模板路径:例如在src/main/resoures下有一个example.tpl文件,
	 *            则起对应的tamplateLocation为example.tpl
	 * @param charsetEncoding
	 *            读取邮件模板使用的charset
	 * @param attributeMap
	 *            模板中对应占位符的值map
	 * @return
	 */
	public String process(String templateLocation, String charsetEncoding,
			Map<String, Object> attributeMap) {
		// First, volidate attributeMap, make sure "dateTool" & "numberTool" not
		// used by application
		this.validate(attributeMap);

		try {
			Properties prop = new Properties();
			prop.put("file.resource.loader.class",
					"org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
			// 增加date和number格式化支持
			try {
				VelocityTemplateMailProcessor.class.getClassLoader().loadClass(
						"org.apache.velocity.tools.generic.DateTool");
				LOGGER.info("DateTool 'org.apache.velocity.tools.generic.DateTool' found in classpath and support date format");
				DateTool dateTool = new DateTool();
				attributeMap.put("dateTool", dateTool);
			} catch (ClassNotFoundException e) {
				// not exist, just skip
			}

			try {
				VelocityTemplateMailProcessor.class.getClassLoader().loadClass(
						"org.apache.velocity.tools.generic.NumberTool");
				LOGGER.info("NumberTool 'org.apache.velocity.tools.generic.NumberTool' found in classpath and support numberic format");
				NumberTool numberTool = new NumberTool();
				attributeMap.put("numberTool", numberTool);
			} catch (ClassNotFoundException e) {
				// not exist, just skip
			}

			VelocityEngineFactoryBean factoryBean = new VelocityEngineFactoryBean();
			factoryBean.setVelocityProperties(prop);
			VelocityEngine velocityEngine = factoryBean.createVelocityEngine();
			LOGGER.debug("VelocityEngine initialization is ok");

			return VelocityEngineUtils.mergeTemplateIntoString(velocityEngine,
					templateLocation, charsetEncoding, attributeMap);
		} catch (Exception e) {
			LOGGER.error("use VelocityEngine to process mail template["
					+ "templateLocation" + "] error", e);
			throw new ParseTemplateException(e);
		}
	}

	private void validate(Map<String, Object> attributeMap) {
		if (attributeMap.get("dateTool") != null) {
			throw new IllegalArgumentException(
					"dateTool is reserved field not for application, Please make sure it's not used");
		}

		if (attributeMap.get("numberTool") != null) {
			throw new IllegalArgumentException(
					"numberTool is reserved field not for application, Please make sure it's not used");
		}
	}
}

      ok,使用该类完成邮件模板的解析工作,接着发送邮件,邮件发送类这里就不贴出来了;

      使用javax mail发送邮件时最容易遇到的问题就是邮件内容和标题的乱码,对于内容乱码,相信很多人都遇到过,也都知道怎么解决。

       至于标题乱码有点棘手,需要对邮件title进行Base64编码,防止邮件标题中文乱码,但是这里就需要对使用哪个Base64实现类进行选择,

       不幸的是开始楼主选择了Sun的sun.misc.BASE64Encoder进行encode,结果发现有的邮件乱码,有的邮件正常;好蛋疼!!

       经过楼主的排查,翻阅javax mail源码(MimeMessage、MimeUtility.java、PropUtil.java)发现在MimeMessage.java中:

public void setSubject(String subject, String charset)
			throws MessagingException {
	if (subject == null) {
	    removeHeader("Subject");
	} else {
	    try {
		setHeader("Subject", MimeUtility.fold(9,
		    MimeUtility.encodeText(subject, charset, null)));
	    } catch (UnsupportedEncodingException uex) {
		throw new MessagingException("Encoding error", uex);
	    }
	}
    }

    该方法调用到了MimeUtility.fold()方法

 public static String fold(int used, String s) {
	if (!foldText)
	    return s;

	int end;
	char c;
	// 请注意这里
	// Strip trailing spaces and newlines
	for (end = s.length() - 1; end >= 0; end--) {
	    c = s.charAt(end);
	    if (c != ' ' && c != '\t' && c != '\r' && c != '\n')
		break;
	}
	if (end != s.length() - 1)
	    s = s.substring(0, end + 1);

	// 一下省略啦
	......
    }

       该方法遇到""、\t、\r、\n就会将邮件标题截取,剩余部分默认将其作为邮件内容;

       回过头来,再看下sun.misc.BASE64Encoder.encode(),sun的base64实现完全遵循了Base64(RFC2045~RFC2049)规范,将加密后的字符串每76个字符后插入回车换行符,这就导致了邮件标题base64之后不完整,而且邮件内容多了一部分,导致邮件无法被解析展示。

       最后楼主经过测试使用了apache的Base64实现org.apache.commons.codec.binary.Base64,该实现对加密后的Base64字符串不进行任何回车换行的操作。

       OK,这里把解决邮件标题乱码的部分代码贴出来:

String subject = new String(Base64.encodeBase64("subject".getBytes("UTF-8")));
mailMessage.setSubject("=?UTF-8?B?" + subject + "?=");

 

 

   第一次写博客,写的不好还请轻拍:)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值