JavaMail是個歷史悠久的寄送電子郵件Email套件,由Sun公司所開發(Oracle),因為電子郵件數年來並沒有多大的改變,所以一直都是十分方便的一個套件,使用方法可參考Java程式Mail、EDM(電子型錄)寄送這篇。
而現在有了另一個framework,是由Apache組職所提供的開放原始碼套件Apache Commons email,使用上更為簡單,而且支援度也很高,像是gmail要使用javamail來寄送,就需要設定繁雜的Sectury項目,而Apache commons email就顯的簡單許多,底下的範例就是使用gmail的smtp.gmail.com來當SMTP服務寄送信件的,我把它應用在之前的留言板,當有人留言時,就可以馬上接到信件的通知。
這範例需要的lib是Apache commons email,寄件的方法,需先建立一個Email主體,可以是SimpleEmail,寄送純文字,也可以是HtmlEmail,寄送html內容,也可以對Email主體進行attach附加文件的功能。
比較特別的是gmail需要使用到TLS或SSL加密,所以需要對Email主體設定setTSL為真及利用setAuthenticator來告知道驗證用的帳號及密碼。
原始碼如下:
mail.properties
host=smtp.gmail.com
port=587
[email protected]
[email protected]
pwd=tyu123
sleep=100
fromName=yslifes Board
Sender
這是寄Mail的Thread,寫成線程可以不用等待SMTP回報傳送結果,所以寄送成功與否並不會回傳到前端,如此可以減少前端等待寄Mail的時間。
package yslifes.mail.thread; import java.util.*; import org.apache.commons.mail.DefaultAuthenticator; import org.apache.commons.mail.EmailException; import org.apache.commons.mail.HtmlEmail; public class Sender extends java.lang.Thread { private String content; private org.apache.log4j.Logger log; private String subject; private String to; private static Properties p = null; public static void loadProperties() { try { p = new Properties(); // log.info(this.getClass().getResource("/mail.properties").getPath()); System.out.println(Sender.class.getResource("/mail.properties")); java.io.BufferedReader reader = new java.io.BufferedReader( new java.io.FileReader(Sender.class.getResource( "/mail.properties").getFile())); ; String str = null; while ((str = reader.readLine()) != null) { String sp[] = str.split("="); p.put(sp[0].trim(), sp[1].trim()); System.out.println(sp[0].trim() + "," + sp[1].trim()); } reader.close(); } catch (java.io.IOException e) { } } public Sender(String to, String subject, String content) { super(); if (p == null) loadProperties(); this.content = content; this.to = to; log = org.apache.log4j.Logger.getLogger(this.getClass()); // this.p = p; this.subject = subject; } public void run() { HtmlEmail email = new HtmlEmail(); try { String host = p.getProperty("host"); String from = p.getProperty("from"); String from_name = p.getProperty("fromName"); String user = p.getProperty("user"); String pwd = p.getProperty("pwd"); String port = p.getProperty("port"); email.setTLS(true); // 是否TLS檢驗,某些email需要TLS安全檢驗,同理有SSL檢驗 // email.setSSL(true); email.setHostName(host); email.setAuthenticator(new DefaultAuthenticator(user, pwd)); // 使用者帳號及密碼 //email.setAuthentication(user, user); // email.setSslSmtpPort(port); email.setSmtpPort(Integer.parseInt(port)); email.setFrom(from, from_name); email.setCharset("utf-8"); email.addTo(to); // 接收方 // email.addCc("[email protected]"); //副本 // email.addBcc("[email protected]"); //密件副本 email.setSubject(subject); // 標題 // email.setTextMsg("Your email client does not support HTML messages"); email.setHtmlMsg(content); // 内容 email.send(); } catch (EmailException e) { e.printStackTrace(); log.info(e); } catch (Exception e) { e.printStackTrace(); log.info(e); } } public static void main(String args[]) { (new Sender("[email protected]","test","test")).start(); } }
如果想把這個範例使用到之前範例留言板,可以把程式碼加到postAction.jsp裡。
ps.setString(++idx, yslifes.tools.StringTool.reFormat(mail)); ps.setString(++idx, yslifes.tools.StringTool.reFormat(tel)); ps.setString(++idx, desc); logger.debug("PostAction:" + desc); ps.executeUpdate(); (new yslifes.mail.thread.Sender("[email protected]", "有人留言","<html><body>標題:"+title+"<br/>內容:"+desc+"</body></html>")).start();
2014/12/27備註
- 還需要一個mail.jar在javamail專案裡有
- http://www.oracle.com/technetwork/java/javamail/index-138643.html
- 另外新版的apache HtmlMail不建議使用setTSL(true);可改用setStartTLSEnabled(true);
2016/11/30補充
上面的範例目前應該會出現
org.apache.commons.mail.EmailException: Sending the email to the following server failed : smtp.gmail.com:587 at org.apache.commons.mail.Email.sendMimeMessage(Email.java:1410) at org.apache.commons.mail.Email.send(Email.java:1437) at com.creations.utils.MailUtils.SendMail(MailUtils.java:103) at com.creations.utils.MailUtils.main(MailUtils.java:153) Caused by: javax.mail.MessagingException: Could not convert socket to TLS; nested exception is: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at com.sun.mail.smtp.SMTPTransport.startTLS(SMTPTransport.java:1907) at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:666) at javax.mail.Service.connect(Service.java:317) at javax.mail.Service.connect(Service.java:176) at javax.mail.Service.connect(Service.java:125) at javax.mail.Transport.send0(Transport.java:194) at javax.mail.Transport.send(Transport.java:124) at org.apache.commons.mail.Email.sendMimeMessage(Email.java:1400) ... 3 more Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.ssl.Alerts.getSSLException(Alerts.java:192) at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949) at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302) at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296) at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1497) at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:212) at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979) at sun.security.ssl.Handshaker.process_record(Handshaker.java:914) at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062) at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387) at com.sun.mail.util.SocketFetcher.configureSSLSocket(SocketFetcher.java:549) at com.sun.mail.util.SocketFetcher.startTLS(SocketFetcher.java:486) at com.sun.mail.smtp.SMTPTransport.startTLS(SMTPTransport.java:1902) ... 10 more Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:387) at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292) at sun.security.validator.Validator.validate(Validator.java:260) at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324) at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229) at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124) at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1479) ... 20 more Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:145) at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:131) at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280) at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382) ... 26 more
這裡提供另一個範例
import javax.mail.MessagingException; import javax.mail.internet.AddressException; import java.util.Properties; import javax.mail.Message; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import org.apache.commons.mail.DefaultAuthenticator; import org.apache.commons.mail.HtmlEmail; import org.apache.log4j.Logger; public class MailUtils { private static Properties mailServerProperties = null ; private static Logger logger = Logger.getLogger(MailUtils.class); public static void generateAndSendEmail(String[] to,String subject,String html) throws AddressException, MessagingException { if(mailServerProperties==null){ // Step1 logger.info("\n 1st ===> setup Mail Server Properties.."); mailServerProperties = System.getProperties(); mailServerProperties.put("mail.smtp.port", "587"); mailServerProperties.put("mail.smtp.ssl.trust", "smtp.gmail.com"); mailServerProperties.put("mail.smtp.auth", "true"); mailServerProperties.put("mail.smtp.starttls.enable", "true"); logger.info("Mail Server Properties have been setup successfully.."); } // Step2 logger.info("\n\n 2nd ===> get Mail Session.."); Session getMailSession = Session.getDefaultInstance(mailServerProperties, null); MimeMessage generateMailMessage = new MimeMessage(getMailSession); for(int i = 0 ; i < to.length;i++) generateMailMessage.addRecipient(Message.RecipientType.TO, new InternetAddress(to[i])); //generateMailMessage.addRecipient(Message.RecipientType.CC, new InternetAddress("[email protected]")); generateMailMessage.setSubject(subject); String emailBody = html; generateMailMessage.setContent(emailBody, "text/html; charset=UTF-8"); logger.info("Mail Session has been created successfully.."); // Step3 logger.info("\n\n 3rd ===> Get Session and Send mail"); Transport transport = getMailSession.getTransport("smtp"); // Enter your correct gmail UserID and Password // if you have 2FA enabled then provide App Specific Password transport.connect("smtp.gmail.com",587, "yourGmail account", "yourGmail password"); transport.sendMessage(generateMailMessage, generateMailMessage.getAllRecipients()); transport.close(); } public static void main(String args[]) throws AddressException, MessagingException{ MailUtils.SendMail(new String[]{"[email protected]"}, "測試", "標題<br/>英文ABC 123<br/>111<a href='https://blog.yslifes.com'>點我進入blog</a> "); } }
如果沒有加
mailServerProperties.put("mail.smtp.ssl.trust", "smtp.gmail.com");
會跟上面的範例有一樣的狀況
另外如果帳號有二階段認證的話可以參考這篇
Java MailAPI Example – Send an Email via GMail SMTP (TLS Authentication)
Getting error? How to triage an issue?
- If you’ve turned on
2-Step Verification
for your account, you might need to enter an App password. Important
: If you’re still having problems, visit http://www.google.com/accounts/DisplayUnlockCaptcha and sign in with your Gmail username and password. If necessary, enter the letters in the distorted picture.
我下載了java6,卻下載不成,因為有個程式需要用到java6,所以我嘗試下載,第一次用可以,但用到後面又不小心刪了,但本來想要在下載一次,卻下載不成,請幫忙求解!
可以改用jdk1.8