背景
近期需要實現一個郵件客戶端的項目,類似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服務”選項處點擊“開啟”。
-
出現了短信驗證的界面,如上操作,將一串數字發送指定的電話號碼后,點擊“我已發送”。
Setting.png -
開啟成功后,就可以根據自己的需求設置“收取選項”。之后“保存設置”,到這里我們就完成了IMAP服務的開啟,可以成功使用第三方郵件客戶端登陸了(可以看到如果你在第三方登入時忘記授權碼,你可以在這里點擊“生成授權碼”,重新發短信獲得新授權碼)。
Setting.png
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("xxx@qq.com"));
設置收件人:
message.setRecipients(Message.RecipientType.TO,InternetAddress.parse("xxx@qq.com;xxxx@qq.com"));
message.addRecipients(Message.RecipientType.Cc,InternetAddress.parse("xxx@qq.com;xxxx@qq.com"));
message.addRecipients(Message.RecipientType.Bcc,InternetAddress.parse("xxx@qq.com;xxx@qq.com"));
設置附件:
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, "xxx@qq.com", "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
總結
到此,郵件收發基本功能完成。后續需要思考:
- 實現一個任務隊列,因為郵件發送過程是個比較耗時的過程,可以擴展實現多線程處理。
- 新郵件收取的通知