使用JavaMail收发Internet邮件

时间:2022-03-26 16:05:04

JavaMail是JavaEE中的一个组件,用来开发邮件客户端程序(MUA)。JavaMail API本身是与具体的消息协议无关的,可以在运行程序之前设置实现具体协议的providers,例如在Sun的mail.jar中提供了一个缺省的文件javamail.default.providers,里面提供了三个协议(imap, smtp和pop3)的实现,其中smtp的provider类是com.sun.mail.smtp.SMTPTransport。如果用户需要开发NNTP协议的客户端程序,可以考虑其他的provider,例如GNU JavaMail。

JavaMail需要JavaSE中的 JavaBeans Activation Framework(JAF)才能运行,JAF位于package: javax.activation.*。这是因为接口Part/MimePart(实现类包括 Message,MimeMessage,BodyPart和MimeBodyPart)需要javax.activation.DataHandler来处理邮件的内容。


********Message的结构********

javax.mail.Message类是一个抽象类,针对Internet邮件,JavaMail中提供了一个具体类javax.mail.internet.MimeMessage。

一条邮件消息Message包含header和content两部分。

header包含Part接口中定义的通用头属性和实现类(比如Message,MimeMessage)中定义的头属性。例如MimeMessage的setHeader("X-Mailer","ljsspace")设置Internet邮件的X-Mailer头,MimeBodyPart的setHeader("Content-ID","a5c8x3f7i98")设置一个BodyPart的Content-ID,以便在其他Part的content中通过cid:a5c8x3f7i98引用该部分内容。

content可以是文本(包含text/plain或text/html),DataHandler或者Multipart/MimeMultipart。例如Part接口的setText(String)用来设置text/plain内容,setContent(String,"text/html")用来设置text/html内容,setContent(Multipart)用来设置Multipart内容,setDataHandler(DataHandler)设置DataHandler内容。另外,为了获取Message的内容,使用getDataHandler和getContent是具有同等效果。

上面提到的Multipart/MimeMultipart类是一个容器,它可以包含多个BodyPart/MimeBodyPart作为该容器中的组成部分,例如一封Internet邮件包含一个text/plain正文和一个附件,正文是一个MimeBodyPart,附件也是一个MimeBodyPart,合起来就是一个MimeMultipart,然后该MimeMultipart作为邮件MimeMessage的content。BodyPart也是Part接口的实现,即也是包含header和content两部分,显然BodyPart的content也可以是一个Multipart,甚至更多级的层次结构。


********接收和发送邮件********

接收邮件在JavaMail中使用的术语的是store,对应的Internet邮件协议有pop3和imap等(遵照JavaMail的习惯,协议名称一律用小写),每一个store中可以能有多有folders(一般情况下pop3中只支持INBOX这一个folder)。发送邮件在JavaMail中使用的术语是transport,对应的Internet邮件协议有smtp等。

在收发邮件之前需要建立一个Session对象,它是一个Factory类,可以生成收发邮件的Store和Transport对象。在smtp协议中,大部分MTA需要用邮件发送人地址的帐号通过认证才允许发邮件,以防open relay产生垃圾邮件,因此需要设置mail.smtp.auth为true,并在构建Session对象时指定一个Authenticator(也可以在transport.connect()时指定认证帐号)。

发送邮件可以用transport.send()或Transport.send(),后者是一个静态方法。如果使用静态方法,JavaMail会根据接收人的邮件地址构造transport对象来发送邮件,这是在后台自动完成的。另外Transport.send有两个重载方法,Transport.send(MimeMessage)根据message中的接收人列表发送邮件,Transport(MimeMessage,InternetAddress[])忽略message中的接收人列表,而直接按第二个参数的地址列表发送邮件,利用这个特点可以给邮件组(mailing list)发送邮件,参考以下代码实现。

接收邮件在JavaMail中使用一个lightweight的类MimeMessage,例如在调用inbox.getMessages()之后,并不是立即将有所有的邮件头和内容全部从store中读取过来,只有在需要的时候才读取。为此,JavaMail还提供了一个FetchProfile类,可以从store中获取所有邮件的部分头信息,而不必读取邮件内容。


********总结********

JavaMail是一个独立于具体协议的邮件或消息框架,除了上面介绍的核心内容之外,JavaMail还支持类似于Swing/AWT中的事件响应机制,而且还有一个专门的package(javax.mail.search)用来根据过滤条件从folder中检索邮件。使用JavaMail可以较轻松地构建一个跨平台的Internet邮件客户端程序,或者在企业环境下开发出邮件或消息子系统。

参考:JavaMail 1.4: http://www.oracle.com/technetwork/java/javamail/index.html

代码:

 

import java.io.File;
import java.io.InputStream;
import java.util.Date;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.Authenticator;
import javax.mail.FetchProfile;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

/**
*
* JavaMail examples: send and fetch mail messages
*
* Copyright (c) 2011 ljs (http://blog.csdn.net/ljsspace/)
* Licensed under GPL (http://www.opensource.org/licenses/gpl-license.php)
*
* @author ljs
* 2011-10-10
*
*/
public class JavaMail {
/**
* send a message to a mailing list (e.g. java-list@ljsspace.org). The actual receivers (e.g. toAddr)
* are specified at the last moment when transport.send is called.
*/
public void sendMail(String host, final String user,
final String password,
String fromAddr,String toAddr,String subject,
String msgText,File attachment,String filename) {
//Transport transport = null;
try {
Properties props = System.getProperties(); //use system properties
props.put("mail.debug", "true"); //or: session.setDebug(true)
props.put("mail.transport.protocol", "smtp");
props.put("mail.smtp.host", host);
props.put("mail.smtp.user", user);

//if SMTP authentication is required, we must set this property
props.put("mail.smtp.auth", "true");


Authenticator auth = new Authenticator() {
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(user, password);
}
};
Session session = Session.getInstance(props, auth);//use authentication
// session = Session.getDefaultInstance(props, null);

MimeMessage msg = new MimeMessage(session);

msg.setFrom(new InternetAddress(fromAddr));
//InternetAddress[] toAddress = {new InternetAddress(toAddr)};
//msg.setRecipients(Message.RecipientType.TO, toAddress);
String mailingListAddr = "java-list@ljsspace.org";
msg.addRecipient(Message.RecipientType.TO, new InternetAddress(mailingListAddr,"Java List"));
msg.setSubject(subject);


msg.setHeader("X-Mailer", "ljsspace-javamail");
msg.setSentDate(new Date());

//create a multipart as the content
MimeMultipart multipart = new MimeMultipart();

MimeBodyPart mbp1 = new MimeBodyPart();
mbp1.setText(msgText);
multipart.addBodyPart(mbp1);

//attachment
DataSource fileDS = new FileDataSource(attachment);
DataHandler fileDH = new DataHandler(fileDS);
MimeBodyPart file_attachment = new MimeBodyPart();
file_attachment.setDataHandler(fileDH);
file_attachment.setFileName(filename);
multipart.addBodyPart(file_attachment);

//set content for the message; the content is a multipart object
msg.setContent(multipart);
//msg.saveChanges(); //unnecessary: Transport.send will call saveChanges

//transport = session.getTransport();
//transport.connect();
//transport.send(msg);

InternetAddress[] toAddress = {new InternetAddress(toAddr)};
//Transport.send(msg); //only send to message's getRecipients()
Transport.send(msg,toAddress); //ignore message's getRecipients()
} catch (Exception e) {
e.printStackTrace();
}
//finally{
//try {
//transport.close();
//} catch (Exception ex) {
//ex.printStackTrace();
//}
//}
}

/**
* Fetch messages from the INBOX store
* @param preview If true, display all messages' profiles; otherwise, display the contents of the first two new messages
*/
public void fetchMail(boolean preview,String host, final String user,
final String password) {
Store store = null;
Folder inbox = null;
try {
Properties props = System.getProperties();
props.put("mail.debug", "true");
props.put("mail.store.protocol", "pop3"); //or store like imap
props.put("mail.pop3.user", user); //no password!
props.put("mail.pop3.host", host);

Authenticator auth = new Authenticator() {
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(user, password);
}
};
Session session = Session.getDefaultInstance(props, auth);

// Get hold of a POP3 message store using session (a factory object)
store = session.getStore(); //the same as session.getStore("pop3"). Since we've set mail.store.protocol property, we just call session.getStore().
// store.connect(host, user, password); //use authenticator instead
store.connect(); //connect using session's authenticator

// inbox = store.getDefaultFolder(); //the root folder in the default namespace
inbox = store.getFolder("INBOX"); //the only folder in POP3
inbox.open(inbox.READ_WRITE);

// Get the messages (a light-weight operation)
Message[] msgs = inbox.getMessages();

if(preview){
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.ENVELOPE); //fetch subject, from, etc.
fp.add("X-mailer"); //fetch x-mailer
inbox.fetch(msgs, fp);

for (int i = 0; i < inbox.getMessageCount(); i++) {
displayMessageProfile(msgs[i]);
}
}else{
//read the first 2 messages only (from the newest to the oldest)
for (int i = inbox.getMessageCount()-1; i>=0 && i > inbox.getMessageCount() - 3; i--) {
processMessage(msgs[i]);
//msgs[i].setFlag(Flags.Flag.DELETED, true);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (inbox != null)
inbox.close(true); //expunge the deleted message
if (store != null)
store.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}

// display a light-weight message's profile
private void displayMessageProfile(Message message) {
try {
System.out.println(message.getFrom()[0] + "\t" + message.getSubject());
} catch (MessagingException e) {
e.printStackTrace();
}
}
private void processMessage(Message message) {
try {
System.out.println("*******" + message.getSubject() + "*******");
readPart(message);
} catch (MessagingException e) {
e.printStackTrace();
}
}

private void readPart(Part p) {
try {
Object content = p.getContent();
if(content instanceof String){
System.out.println(content);
}else if (content instanceof Multipart) {
Multipart mp = (Multipart) content;
int count = mp.getCount();
for (int i = 0; i < count; i++) {
Part bodyPart = mp.getBodyPart(i); //or check: boyPart.getDisposition()
readPart(bodyPart); //recursion
}
}else if (content instanceof InputStream) {
System.out.println("****found inputstream as a part's content****");
}else{
System.out.println("****unknown type****");
}
} catch (Exception e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
//test store: pop3
String pop3host="pop.126.com";
String pop3user="abc"; //test account
String pop3pass="******";
JavaMail mailer = new JavaMail();
mailer.fetchMail(true,pop3host, pop3user, pop3pass);


//test transport: smtp
String smtphost="smtp.126.com";
String smtpuser="abc"; //test account
String smtppass="******";
String fromAddr="abc@126.com";
String toAddr="someone@somewhere.com";
String subject="A Test Message sent via JavaMail";
String msgText = "Please see the attachment for details.";
File attachment = new File("/tmp/test.pdf");
String filename = "test.pdf";
mailer.sendMail(smtphost, smtpuser,smtppass,
fromAddr,toAddr,subject,
msgText,attachment,filename);
}

}