背景
近期需要实现一个邮件客户端的项目,类似Foxmail,outlook客户端。但项目只做消息转发,不需要完整实现。
邮件协议
常用的电子邮件协议有SMTP、POP3、IMAP4,它们都隶属于TCP/IP协议簇,默认状态下,分别通过TCP端口25、110和143建立连接。
SMTP即简单邮件传输协议,SMTP邮件服务器是遵循SMTP协议的发送邮件的服务器。如QQ:smtp.qq.com、163:smtp.163.com
POP即邮局协议,可以查询邮件,查询是否有新邮件,可以删除邮件。POP3是POP协议的第三个版本。如QQ:pop.qq.com、163:pop.163.com。
IMAP即互联网信息访问协议,此协议优于POP协议,拥有POP协议的功能,克服了POP协议的缺点。这里不做过多介绍。
MIME多用途互联网邮件扩展类型,这个不是协议,也在这里介绍,因为邮件消息需要遵循MIME扩展类型。具体的对照关系可以参看http://tool.oschina.net/commons
邮箱帐号的设置
在第三方客户端登录,需要设置帐号授权,获取授权码,此授权码将作为登录鉴权的密码使用。具体设置以QQ邮箱为例:
- 登录QQ邮箱,点击设置,帐号界面,下拉直到出现POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务的设置,在“IMAP/SMTP服务”选项处点击“开启”。
-
出现了短信验证的界面,如上操作,将一串数字发送指定的电话号码后,点击“我已发送”。
-
开启成功后,就可以根据自己的需求设置“收取选项”。之后“保存设置”,到这里我们就完成了IMAP服务的开启,可以成功使用第三方邮件客户端登陆了(可以看到如果你在第三方登入时忘记授权码,你可以在这里点击“生成授权码”,重新发短信获得新授权码)。
Javamail简介
JavaMail API提供了一种与平台无关和协议独立的框架来构建邮件和消息应用程序。下载地址:http://java.sun.com/products/javamail/。
解压后将javax.mail.jar导入项目。
javamail主要的模块:
- Session对象
Session对象管理客户端与邮件服务器的连接会话
static Session getDefaultInstance(Properties props);
static Session getDefaultInstance(Properties props, Authenticator authenticator);
static Session getInstance(Properties props);
static Session getInstance(Properties props, Authenticator authenticator);
getDefaultInstance获取缺省初始对象,getInstance获取的都是新对象。
Properties对象复用java.util.Properties,SMTP协议属性参数设置如下:
Properties props = new Properties();
props.put("mail.debug", "true");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.host", "smtp.qq.com");
props.put("mail.smtp.port", "465");
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); props.put("mail.smtp.socketFactory.port", "465");
props.put("mail.smtp.socketFactory.fallback", "false");
POP3协议属性参数设置如下:
Properties props = new Properties();
props.put("mail.debug", "true");
props.put("mail.pop3.host", "pop3.qq.com");
props.put("mail.pop3.port", "995");
props.put("mail.pop3.starttls.enable", "true");
Authenticator对象控制连接过程中的权限认证,设置代码如下:
Authenticator authenticator = new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("user:XXXX", "password:XXXX");
}
user是邮箱登录帐号,password是第三方客户端登录授权码
- Message对象
MimeMessage对象表示整封邮件,MimeBodyPart对象表示邮件的一个MIME消息,MimeMultipart对象表示一个由多个MIME消息组合而成的MIME消息。在具体组装复杂MIME消息,可参考如下代码:
Message message = new MimeMessage(session);
message.setSubject("subject");
message.setContent("test", "text/plain;charset=UTF-8");
Multipart multipart = new MimeMultipart();
BodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setText("test");
multipart.addBodyPart(messageBodyPart);
message.setContent(multipart);
这里涉及到MIME类型参考:http://tool.oschina.net/commons
- 邮件地址设置以及附件参数的组装
邮件地址由InternetAddress对象做转换,邮件包含的地址类型如下:
Message.RecipientType.TO;
Message.RecipientType.Cc;
Message.RecipientType.Bcc;
设置发件人:
message.setFrom(new InternetAddress("[email protected]"));
设置收件人:
message.setRecipients(Message.RecipientType.TO,InternetAddress.parse("[email protected];[email protected]"));
message.addRecipients(Message.RecipientType.Cc,InternetAddress.parse("[email protected];[email protected]"));
message.addRecipients(Message.RecipientType.Bcc,InternetAddress.parse("[email protected];[email protected]"));
设置附件:
messageBodyPart = new MimeBodyPart();
messageBodyPart.setDataHandler(new DataHandler(new DataSource("xxxx")));
messageBodyPart.setFileName("filename");
multipart.addBodyPart(messageBodyPart);
附件对象构造依赖DataHandler对象,DataHandler对象构造依赖DataSource对象。DataSource就是通过读取文件二进制流生成。javax.mail.util.ByteArrayDataSource可以直接通过二进制流生成DataSource对象。
final ByteArrayDataSource dataSource = new ByteArrayDataSource(data, (type == null || "".equals(type)) ? "application/octet-stream" : type);
- Transport对象
Transport对象控制邮件的发送,具体过程是:连接服务器->发送邮件->关闭连接,代码如下:
Transport transport = session.getTransport("smtps");
transport.connect("smtp.qq.com", 465, "[email protected]", "password:xxxxx");
transport.sendMessage(message, message.getAllRecipients());
transport.close();
- Store对象
Store对象表示整个邮局,可以获取查看邮局的所有信息,如查看收件夹,需要获取收件夹文件目录:
Folder emailFolder = store.getFolder("INBOX");
emailFolder.open(Folder.READ_ONLY);
//从folder中获取这些邮件信息并打印出来
Message[] messages = emailFolder.getMessages();
Folder 对象表示邮局的一个文件目录,一个Message对象就是一封邮件,拆解的过程与组装过程相反。
当获取整个Store对象以后,我们可以做更多的操作,如删除邮件,移动邮件等。但删除邮件与邮件帐号的设置相关,需要授权给第三方客户端操作权限。具体设置,参看各邮件服务商的帐号设置选项。
注意
使用javamail在连接邮件服务器时connect报错:
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
问题原因是jdk里面的jce包,安全性机制导致的访问https会报错,官网上有替代的jar包,下载替换:
http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
总结
到此,邮件收发基本功能完成。后续需要思考:
- 实现一个任务队列,因为邮件发送过程是个比较耗时的过程,可以扩展实现多线程处理。
- 新邮件收取的通知