在Java Mail 中Message类是所有电子邮件的的超类它的定义如下:
public abstract class javax.mail.Message implements javax.mail.Part
1.标准的Java Mail API中有一个Message的子类:MimeMessage,它可用于电子邮件和Usenet新闻消息。除此之外,其他厂商可以自由扩展Message来满足自身需求。
Message类主要声明了定义大多数消息公共属性的抽象获取和设置方法。这些属性包括
(1)消息地址
(2)消息接收方
(3)消息主题和主体等
可以将这些属性视为包含消息的信封。
2.Message还实现了Part接口。Part接口用于处理消息的主体
创建消息
Message类有三个构造函数可以创建消息:
protected Message();
protected Message(Folder folder,int messageNumber);
protected Message(Session);
其中,第二个方法的参数说明如下:
folder
- 包含文件夹msgnum
- 在这个文件夹中消息的序列号
回复消息
如果已经有了一个Message对象,要创建一个新的Message对象,可以使用:
public abstract Message reply(boolean replyAll)throws MessagingException此方法用加了前缀“Re:”的相同主题和最初消息的发送方地址创建一个新的Message对象。如果参数为true,
消息会寻址到最初消息的所有接收方。消息的内容为空。如果要引用初始消息,就必须自己来完成这个工作。
From地址
下面四个方法用来获得和设置消息的“From”
public abstract Address[] getFrom() throws MessagingExceptionpublic abstract void setFrom() throws MessagingExceptionpublic abstract void addFrom(Address[] addresses) throws MessagingExceptionpublic abstract void addFrom(Address[] addresses) throws MessagingExceptionReply-to地址
有些消息包含一个Reply-to,指示回复消息应当发送到与发送消息不同的地址。有两个方法可以设置和获得这些地址:
public Address[] getReplyTo() throws MessagingExceptionGet the addresses to which replies should be directed.
public void setReplyTo(Address[] addresses) throws MessagingException
- Set the addresses to which replies should be directed
接收方地址
消息的发送方一般只存在于From:首部中,而消息的接收方却分为To,Cc,Bcc三个字段。在Java Mail中,这三个字段分别是:
Message.RecipientType.TO
Message.RecipientType.CC
Message.RecipientType.BCC
获取Message的接收方地址有两个方法:
public abstract Address[] getRecipients(Message.RecipientType type) throws MessagingExceptionpublic Address[] getAllRecipients() throws MessagingException消息主题
主要有两个方法:
public abstract void setSubject(String subject) throws MessagingExceptionpublic abstract String getSubject() throws MessagingException消息日期
消息还有发送、接收日期:
public abstract Date getSentDate() throws MessagingExceptionpublic abstract void setSentDate(Date date) throws MessagingExceptionpublic abstract Date getReceivedDate() throws MessagingException
package com.mail; import java.util.Date; import java.util.Properties; import javax.mail.*; import javax.mail.internet.InternetAddress; public class HeaderClient { /** * @param args */ public static void main(String[] args) { try { Properties props=new Properties(); props.setProperty("mail.transport.protocol", "pop3"); props.setProperty("mail.host", "pop3.sina.com"); Session session=Session.getDefaultInstance(props, new Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("[email protected]","111111"); } } ); //连接服务器,并打开文件夹 Store store=session.getStore("pop3"); store.connect("pop3.sina.com", "[email protected]", "1111111"); Folder folder=store.getFolder("INBOX"); if(folder==null){ System.out.println("Folder not found!"); System.exit(1); } folder.open(Folder.READ_ONLY); //从服务器获取消息 Message[] ms=folder.getMessages(); for(int i=0;i<ms.length;i++){ System.out.println("--------------Message"+(i+1)+"---------------"); String from =InternetAddress.toString(ms[i].getFrom()); if(from!=null){ System.out.println("消息来自:"+from); } String to=InternetAddress.toString(ms[i].getRecipients(Message.RecipientType.TO)); if(to!=null){ System.out.println("消息去往:"+to); } String replyTo=InternetAddress.toString(ms[i].getReplyTo()); if(replyTo!=null){ System.out.println("消息回复给:"+replyTo); } String cc=InternetAddress.toString(ms[i].getRecipients(Message.RecipientType.CC)); if(cc!=null){ System.out.println("消息抄送:"+cc); } Date sent=ms[i].getSentDate(); if(sent!=null){ System.out.println("消息发送时间::"+sent); } String subject=ms[i].getSubject(); if(subject!=null){ System.out.println("消息主题:"+subject); } Date received=ms[i].getReceivedDate(); if(received!=null){ System.out.println("消息接收时间:"+received); } System.out.println(); } folder.close(false); store.close(); } catch (MessagingException e) { e.printStackTrace(); } } }
下面是输出:
标志
标志类代表了消息上的标志集合。它是系统标志的预定义的组成,也包括用户自定义的标志。 在Message中与标志有关的方法如下:
public abstract Flags getFlags() throws MessagingExceptionpublic boolean isSet(Flags.Flag flag) throws MessagingExceptionpublic void setFlag(Flags.Flag flag, boolean set) throws MessagingExceptionpublic abstract void setFlags(Flags flag, boolean set) throws MessagingException其中,最后两个方法用于设置或取消设置(取决于第二个参数)由第一个参数指示的标志,例如:ms[i].setFlag(Flags.Flag.DELETED,true)
上面一行代码会删除消息,这只是标记消息已经删除,它并没有实际从服务器上清除其文件。在消息被清除前,仍然可以将Flags.Flag.DELETED设回false来完成取消删除。
Flags类包括一个static的Flag字段,该字段预定义了七个常量:
static Flags.Flag
ANSWERED
This message has been answered.static Flags.Flag
DELETED
This message is marked deleted.static Flags.Flag
DRAFT
This message is a draft.static Flags.Flag
FLAGGED
This message is flagged.static Flags.Flag
RECENT
This message is recent.static Flags.Flag
SEEN
This message is seen.static Flags.Flag
USER
A special flag that indicates that this folder supports user defined flags.
值得注意的是,pop3服务器不会报告标志,只有存储并转发消息的协议(如IMAP协议)才会报告标志。
下面的代码用来显示电子邮件的标志信息:
//测试标志 if(ms[i].isSet(Flags.Flag.DELETED)){ System.out.println("deleted!"); } if(ms[i].isSet(Flags.Flag.ANSWERED)){ System.out.println("answered!"); } if(ms[i].isSet(Flags.Flag.DRAFT)){ System.out.println("draft!"); } if(ms[i].isSet(Flags.Flag.FLAGGED)){ System.out.println("flagged!"); } if(ms[i].isSet(Flags.Flag.RECENT)){ System.out.println("recent!"); } if(ms[i].isSet(Flags.Flag.SEEN)){ System.out.println("seen!"); } if(ms[i].isSet(Flags.Flag.USER)){ //预先不知道用户标志是什么 //所以它们返回字符串数组 String[] userFlags=ms[i].getFlags().getUserFlags(); for(int k=0;i<userFlags.length;k++){ System.out.println("用户标志:"+userFlags[i]); } }
如果服务器支持搜索(很多IMAP服务器支持,而大多数POP服务器不支持),就很容易在文件夹中搜索满足条件的消息。
Message提供了搜索邮件的接口:
public boolean match(SearchTerm term) throws MessagingException它将根据SearchTerm类型的对象来搜索Message,返回值告诉调用者是否搜索到了满足条件的Message。
SearchTerm是一个抽象类,它的常用子类可以分为:
import java.util.Date; import java.util.Properties; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeUtility; import javax.mail.search.BodyTerm; import javax.mail.search.SearchTerm; import javax.mail.*; public class SearchMailTest { /** * @param args */ public static void main(String[] args) { try { Properties props=new Properties(); props.setProperty("mail.transport.protocol", "imap"); props.setProperty("mail.host", "imap.sina.com"); Session session=Session.getDefaultInstance(props, new Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("[email protected]","******"); } } ); //连接服务器,并打开文件夹 Store store=session.getStore("imap"); store.connect("imap.sina.com", "[email protected]", "******"); Folder folder=store.getFolder("INBOX"); if(folder==null){ System.out.println("Folder not found!"); System.exit(1); } folder.open(Folder.READ_WRITE); //从服务器获取消息 SearchTerm search= new BodyTerm("test"); Message[] ms=folder.search(search); System.out.println("搜索到"+ms.length+"封符合条件的邮件,正在打印....."); for(int i=0;i<ms.length;i++){ System.out.println("--------------Message"+(i+1)+"---------------"); String from =InternetAddress.toString(ms[i].getFrom()); if(from!=null){ from=MimeUtility.decodeText(from); System.out.println("消息来自:"+from); } String to=InternetAddress.toString(ms[i].getRecipients(Message.RecipientType.TO)); if(to!=null){ to=MimeUtility.decodeText(to); System.out.println("消息去往:"+to); } String replyTo=InternetAddress.toString(ms[i].getReplyTo()); if(replyTo!=null){ replyTo=MimeUtility.decodeText(replyTo); System.out.println("消息回复给:"+replyTo); } String cc=InternetAddress.toString(ms[i].getRecipients(Message.RecipientType.CC)); if(cc!=null){ System.out.println("消息抄送:"+cc); } Date sent=ms[i].getSentDate(); if(sent!=null){ System.out.println("消息发送时间::"+sent); } String subject=ms[i].getSubject(); if(subject!=null){ subject=MimeUtility.decodeText(subject); System.out.println("消息主题:"+subject); } Date received=ms[i].getReceivedDate(); if(received!=null){ System.out.println("消息接收时间:"+received); } System.out.println("消息内容:"); if(ms[i].isMimeType("multipart/*")){ Multipart mp=(Multipart) ms[i].getContent(); int bodyNum=mp.getCount(); for(int k=0;k<bodyNum;k++){ if(mp.getBodyPart(k).isMimeType("text/html")){ String content=(String) mp.getBodyPart(k).getContent(); System.out.println(content); } } }else{ System.out.println("不支持的邮件类型!"); } } folder.close(false); store.close(); } catch (Exception e) { e.printStackTrace(); System.exit(1); } } }
Message和BodyPart都实现了Part接口。每个Message都是一个Part,但有些Part可能包含其他部分。Part接口声明了三种方法:
获取和设置部分属性的方法
获取和设置部分首部的方法
获取和设置部分内容的方法
部分属性是类似消息大小或接收日期的内容,其细节未在消息首部中显示指定;与之相反,首部则是包含在部分前面的名-值对;部分
的内容是消息传输的实际数据。
Java mail API 定义了五个部分属性:
size:部分的字节大小
line count:部分的行数
description:部分的简短文本概括说明
desposition:标示部分是否为附件或者是否应当在内部显示
file name:附件的文件名
不是所有部分都有所有属性。例如,不表示附件的部分不可能有filename属性。每个属性都映射到一个获取它的方法,例如:
String getDisposition() throws MessagingExceptionint getSize() throws MessagingExceptionint getLineCount() throws MessagingExceptionString getFileName() throws MessagingExceptionString getDescription() throws MessagingException
注意:如果部分没有所请求的属性,获取方法就会返回null或者-1。
getDisprsition()方法返回一个字符串,指示内容应当在内部显示,还是作为附件显示。返回值应当是下面三种:
null
Part.INLINE
Part.ATTACHMENT
下面的程序展示了消息首部的控制台输出:
package com.mail; import java.util.Properties; import javax.mail.*; public class MailAttributeTest { /** * @param args */ public static void main(String[] args) { try { Properties props=new Properties(); props.setProperty("mail.transport.protocol", "imap"); props.setProperty("mail.host", "imap.sina.com"); Session session=Session.getDefaultInstance(props, new Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("[email protected]","1111111"); } } ); //连接服务器,并打开文件夹 Store store=session.getStore("imap"); store.connect("imap.sina.com", "[email protected]", "1111111"); Folder folder=store.getFolder("INBOX"); if(folder==null){ System.out.println("Folder not found!"); System.exit(1); } folder.open(Folder.READ_WRITE); //从服务器获取消息 Message[] ms=folder.getMessages(); for(int i=0;i<ms.length;i++){ System.out.println("-----------下面是Message"+(i+1)+"的属性打印------------"); System.out.println("Message size(byte) is:"+ms[i].getSize()); System.out.println("Message lineCount is:"+ms[i].getLineCount()); System.out.println("Message subject is"+ms[i].getSubject()); String desc=ms[i].getDescription(); if(desc!=null){ System.out.println("Message description is:"+desc); } if(ms[i].getContent() instanceof Multipart){ //该方法只是显示了附件的名字,如果消息包含附件的话 processMultipart(ms[i]); } System.out.println("-----------Message"+(i+1)+"的属性结束------------"); } folder.close(false); store.close(); } catch (Exception e) { e.printStackTrace(); System.exit(1); } } /** * if the message contains attachment * this method will show the name of attachment * @param ms * @throws Exception */ private static void processMultipart(Message ms) throws Exception{ Multipart multipart=(Multipart) ms.getContent(); for(int k=0;k<multipart.getCount();k++){ Part part=multipart.getBodyPart(k); String disposition=part.getDisposition(); if(disposition==null); if(disposition.equalsIgnoreCase(Part.INLINE)){ System.out.println("This part should be displayed inline"); } if(disposition!=null&&disposition.equalsIgnoreCase(Part.ATTACHMENT)){ System.out.println("This part is an attachment"); String fileName=part.getFileName(); if(fileName!=null){ System.out.println("The file name of this attachment is"+fileName); } } } } }
下面是运行结果:
每个部分都有可以表现为字节序列的内容。例如,在简单电子邮件消息的部分中,内容是消息的主体。但是在多部分消息中,内容本身
可以包含其他部分。
Part接口声明了两个可以确定部分的MIME内容类型的方法。
getContentType()
isMimeType(String mimeType)
下面程序,显示了如何处理带有附件的多部分消息:
package com.mail; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.Enumeration; import java.util.Properties; import javax.mail.*; public class AllPartsClient { /** * @param args */ public static void main(String[] args) { try { Properties props=new Properties(); props.setProperty("mail.transport.protocol", "imap"); props.setProperty("mail.host", "imap.sina.com"); Session session=Session.getDefaultInstance(props, new Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("[email protected]","111111"); } } ); //连接服务器,并打开文件夹 Store store=session.getStore("imap"); store.connect("imap.sina.com", "[email protected]", "111111"); Folder folder=store.getFolder("INBOX"); if(folder==null){ System.out.println("Folder not found!"); System.exit(1); } folder.open(Folder.READ_WRITE); //从服务器获取消息 Message[] ms=folder.getMessages(); for(int i=0;i<ms.length;i++){ System.out.println("-----------Message"+i+"begin-----------"); //显示消息首部 Enumeration headers=ms[i].getAllHeaders(); while(headers.hasMoreElements()){ Header header=(Header) headers.nextElement(); System.out.println(header.getName()+" : "+header.getValue()); } System.out.println(); //枚举各个部分 Object obj=ms[i].getContent(); if(obj instanceof Multipart){ processMultipart((Multipart)obj); }else{ processPart(ms[i]); } } folder.close(false); store.close(); } catch (Exception e) { e.printStackTrace(); System.exit(1); } } private static void processPart(Part part) { try { String fileName=part.getFileName(); String disposition=part.getDisposition(); String contentType=part.getContentType(); if(contentType.toLowerCase().startsWith("multipart/*")){ processMultipart((Multipart)part.getContent()); }else if((fileName==null)&&(Part.ATTACHMENT.equalsIgnoreCase(disposition)) ||(!contentType.equalsIgnoreCase("text/plain"))){ if(fileName!=null){ if(fileName.endsWith(".jpg")){ fileName=File.createTempFile("attachment", ".jpg").getName(); }else{ fileName=File.createTempFile("attachment", ".txt").getName(); } } } if(fileName==null){ //可能是内部邮件 part.writeTo(System.out); }else{ File f=new File(fileName); for(int i=1;f.exists();i++){ String newName=fileName+"_"+i; f=new File(newName); } OutputStream output=new BufferedOutputStream(new FileOutputStream(f)); InputStream input=new BufferedInputStream(part.getInputStream()); int b; while((b=input.read())!=-1){ output.write(b); } output.flush(); output.close(); input.close(); System.out.println("附件"+f.getAbsolutePath()+"成功下载!"); } } catch (Exception e) { e.printStackTrace(); } } private static void processMultipart(Multipart obj) throws MessagingException { for(int i=0;i<obj.getCount();i++){ processPart(obj.getBodyPart(i)); } } }
1.当得到一个Message对象时,调用它的getSubject(),getFrom()等等方法,可以得到邮件的基本信息。
2.调用Messag.getContentType()方法来判断邮件的类型
(1)如果邮件类型是“text/plain”或者“text/html”,说明邮件
是纯文本,可以调用getContent()来获取邮件内容并加以显示。
(2)如果邮件类型是“multipart/*”,表示邮件类型是复合类型,此时需将邮件的getContent()方法返回的对象
转换成Multipart。
3.调用Multipart的getCount()方法来检测Multipart中包含多少BodyPart对象,通过循环取出每个BodyPart对象
在处理每个BodyPart对象时,调用它的getContent()方法确定它的类型,有三种情况:
(1)当其类型是”text/*“表示是纯文本
(2)当其类型是”multipart/*“需要重复2.(2)->3步骤
(3)当其表示图片,音频等二进制内容时,可以调用getInputStream()获取原始的二进制内容。
之前的文章发送电子邮件,只是发送的简单文本而已,这对于现在的电子邮件客户端而言,是非常简单的;在创建复杂邮件之前,先要弄明白一些概念:setText(String text)
setContent(Object o,String type)
writeTo(OutputStream out)
1.MimeMessage :继承Message,表示整封邮件
2.MimeBodyPart:表示邮件的一个MIME消息
3.MimeMultipart:表示一个由多个MIME消息组合成的组合MIME消息。
故:
1.复杂邮件和简单文本邮件一样,都是有MimeMesage表示的
2.对于复杂邮件,不能简单的用MimeMessage.setText 方法生成,而是要将MimeMultipart对象设置到MimeMessage对象中
3.MimeMultipart 用来组合一个或者多个MimeBodyPart对象,其中每个MimeBodyPart对象使用MimeMultipart.addBodyPart(BodyPart part)方法
将一个MIME消息添加到MimeMultipart对象中
4.MimeBodyPart 对象表示的MIME消息,可能还是一个MIME组合消息,那么它的内容就需要使用一个MimeMultipart表示,这一步只是重复上述步骤而已。
可见,不管邮件体重的数据层次关系多么复杂,我们都可以用MimeBodyPart和MimeMultipart对象的这种相互嵌套关系,来组织并封装复杂邮件。
下面,是一个示例:
package com.mail; import java.io.File; import java.util.Properties; import javax.activation.DataHandler; import javax.activation.FileDataSource; import javax.mail.Authenticator; import javax.mail.Message; import javax.mail.Part; import javax.mail.PasswordAuthentication; 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; import javax.mail.internet.MimeMessage.RecipientType; public class SendMail { /** * @param args */ public static void main(String[] args) { Properties props=new Properties(); props.setProperty("mail.smtp.auth", "true"); props.setProperty("mail.transport.protocol", "smtp"); props.setProperty("mail.host", "smtp.sina.com");//smtp.sina.com Session session=Session.getInstance(props, new Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("[email protected]","111111"); } } ); Message msg=new MimeMessage(session); try { String body="在天愿为比翼鸟,在地愿为连理枝。<br/>"+"<img src=\"cid:yanan\">"; msg.setFrom(new InternetAddress("[email protected]")); msg.setSubject("test"); msg.setRecipients(RecipientType.TO, InternetAddress.parse("[email protected]"));//[email protected] MimeBodyPart contentPart=(MimeBodyPart) createContent(body,"contentFilePath");; MimeBodyPart part=(MimeBodyPart) createAttachment("attachmentFilePath"); MimeMultipart mime=new MimeMultipart("mixed"); mime.addBodyPart(contentPart); mime.addBodyPart(part); msg.setContent(mime); Transport.send(msg); System.out.println("success"); } catch (Exception e) { e.printStackTrace(); } } static Part createContent(String content,String fileName){ MimeBodyPart contentPart=null; try { contentPart=new MimeBodyPart(); MimeMultipart contentMultipart=new MimeMultipart("related"); MimeBodyPart htmlPart=new MimeBodyPart(); htmlPart.setContent(content, "text/html;charset=gbk"); contentMultipart.addBodyPart(htmlPart); MimeBodyPart gifBodyPart=new MimeBodyPart(); FileDataSource fds=new FileDataSource(new File(fileName)); gifBodyPart.setDataHandler(new DataHandler(fds)); gifBodyPart.setFileName(fds.getName()); gifBodyPart.setContentID("yanan"); contentMultipart.addBodyPart(gifBodyPart); contentPart.setContent(contentMultipart); } catch (Exception e) { e.printStackTrace(); } return contentPart; } static Part createAttachment(String fileName){ Part part=new MimeBodyPart(); FileDataSource fds=new FileDataSource(new File(fileName)); try { part.setDataHandler(new DataHandler(fds)); part.setFileName(fds.getName()); } catch (Exception e) { e.printStackTrace(); } return part; } }