Hôm nay chúng ta sẽ tìm hiểu về một ví dụ dùng JavaMail để gửi email trong các chương trình Java. Đây là một trong những tác vụ phổ biến trong các ứng dụng thực tế, cũng là lý do Java cung cấp một API mạnh mẽ như vậy để chúng ta có thể sử dụng để gửi email thông qua máy chủ SMTP. JavaMail hỗ trợ cả TLS và SSL trong việc xác thực email.

Ví dụ dùng JavaMail
Hôm nay, chúng ta sẽ học cách sử dụng JavaMail API để gửi email qua SMTP server trong cả trường hợp không cần xác thực và xác thực với TLS và SSL, cũng như cách gửi file đính kèm và đính kèm hình ảnh để sử dụng trong nội dung email.
Đối với xác thực TLS và SSL, ta sử dụng GMail SMTP server vì nó hỗ trợ cả hai giao thức. Bạn cũng có thể chọn sử dụng một máy chủ mailJava tùy theo nhu cầu của mình.
JavaMail API không phải là một phần của JDK chuẩn, vì vậy bạn sẽ cần tải nó từ trang web chính thức. Hãy tải xuống phiên bản mới nhất của bản reference implementation (triển khai tham chiếu) từ đó và đưa nó vào build path của dự án. Tên file jar sẽ là javax.mail.jar. Nếu dự án của bạn dùng Maven, chỉ cần thêm dependency dưới đây vào nó:
dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.5.5</version>
</dependency>
Để gửi email ta cần thực hiện các bước sau:
- Tạo đối tượng
javax.mail.Session. - Tạo đối tượng
javax.mail.internet.MimeMessage. Chúng ta cần thiết lập các thuộc tính khác nhau cho đối tượng này, chẳng hạn như địa chỉ email người nhận, chủ đề email, email trả lời (Reply-To), nội dung email, file đính kèm, v.v. - Sử dụng
javax.mail.Transportđể gửi email.
Logic để tạo session khác nhau tùy thuộc vào loại máy chủ SMTP. Ví dụ, nếu nó không yêu cầu bất kỳ xác thực nào, chúng ta có thể tạo đối tượng Session với một vài thuộc tính đơn giản. Trong khi nếu nó yêu cầu xác thực TLS hoặc SSL, thì logic tạo Session sẽ khác.
Vì vậy, ở bài này ta sẽ tạo một utility class với một số phương thức tiện ích để gửi email, và sau đó ta sẽ sử dụng chúng với các SMTP server khác nhau.
Ví dụ chương trình dùng JavaMail
Class EmailUtil của chúng ta vốn có một phương thức duy nhất để gửi email như bên dưới. Nó yêu cầu javax.mail.Session và một số trường bắt buộc khác làm tham số.
Để giữ cho nó đơn giản, một số tham số được gán cố định. Nhưng bạn có thể tùy ý mở rộng phương thức này để truyền chúng vào hoặc đọc chúng từ các file config.
package com.journaldev.mail;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
public class EmailUtil {
/**
* Utility method to send simple HTML email
* @param session
* @param toEmail
* @param subject
* @param body
*/
public static void sendEmail(Session session, String toEmail, String subject, String body){
try
{
MimeMessage msg = new MimeMessage(session);
//set message headers
msg.addHeader("Content-type", "text/HTML; charset=UTF-8");
msg.addHeader("format", "flowed");
msg.addHeader("Content-Transfer-Encoding", "8bit");
msg.setFrom(new InternetAddress("no_reply@example.com", "NoReply-JD"));
msg.setReplyTo(InternetAddress.parse("no_reply@example.com", false));
msg.setSubject(subject, "UTF-8");
msg.setText(body, "UTF-8");
msg.setSentDate(new Date());
msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmail, false));
System.out.println("Message is ready");
Transport.send(msg);
System.out.println("EMail Sent Successfully!!");
}
catch (Exception e) {
e.printStackTrace();
}
}
}
Lưu ý rằng ta đang thiết lập một số header property (thuộc tính tiêu đề) trong MimeMessage. Chúng được các email client sử dụng để render và hiển thị email một cách chính xác. Phần còn lại của chương trình rất đơn giản và dễ hiểu.
Bây giờ, chúng ta hãy tạo chương trình để gửi email không cần xác thực.
Gửi email không cần xác thực trong Java bằng SMTP
package com.journaldev.mail;
import java.util.Properties;
import javax.mail.Session;
public class SimpleEmail {
public static void main(String[] args) {
System.out.println("SimpleEmail Start");
String smtpHostServer = "smtp.example.com";
String emailID = "email_me@example.com";
Properties props = System.getProperties();
props.put("mail.smtp.host", smtpHostServer);
Session session = Session.getInstance(props, null);
EmailUtil.sendEmail(session, emailID,"SimpleEmail Testing Subject", "SimpleEmail Testing Body");
}
}
Lưu ý rằng ta đang sử dụng Session.getInstance() để lấy đối tượng Session bằng cách truyền vào đối tượng Properties. Chúng ta cần đặt thuộc tính mail.smtp.host với host của SMTP server. Nếu SMTP server không chạy trên port mặc định (25), thì bạn cũng sẽ cần đặt thuộc tính mail.smtp.port.
Chỉ cần chạy chương trình trên với SMTP server không yêu cầu xác thực của bạn và đặt ID email người nhận là ID email của chính bạn, bạn sẽ nhận được email ngay lập tức. Chương trình này siêu đơn giản, dễ hiểu và hoạt động tốt. Nhưng trong thực tế, hầu hết các SMTP server đều sử dụng một số loại xác thực như TLS hoặc SSL. Vì vậy, bây giờ chúng ta sẽ xem cách tạo đối tượng Session cho các giao thức này.
Gửi email với xác thực TLS trong Java SMTP
package com.journaldev.mail;
import java.util.Properties;
import javax.mail.Authenticator;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
public class TLSEmail {
/**
Outgoing Mail (SMTP) Server
requires TLS or SSL: smtp.gmail.com (use authentication)
Use Authentication: Yes
Port for TLS/STARTTLS: 587
*/
public static void main(String[] args) {
final String fromEmail = "myemailid@gmail.com"; //requires valid gmail id
final String password = "mypassword"; // correct password for gmail id
final String toEmail = "myemail@yahoo.com"; // can be any email id
System.out.println("TLSEmail Start");
Properties props = new Properties();
props.put("mail.smtp.host", "smtp.gmail.com"); //SMTP Host
props.put("mail.smtp.port", "587"); //TLS Port
props.put("mail.smtp.auth", "true"); //enable authentication
props.put("mail.smtp.starttls.enable", "true"); //enable STARTTLS
//create Authenticator object to pass in Session.getInstance argument
Authenticator auth = new Authenticator() {
//override the getPasswordAuthentication method
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(fromEmail, password);
}
};
Session session = Session.getInstance(props, auth);
EmailUtil.sendEmail(session, toEmail,"TLSEmail Testing Subject", "TLSEmail Testing Body");
}
}
Vì ta đang sử dụng máy chủ SMTP của GMail mà ai cũng có thể truy cập được, bạn có thể đặt các biến chính xác trong chương trình trên và tự mình chạy thử.
Ví dụ Java SMTP với xác thực SSL
package com.journaldev.mail;
import java.util.Properties;
import javax.mail.Authenticator;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
public class SSLEmail {
/**
Outgoing Mail (SMTP) Server
requires TLS or SSL: smtp.gmail.com (use authentication)
Use Authentication: Yes
Port for SSL: 465
*/
public static void main(String[] args) {
final String fromEmail = "myemailid@gmail.com"; //requires valid gmail id
final String password = "mypassword"; // correct password for gmail id
final String toEmail = "myemail@yahoo.com"; // can be any email id
System.out.println("SSLEmail Start");
Properties props = new Properties();
props.put("mail.smtp.host", "smtp.gmail.com"); //SMTP Host
props.put("mail.smtp.socketFactory.port", "465"); //SSL Port
props.put("mail.smtp.socketFactory.class",
"javax.net.ssl.SSLSocketFactory"); //SSL Factory Class
props.put("mail.smtp.auth", "true"); //Enabling SMTP Authentication
props.put("mail.smtp.port", "465"); //SMTP Port
Authenticator auth = new Authenticator() {
//override the getPasswordAuthentication method
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(fromEmail, password);
}
};
Session session = Session.getDefaultInstance(props, auth);
System.out.println("Session created");
EmailUtil.sendEmail(session, toEmail,"SSLEmail Testing Subject", "SSLEmail Testing Body");
EmailUtil.sendAttachmentEmail(session, toEmail,"SSLEmail Testing Subject with Attachment", "SSLEmail Testing Body with Attachment");
EmailUtil.sendImageEmail(session, toEmail,"SSLEmail Testing Subject with Image", "SSLEmail Testing Body with Image");
}
}
Chương trình này gần giống với kiểu xác thực TLS ở trên, chỉ có một vài thuộc tính là khác nhau. Như bạn có thể thấy, ta đang gọi một số phương thức khác từ class EmailUtil để gửi file đính kèm và hình ảnh trong email nhưng ta chưa định nghĩa chúng. Thực ra, chúng tôi đã cố tình giữ chúng lại để trình bày sau và giữ cho phần đầu của hướng dẫn đơn giản hơn.
Ví dụ gửi email trong Java với file đính kèm bằng JavaMail
Để gửi một file dưới dạng đính kèm, chúng ta cần tạo một đối tượng javax.mail.internet.MimeBodyPart và javax.mail.internet.MimeMultipart. Đầu tiên, thêm phần body cho phần tin nhắn text trong email, và sau đó sử dụng FileDataSource để đính kèm file vào phần thứ hai của multipart body. Phương thức đó sẽ trông như thế này:
/**
* Utility method to send email with attachment
* @param session
* @param toEmail
* @param subject
* @param body
*/
public static void sendAttachmentEmail(Session session, String toEmail, String subject, String body){
try{
MimeMessage msg = new MimeMessage(session);
msg.addHeader("Content-type", "text/HTML; charset=UTF-8");
msg.addHeader("format", "flowed");
msg.addHeader("Content-Transfer-Encoding", "8bit");
msg.setFrom(new InternetAddress("no_reply@example.com", "NoReply-JD"));
msg.setReplyTo(InternetAddress.parse("no_reply@example.com", false));
msg.setSubject(subject, "UTF-8");
msg.setSentDate(new Date());
msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmail, false));
// Create the message body part
BodyPart messageBodyPart = new MimeBodyPart();
// Fill the message
messageBodyPart.setText(body);
// Create a multipart message for attachment
Multipart multipart = new MimeMultipart();
// Set text message part
multipart.addBodyPart(messageBodyPart);
// Second part is attachment
messageBodyPart = new MimeBodyPart();
String filename = "abc.txt";
DataSource source = new FileDataSource(filename);
messageBodyPart.setDataHandler(new DataHandler(source));
messageBodyPart.setFileName(filename);
multipart.addBodyPart(messageBodyPart);
// Send the complete message parts
msg.setContent(multipart);
// Send message
Transport.send(msg);
System.out.println("EMail Sent Successfully with attachment!!");
}catch (MessagingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
Chương trình có thể trông phức tạp nhưng thực ra nó khá đơn giản. Chỉ cần tạo một body part cho phần tin nhắn text và một body part khác cho file đính kèm, sau đó thêm tất cả vào multipart. Bạn cũng có thể mở rộng phương thức này để đính kèm nhiều file cùng một lúc
Ví dụ gửi email kèm hình ảnh bằng JavaMail
/**
* Utility method to send image in email body
* @param session
* @param toEmail
* @param subject
* @param body
*/
public static void sendImageEmail(Session session, String toEmail, String subject, String body){
try{
MimeMessage msg = new MimeMessage(session);
msg.addHeader("Content-type", "text/HTML; charset=UTF-8");
msg.addHeader("format", "flowed");
msg.addHeader("Content-Transfer-Encoding", "8bit");
msg.setFrom(new InternetAddress("no_reply@example.com", "NoReply-JD"));
msg.setReplyTo(InternetAddress.parse("no_reply@example.com", false));
msg.setSubject(subject, "UTF-8");
msg.setSentDate(new Date());
msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmail, false));
// Create the message body part
BodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setText(body);
// Create a multipart message for attachment
Multipart multipart = new MimeMultipart();
// Set text message part
multipart.addBodyPart(messageBodyPart);
// Second part is image attachment
messageBodyPart = new MimeBodyPart();
String filename = "image.png";
DataSource source = new FileDataSource(filename);
messageBodyPart.setDataHandler(new DataHandler(source));
messageBodyPart.setFileName(filename);
//Trick is to add the content-id header here
messageBodyPart.setHeader("Content-ID", "image_id");
multipart.addBodyPart(messageBodyPart);
//third part for displaying image in the email body
messageBodyPart = new MimeBodyPart();
messageBodyPart.setContent("<h1>Attached Image</h1>" +
"<img src='cid:image_id'>", "text/html");
multipart.addBodyPart(messageBodyPart);
//Set the multipart message to the email message
msg.setContent(multipart);
// Send message
Transport.send(msg);
System.out.println("EMail Sent Successfully with image!!");
}catch (MessagingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
Vì chúng ta có thể tạo body của tin nhắn email dạng HTML, nếu file hình ảnh được đặt tại một vị trí nào đó trên server, chúng ta có thể sử dụng thẻ img để hiển thị chúng trong nội dung email. Nhưng đôi khi chúng ta muốn đính kèm hình ảnh vào email và sau đó hiển thị nó ngay trong trong nội dung email. Chắc hẳn bạn đã thấy nhiều email có hình ảnh đính kèm và trong chính phần nội dung cũng hiển thị các hình ảnh đó.
Mẹo ở đây là ta sẽ đính kèm file hình ảnh giống như bất kỳ file đính kèm nào khác, sau đó đặt header Content-ID cho file hình ảnh và sử dụng cùng content ID đó trong phần nội dung email với <img src='cid:image_id'>.
/**
* Utility method to send image in email body
* @param session
* @param toEmail
* @param subject
* @param body
*/
public static void sendImageEmail(Session session, String toEmail, String subject, String body){
try{
MimeMessage msg = new MimeMessage(session);
msg.addHeader("Content-type", "text/HTML; charset=UTF-8");
msg.addHeader("format", "flowed");
msg.addHeader("Content-Transfer-Encoding", "8bit");
msg.setFrom(new InternetAddress("no_reply@example.com", "NoReply-JD"));
msg.setReplyTo(InternetAddress.parse("no_reply@example.com", false));
msg.setSubject(subject, "UTF-8");
msg.setSentDate(new Date());
msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmail, false));
// Create the message body part
BodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setText(body);
// Create a multipart message for attachment
Multipart multipart = new MimeMultipart();
// Set text message part
multipart.addBodyPart(messageBodyPart);
// Second part is image attachment
messageBodyPart = new MimeBodyPart();
String filename = "image.png";
DataSource source = new FileDataSource(filename);
messageBodyPart.setDataHandler(new DataHandler(source));
messageBodyPart.setFileName(filename);
//Trick is to add the content-id header here
messageBodyPart.setHeader("Content-ID", "image_id");
multipart.addBodyPart(messageBodyPart);
//third part for displaying image in the email body
messageBodyPart = new MimeBodyPart();
messageBodyPart.setContent("<h1>Attached Image</h1>" +
"<img src='cid:image_id'>", "text/html");
multipart.addBodyPart(messageBodyPart);
//Set the multipart message to the email message
msg.setContent(multipart);
// Send message
Transport.send(msg);
System.out.println("EMail Sent Successfully with image!!");
}catch (MessagingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
Các mẹo khắc phục lỗi JavaMail API
- Lỗi
javax.mail.MessagingExceptionxảy ra khi hệ thống của bạn không thể phân giải địa chỉ IP cho SMTP server. Có thể là do hostname sai hoặc máy chủ không thể truy cập được từ nó. Ví dụ, địa chỉ chính xác của máy chủ GMail SMTP là smtp.gmail.com và ta sẽ gặp lỗi nếu dùng smtp.google.com. Nếu hostname chắc chắn đúng, hãy thử dùng lệnhpingđể kiểm tra liệu hệ thống của bạn có thể truy cập được nó.
pankaj@Pankaj:~/CODE$ ping smtp.gmail.com
PING gmail-smtp-msa.l.google.com (74.125.129.108): 56 data bytes
64 bytes from 74.125.129.108: icmp_seq=0 ttl=46 time=38.308 ms
64 bytes from 74.125.129.108: icmp_seq=1 ttl=46 time=42.247 ms
64 bytes from 74.125.129.108: icmp_seq=2 ttl=46 time=38.164 ms
64 bytes from 74.125.129.108: icmp_seq=3 ttl=46 time=53.153 ms
- Nếu chương trình của bạn bị kẹt khi gọi phương thức
Transport.send(), hãy kiểm tra xem SMTP port có chính xác không. Nếu port đúng, hãy sử dụngtelnetđể xác minh rằng nó có thể truy cập được từ máy của bạn (giốn như output như dưới đây):
pankaj@Pankaj:~/CODE$ telnet smtp.gmail.com 587
Trying 2607:f8b0:400e:c02::6d...
Connected to gmail-smtp-msa.l.google.com.
Escape character is '^]'.
220 mx.google.com ESMTP sx8sm78485186pab.5 - gsmtp
HELO
250 mx.google.com at your service
Tổng kết
Ở trên là những ví dụ sử dụng JavaMail để gửi mail trong Java bằng SMTP server với các giao thức xác thực khác nhau, cũng như với file đính kèm và hình ảnh. Chúng tôi hy vọng nó sẽ giúp bạn giải quyết các yêu cầu gửi email trong các chương trình Java của bạn.