Java-Mail郵件開發
Email的歷史比Web還要久遠,直到現在,Email也是互聯網上應用非常廣泛的服務。
幾乎所有的編程語言都支持發送和接收電子郵件,但是,先等等,在我們開始編寫代碼之前,有必要搞清楚電子郵件是如何在互聯網上運作的。
我們來看看傳統郵件是如何運作的。假設你現在在北京,要給一個香港的朋友發一封信,怎麽做呢?
首先你得寫好信,裝進信封,寫上地址,貼上郵票,然後就近找個郵局,把信仍進去。
信件會從就近的小郵局轉運到大郵局,再從大郵局往別的城市發,比如先發到天津,再走海運到達香港,也可能走京九線到香港,但是你不用關心具體路線,你只需要知道一件事,就是信件走得很慢,至少要幾天時間。
信件到達香港的某個郵局,也不會直接送到朋友的家裏,因為郵局的叔叔是很聰明的,他怕你的朋友不在家,一趟一趟地白跑,所以,信件會投遞到你的朋友的郵箱裏,郵箱可能在公寓的一層,或者家門口,直到你的朋友回家的時候檢查郵箱,發現信件後,就可以取到郵件了。
電子郵件的流程基本上也是按上面的方式運作的,只不過速度不是按天算,而是按秒算。
現在我們回到電子郵件,假設我們自己的電子郵件地址是[email protected],對方的電子郵件地址是[email protected](註意地址都是虛構的哈),現在我們用Outlook或者Foxmail之類的軟件寫好郵件,填上對方的Email地址,點“發送”,電子郵件就發出去了。這些電子郵件軟件被稱為MUA:Mail User Agent——郵件用戶代理。
Email從MUA發出去,不是直接到達對方電腦,而是發到MTA:Mail Transfer Agent——郵件傳輸代理,就是那些Email服務提供商,比如網易、新浪等等。由於我們自己的電子郵件是163.com,所以,Email首先被投遞到網易提供的MTA,再由網易的MTA發到對方服務商,也就是新浪的MTA。這個過程中間可能還會經過別的MTA,但是我們不關心具體路線,我們只關心速度。
Email到達新浪的MTA後,由於對方使用的是@sina.com的郵箱,因此,新浪的MTA會把Email投遞到郵件的最終目的地MDA:Mail Delivery Agent——郵件投遞代理。Email到達MDA後,就靜靜地躺在新浪的某個服務器上,存放在某個文件或特殊的數據庫裏,我們將這個長期保存郵件的地方稱之為電子郵箱。
同普通郵件類似,Email不會直接到達對方的電腦,因為對方電腦不一定開機,開機也不一定聯網。對方要取到郵件,必須通過MUA從MDA上把郵件取到自己的電腦上。
所以,一封電子郵件的旅程就是:
發件人 -> MUA -> MTA -> MTA -> 若幹個MTA -> MDA <- MUA <- 收件人
有了上述基本概念,要編寫程序來發送和接收郵件,本質上就是:
- 編寫MUA把郵件發到MTA;
- 編寫MUA從MDA上收郵件。
發郵件時,MUA和MTA使用的協議就是SMTP:Simple Mail Transfer Protocol,後面的MTA到另一個MTA也是用SMTP協議。
收郵件時,MUA和MDA使用的協議有兩種:POP:Post Office Protocol,目前版本是3,俗稱POP3;IMAP:Internet Message Access Protocol,目前版本是4,優點是不但能取郵件,還可以直接操作MDA上存儲的郵件,比如從收件箱移到垃圾箱,等等。
郵件客戶端軟件在發郵件時,會讓你先配置SMTP服務器,也就是你要發到哪個MTA上。假設你正在使用163的郵箱,你就不能直接發到新浪的MTA上,因為它只服務新浪的用戶,所以,你得填163提供的SMTP服務器地址:smtp.163.com,為了證明你是163的用戶,SMTP服務器還要求你填寫郵箱地址和郵箱口令,這樣,MUA才能正常地把Email通過SMTP協議發送到MTA。
類似的,從MDA收郵件時,MDA服務器也要求驗證你的郵箱口令,確保不會有人冒充你收取你的郵件,所以,Outlook之類的郵件客戶端會要求你填寫POP3或IMAP服務器地址、郵箱地址和口令,這樣,MUA才能順利地通過POP或IMAP協議從MDA取到郵件。
在收發郵件前,請先準備好至少兩個電子郵件,如[email protected],[email protected],[email protected]等,註意兩個郵箱不要用同一家郵件服務商。
1. 郵件協議
1.1 收發郵件
發郵件大家都會吧!發郵件是從客戶端把郵件發送到郵件服務器,收郵件是把郵件服務器的郵件下載到客戶端。
我們在163、126、QQ、sohu、sina等網站註冊的Email賬戶,其實就是在郵件服務器中註冊的。這些網站都有自己的郵件服務器。
1.2 郵件協議概述
與HTTP協議相同,收發郵件也是需要有傳輸協議的。
- SMTP:(Simple Mail Transfer Protocol,簡單郵件傳輸協議)發郵件協議;
- POP3:(Post Office Protocol Version 3,郵局協議第3版)收郵件協議;
- IMAP:(Internet Message Access Protocol,因特網消息訪問協議)收發郵件協議,我們的課程不涉及該協議。
1.3 理解郵件收發過程
其實你可以把郵件服務器理解為郵局!如果你需要給朋友寄一封信,那麽你需要把信放到郵筒中,這樣你的信會“自動”到達郵局,郵局會把信郵到另一個省市的郵局中。然後這封信會被送到收信人的郵箱中。最終收信人需要自己經常查看郵箱是否有新的信件。
其實每個郵件服務器都由SMTP服務器和POP3服務器構成,其中SMTP服務器負責發郵件的請求,而POP3負責收郵件的請求。
當然,有時我們也會使用163的賬號,向126的賬號發送郵件。這時郵件是發送到126的郵件服務器,而對於163的郵件服務器是不會存儲這封郵件的。
1.4 郵件服務器名稱
smtp服務器的端口號為25,服務器名稱為smtp.xxx.xxx。
pop3服務器的端口號為110,服務器名稱為pop3.xxx.xxx。
例如:
- 163:smtp.163.com和pop3.163.com;
- 126:smtp.126.com和pop3.126.com;
- qq:smtp.qq.com和pop3.qq.com;
- sohu:smtp.sohu.com和pop3.sohu.com;
- sina:smtp.sina.com和pop3.sina.com。
2. Telnet收發郵件
Telnet協議是TCP/IP協議族中的一員,是Internet遠程登陸服務的標準協議和主要方式。它為用戶提供了在本地計算機上完成遠程主機工作的能力。在終端使用者的電腦上使用telnet程序,用它連接到服務器。終端使用者可以在telnet程序中輸入命令,這些命令會在服務器上運行,就像直接在服務器的控制臺上輸入一樣。可以在本地就能控制服務器。要開始一個telnet會話,必須輸入用戶名和密碼來登錄服務器。Telnet是常用的遠程控制Web服務器的方法
2.1 BASE64加密
BASE64是一種加密算法,這種加密方式是可逆的!它的作用是使加密後的文本無法用肉眼識別。Java提供了sun.misc.BASE64Encoder這個類,用來對做Base64的加密和解密,但我們知道,使用sun包下的東西會有警告!甚至在eclipse中根本使用不了這個類(需要設置),所以我們還是聽sun公司的話,不要去使用它內部使用的類,我們去使用apache commons組件中的codec包下的Base64這個類來完成BASE64加密和解密。
package cn.itcast; import org.apache.commons.codec.binary.Base64; public class Base64Utils { public static String encode(String s) { return encode(s, "utf-8"); } public static String decode(String s) { return decode(s, "utf-8"); } public static String encode(String s, String charset) { try { byte[] bytes = s.getBytes(charset); bytes = Base64.encodeBase64(bytes); return new String(bytes, charset); } catch (Exception e) { throw new RuntimeException(e); } } public static String decode(String s, String charset) { try { byte[] bytes = s.getBytes(charset); bytes = Base64.decodeBase64(bytes); return new String(bytes, charset); } catch (Exception e) { throw new RuntimeException(e); } } }
2.2 telnet發郵件
Xshell是一個強大的安全終端模擬軟件,它支持SSH1, SSH2, 以及Microsoft Windows 平臺的TELNET 協議。Xshell 通過互聯網到遠程主機的安全連接以及它創新性的設計和特色幫助用戶在復雜的網絡環境中享受他們的工作
Xshell可以在Windows界面下用來訪問遠端不同系統下的服務器,從而比較好的達到遠程控制終端的目的。
連接163的smtp服務器
連接成功後需要如下步驟才能發送郵件:
(1)、與服務器打招呼:ehlo你的名字
(2)、發出登錄請求:auth login
(3)、輸入加密後的郵箱名:([email protected])aXRjYXN0X2N4ZkAxNjMuY29t
(4)、輸入加密後的郵箱密碼:(itcast)aXRjYXN0
(5)、輸入誰來發送郵件,即from:mail from:[email protected]
(6)、輸入把郵件發給誰,即to:rcpt to:[email protected]
(7)、發送填寫數據請求:data
(8)、開始輸入數據,數據包含:from、to、subject,以及郵件內容,如果輸入結束後,以一個“.”為一行,表示輸入結束:
from:<zhangBoZhi@163.com> to:<[email protected]> subject: 我愛上你了 我已經深深的愛上你了,我是張柏芝。 .
註意,在標題和郵件正文之間要有一個空行!當要退出時,一定要以一個“.”為單行,表示輸入結束。
(9)、最後一步:quit
3. telnet收郵件
3.1 telnet收郵件的步驟
pop3無需使用Base64加密
收郵件連接的服務器是pop3.xxx.com,pop3協議的默認端口號是110。請註意!這與發郵件完全不同。如果你在163有郵箱賬戶,那麽你想使用telnet收郵件,需要連接的服務器是pop3.163.com
連接pop3服務器:telnet pop3.163.com 110
命令 | 功能描述 |
---|---|
user命令 | user 用戶名,例如:user [email protected] |
pass命令 | pass 密碼,例如:pass itcast |
stat命令 | stat命令用來查看郵箱中郵件的個數,所有郵件所占的空間 |
list命令 | list命令用來查看所有郵件,或指定郵件的狀態,例如:list 1是查看第一封郵件的大小, list是查看郵件列表,即列出所有郵件的編號,及大小 |
retr命令 | 查看指定郵件的內容,例如:retr 1#是查看第一封郵件的內容 |
dele命令 | 標記某郵件為刪除,但不是馬上刪除,而是在退出時才會真正刪除 |
quit命令 | 退出!如果在退出之前已經使用dele命令標記了某些郵件,那麽會在退出是刪除它們 |
4. JavaMail
4.1 JavaMail概述
Java Mail是由SUN公司提供的專門針對郵件的API,主要Jar包:mail.jar、activation.jar。
在使用MyEclipse創建web項目時,需要小心!如果只是在web項目中使用java mail是沒有什麽問題的,發布到Tomcat上運行一點問題都沒有!
但是如果是在web項目中寫測試那就出問題了。
在MyEclipse中,會自動給web項目導入javax.mail包中的類,但是不全(其實是只有接口,而沒有接口的實現類),所以只靠MyEclipse中的類是不能運行java mail項目的,但是如果這時你再去自行導入mail.jar時,就會出現沖突。
處理方案:到下面路徑中找到javaee.jar文件,把javax.mail刪除!!!
D:\Program Files\MyEclipse\Common\plugins\com.genuitec.eclipse.j2eedt.core_10.0.0.me201110301321\data\libraryset\EE_5
4.2 JavaMail中主要類
java mail中主要類:javax.mail.Session、javax.mail.internet.MimeMessage、javax.mail.Transport。
Session:表示會話,即客戶端與郵件服務器之間的會話!想獲得會話需要給出賬戶和密碼,當然還要給出服務器名稱。在郵件服務中的Session對象,就相當於連接數據庫時的Connection對象。
MimeMessage:表示郵件類,它是Message的子類。它包含郵件的主題(標題)、內容,收件人地址、發件人地址,還可以設置抄送和暗送,甚至還可以設置附件。
Transport:用來發送郵件。它是發送器!
4.3 JavaMail之Hello World
在使用telnet發郵件時,還需要自己來處理Base64編碼的問題,但使用JavaMail就不必理會這些問題了,都由JavaMail來處理。
第一步:獲得Session
Session session = Session.getInstance(Properties prop, Authenticator auth);
其中prop需要指定兩個鍵值,一個是指定服務器主機名,另一個是指定是否需要認證!我們當然需要認證!
Properties prop = new Properties(); prop.setProperty(“mail.host”, “smtp.163.com”);//設置服務器主機名 prop.setProperty(“mail.smtp.auth”, “true”);//設置需要認證
其中Authenticator是一個接口表示認證器,即校驗客戶端的身份。我們需要自己來實現這個接口,實現這個接口需要使用賬戶和密碼。
Authenticator auth = new Authenticator() { public PasswordAuthentication getPasswordAuthentication () { new PasswordAuthentication(“itcast_cxf”, “itcast”);//用戶名和密碼 } };
通過上面的準備,現在可以獲取得Session對象了:
Session session = Session.getInstance(prop, auth);
第二步:創建MimeMessage對象
創建MimeMessage需要使用Session對象來創建:
MimeMessage msg = new MimeMessage(session);
然後需要設置發信人地址、收信人地址、主題,以及郵件正文。
msg.setFrom(new InternetAddress(“[email protected]”));//設置發信人 msg.addRecipients(RecipientType.TO, “[email protected],[email protected]”);//設置多個收信人 msg.addRecipients(RecipientType.CC, “[email protected],[email protected]”);//設置多個抄送 msg.addRecipients(RecipientType.BCC, ”[email protected]”);//設置暗送 msg.setSubject(“這是一封測試郵件”);//設置主題(標題) msg.setContent(“當然是hello world!”, “text/plain;charset=utf-8”);//設置正文
第三步:發送郵件
Transport.send(msg);//發送郵件
4.4 JavaMail發送帶有附件的郵件
一封郵件可以包含正文、附件N個,所以正文與N個附件都是郵件的一個部份。
上面的hello world案例中,只是發送了帶有正文的郵件!所以在調用setContent()方法時直接設置了正文,如果想發送帶有附件郵件,那麽需要設置郵件的內容為MimeMultiPart。
MimeMulitpart parts = new MimeMulitpart();//多部件對象,可以理解為是部件的集合 msg.setContent(parts);//設置郵件的內容為多部件內容。
然後我們需要把正文、N個附件創建為“主體部件”對象(MimeBodyPart),添加到MimeMuiltPart中即可。
MimeBodyPart part1 = new MimeBodyPart();//創建一個部件 part1.setCotnent(“這是正文部分”, “text/html;charset=utf-8”);//給部件設置內容 parts.addBodyPart(part1);//把部件添加到部件集中。
下面我們創建一個附件:
MimeBodyPart part2 = new MimeBodyPart();//創建一個部件 part2.attachFile(“F:\\a.jpg”);//設置附件 part2.setFileName(“hello.jpg”);//設置附件名稱 parts.addBodyPart(part2);//把附件添加到部件集中
註意,如果在設置文件名稱時,文件名稱中包含了中文的話,那麽需要使用MimeUitlity類來給中文編碼:
part2.setFileName(MimeUitlity.encodeText(“美女.jpg”));
public class JavaMailDemo { public void sendMail() throws Exception { /* * 1. 得到session */ Properties props = new Properties(); props.setProperty("mail.host", "smtp.163.com"); props.setProperty("mail.smtp.auth", "true"); Authenticator auth = new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("itcast_cxf", "itcast"); } }; Session session = Session.getInstance(props, auth); /* * 2. 創建MimeMessage */ MimeMessage msg = new MimeMessage(session); msg.setFrom(new InternetAddress("[email protected]"));//設置發件人 msg.setRecipients(RecipientType.TO, "[email protected]");//設置收件人 msg.setRecipients(RecipientType.CC, "[email protected]");//設置抄送 msg.setRecipients(RecipientType.BCC, "[email protected]");//設置暗送 msg.setSubject("這是來自ITCAST的測試郵件"); msg.setContent("這就是一封垃圾郵件!", "text/html;charset=utf-8"); /* * 3. 發郵件 */ Transport.send(msg); } /** * 帶有附件的郵件!!! */ public void sendMail2() throws Exception { /* * 1. 得到session */ Properties props = new Properties(); props.setProperty("mail.host", "smtp.163.com"); props.setProperty("mail.smtp.auth", "true"); Authenticator auth = new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("itcast_cxf", "itcast"); } }; Session session = Session.getInstance(props, auth); /* * 2. 創建MimeMessage */ MimeMessage msg = new MimeMessage(session); msg.setFrom(new InternetAddress("[email protected]"));//設置發件人 msg.setRecipients(RecipientType.TO, "[email protected]");//設置收件人 msg.setSubject("這是來自ITCAST的測試郵件有附件"); //////////////////////////////////////////////////////// /* * 當發送包含附件的郵件時,郵件體就為多部件形式! * 1. 創建一個多部件的部件內容!MimeMultipart * MimeMultipart就是一個集合,用來裝載多個主體部件! * 2. 我們需要創建兩個主體部件,一個是文本內容的,另一個是附件的。 * 主體部件叫MimeBodyPart * 3. 把MimeMultipart設置給MimeMessage的內容! */ MimeMultipart list = new MimeMultipart();//創建多部分內容 // 創建MimeBodyPart MimeBodyPart part1 = new MimeBodyPart(); // 設置主體部件的內容 part1.setContent("這是一封包含附件的垃圾郵件", "text/html;charset=utf-8"); // 把主體部件添加到集合中 list.addBodyPart(part1); // 創建MimeBodyPart MimeBodyPart part2 = new MimeBodyPart(); part2.attachFile(new File("F:/f/白冰.jpg"));//設置附件的內容 //設置顯示的文件名稱,其中encodeText用來處理中文亂碼問題 part2.setFileName(MimeUtility.encodeText("大美女.jpg")); list.addBodyPart(part2); msg.setContent(list);//把它設置給郵件作為郵件的內容。 //////////////////////////////////////////////////////// /* * 3. 發郵件 */ Transport.send(msg); } public void sendMail3() throws Exception { /* * 1. 得到session */ Session session = MailUtils.createSession("smtp.163.com", "itcast_cxf", "itcast"); /* * 2. 創建郵件對象 */ Mail mail = new Mail("[email protected]", "[email protected],[email protected]", "不是垃圾郵件能是什麽呢?", "這裏是正文"); /* * 創建兩個附件對象 */ AttachBean ab1 = new AttachBean(new File("F:/f/白冰.jpg"), "小美女.jpg"); AttachBean ab2 = new AttachBean(new File("F:/f/big.jpg"), "我的羽絨服.jpg"); // 添加到mail中 mail.addAttach(ab1); mail.addAttach(ab2); /* * 3. 發送 */ MailUtils.send(session, mail); } }
5. MailUtils
public class MailUtils { public static Session createSession(String host, final String username, final String password) { Properties prop = new Properties(); prop.setProperty("mail.host", host);// 指定主機 prop.setProperty("mail.smtp.auth", "true");// 指定驗證為true // 創建驗證器 Authenticator auth = new Authenticator() { public PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(username, password); } }; // 獲取session對象 return Session.getInstance(prop, auth); } /** * 發送指定的郵件 * * @param mail */ public static void send(Session session, final Mail mail) throws MessagingException, IOException { MimeMessage msg = new MimeMessage(session);// 創建郵件對象 msg.setFrom(new InternetAddress(mail.getFrom()));// 設置發件人 msg.addRecipients(RecipientType.TO, mail.getToAddress());// 設置收件人 // 設置抄送 String cc = mail.getCcAddress(); if (!cc.isEmpty()) { msg.addRecipients(RecipientType.CC, cc); } // 設置暗送 String bcc = mail.getBccAddress(); if (!bcc.isEmpty()) { msg.addRecipients(RecipientType.BCC, bcc); } msg.setSubject(mail.getSubject());// 設置主題 MimeMultipart parts = new MimeMultipart();// 創建部件集對象 MimeBodyPart part = new MimeBodyPart();// 創建一個部件 part.setContent(mail.getContent(), "text/html;charset=utf-8");// 設置郵件文本內容 parts.addBodyPart(part);// 把部件添加到部件集中 /////////////////////////////////////////// // 添加附件 List<AttachBean> attachBeanList = mail.getAttachs();// 獲取所有附件 if (attachBeanList != null) { for (AttachBean attach : attachBeanList) { MimeBodyPart attachPart = new MimeBodyPart();// 創建一個部件 attachPart.attachFile(attach.getFile());// 設置附件文件 attachPart.setFileName(MimeUtility.encodeText(attach .getFileName()));// 設置附件文件名 parts.addBodyPart(attachPart); } } msg.setContent(parts);// 給郵件設置內容 Transport.send(msg);// 發郵件 } }
Java-Mail郵件開發