Java寄送電子郵件-使用gmail帳號

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.

2 thoughts to “Java寄送電子郵件-使用gmail帳號”

  1. 我下載了java6,卻下載不成,因為有個程式需要用到java6,所以我嘗試下載,第一次用可以,但用到後面又不小心刪了,但本來想要在下載一次,卻下載不成,請幫忙求解!

發表迴響