今天需要用javamail自动发邮件,结果测试结果一看,发现邮件发是发出去了,就是有一大堆的乱码,这是郁闷
上网查了下,其实就是多设置一个小地方就可以咯,呵呵
看来自己功夫还不到家哦,要多多练习
package email;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Properties;
import java.util.Scanner;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
public class MailSender {
private static Properties pro = new Properties();
static {
pro.put("mail.transport.protocol", "smtp");
pro.put("mail.smtp.auth", "true");
pro.put("mail.smtp.host", "smtp.126.com");
pro.put("mail.host", "126.com");
}
public MailSender() {
super();
// TODO Auto-generated constructor stub
}
public static void sendWelcomeMail(String to) {
try {
DyeracAuthenticator authen = new DyeracAuthenticator();
Session session = Session.getInstance(pro, authen);
Message msg = new MimeMessage(session);
msg.setFrom(new InternetAddress("opencampus@126.com"));
msg.setSubject("欢迎注册open-campus开源平台");
msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(
to, false));
File mail=new File("mail.txt");
StringBuffer buf=new StringBuffer();
try {
Scanner s=new Scanner(mail);
while(s.hasNextLine()){
buf.append(s.nextLine());
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(buf.length()<1)
buf.append("no text");
msg.setContent(buf.toString(),"text/html;charset=utf8");
System.err.println(buf.toString());
Transport.send(msg);
} catch (AddressException ae) {
// TODO Auto-generated catch block
ae.printStackTrace();
} catch (MessagingException me) {
me.printStackTrace();
}
}
/** *//**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
MailSender.sendWelcomeMail("dyerac@gmail.com");
}
}
要设置的地方就是msg.setContent(buf.toString(),"text/html;charset=utf8");必须设置一个字符集,不然估计对方就会按默认的us-ascii来处理了吧
为了不浪费我在网上的搜索结果,我把别人写的也转在后面:
可能显得比较凌乱,sorry咯
LIST:
JavaMail API简介
JavaMail 之 杜绝邮件乱码
JavaMailAPI及其应用——一个邮件列表服务器的实现
JavaMail操作的总结
JavaMail的标题的中文问题最新版已经解决
JavaMail访问Hotmail邮箱
Javamail中的常见中文乱码问题与解决办法
使用javamail发送html邮件
--------------------------------------------------------------------------------------------------
javamai
原文:javamai
JavaMail API简介
JavaMail API是一种可选的、能用于读取、编写和发送电子消息的包(标准扩展)。您可使用这种包创建邮件用户代理(Mail User Agent ,MUA) 类型的程序,它类似于Eudora、Pine及Microsoft Outlook这些邮件程序。其主要目的不是像发送邮件或其他邮件传输代理(Mail Transfer Agent,MTA)类型的程序那样用于传输、发送和转发消息。换句话说,用户可以与MUA类型的程序交互,以阅读和撰写电子邮件。MUA依靠MTA处理实际的发送任务。
JavaMail API的设计是,为收发信息提供与协议无关的访问。方式是把该API划分成两个部分:
· 该API的第一个部分是本课程的重点。基本上是如何发送和接收独立于提供程序/协议的消息。
· 第二个部分则使用特定的协议语言,如:SMTP、POP、IMAP和NNTP。如果要让JavaMail API与服务器通信,就需要为之提供协议。由于Sun公司对特定协议提供程序有充分的介绍,用户可以免费获取,所以本课程没有介绍创建特定协议提供程序的内容。
复习相关协议
在学习JavaMail API的深层知识之前,让我们回过头来看一看在该API中使用的协议,本质上有4种人们常用的协议:
· SMTP
· POP
· IMAP
· MIME
您还需要了解NNTP及其他一些协议。理解这些协议的基本原理有助于您理解如何使用JavaMail API。而该API的设计要与协议无关,所以不能克服这些基础协议的限制。如果选用的协议不支持某种功能,那么JavaMail API也无法在其上添加这种功能。(正如您一会儿就会看到的,在操作POP协议时,常常会碰到这种问题)。
SMTP
简单邮件传输协议(SMTP)是用于传送电子邮件的机制。在JavaMail API环境中,您的基于JavaMail的程序将与您公司或Internet服务提供商(ISP)的SMTP服务器通信。该SMTP服务器将会把消息转发给用作接收消息的SMTP服务器,最后用户可通过POP或IMAP协议获取该消息。由于支持身份验证,所以不需要SMTP服务器是一种开放的转发器,但需要确保SMTP服务器配置正确。JavaMail API中没有集成用于处理诸如配置服务器以转发消息或添加/删除电子邮件帐户这一类任务的功能。
POP
POP的含义是邮局协议,当前的版本为3,也称作POP3,该协议是在RFC 1939中定义的。POP是Internet上的大多数人用来接收邮件的机制。它为每个用户的每个邮箱定义支持,这是它所做的全部工作,也是大多数问题的根源。在使用POP协议时,人们熟悉的很多功能,如查看收到了多少新邮件消息的功能,POP根本不支持。这些功能都内置到诸如Eudora或Microsoft Outlook之类的邮件程序中,能为您记住接收的上一封邮件,以及计算有多少新邮件这类信息。因此,使用JavaMail API时,如果想获取这类信息,将需要由自己进行计算。
IMAP
IMAP是用于接收消息的更加高级的协议,它是在RFC 2060中定义的。IMAP的含义是“Internet消息访问协议”,当前版本是第4版,也称作IMAP4。使用IMAP时,您的邮件服务器必须支持该协议。您不能只是简单地把程序转变为支持IMAP,而不是支持POP,就指望能支持IMAP中的一切。假定您的邮件服务器支持IMAP,那么基于JavaMail的程序就可利用在服务器上拥有多个文件夹的用户,并且这些文件夹可以被多个用户共享的功能。
由于IMAP协议具有更高级的功能,您也许会想IMAP应该被每一个人使用,但事实不是这样。因为IMAP会加重邮件服务器的负荷,它需要服务器接收新消息,发送消息给请求的用户,并在多个文件夹中为每个用户维护这些消息。而这要集中备份,因而长期下去用户的文件夹会变得越来越大,当磁盘空间用光了时,每个人都会遭受损失。而使用POP协议时,已保存消息可以解除服务器的重负。
MIME
MIME的含义是“多用途的网际邮件扩充协议”。它不是一种邮件传输协议,相反,它定义传输的内容:消息的格式、附件等。许多文档都定义了MIME协议,包含:RFC 822、RFC 2045、RFC 2046和RFC 2047。作为JavaMail API的用户,一般不需要担心这些格式。但是,这些格式确实存在,并为您的程序所用。
NNP和其他协议
由于JavaMail API分开了提供程序和其他部分,所以您可以轻松地为附加协议添加支持。Sun公司提供第3方提供程序清单,这些提供程序要利用 Sun公司不支持的少见的协议。在这份清单中,您将会看到对NNTP(网络新闻传输协议)[新闻组]、S/MIME(安全多用途的网际邮件扩充协议)及其他协议的提供支持的第3方提供程序。
安装
目前有两种版本的JavaMail API最常用:1.2和1.1.3。本课程中的所有例子都适用于这两种版本。其中JavaMail API 1.2是最新的,而JavaMail API 1.1.3中包含了Java 2企业版(J2EE)平台1.2.1版,所以它仍然很常用。使用JavaMail API的版本会对您的下载和安装产生一些影响。这两种版本的JavaMail API都能与JDK 1.1.6、Java 2标准版(J2SE)平台1.2.x和1.3.x协同工作。
注意:在安装了Sun公司的JavaMail工具后,会在演示目录下看到许多示例程序。
安装JavaMail 1.2
要使用JavaMail 1.2 API,可以下载JavaMail 1.2工具,然后解压缩javamail-1_2.zip文件,并把mail.jar文件添加到典型安装路径下。JavaMail 1.2工具带有SMTP、IMAP4和POP3提供程序以及核心类。
安装完JavaMail 1.2后,再安装JavaBeans Activation Framework。
安装JavaMail 1.1.3
要使用JavaMail 1.1.3 API,可以下载JavaMail 1.1.3工具,然后解压缩javamail1_1_3.zip文件,并把mail.jar文件添加到典型安装路径下。JavaMail 1.1.3工具带有SMTP和IMAP4提供程序以及核心类。
如果您想用JavaMail 1.1.3访问POP服务器,需要下载并安装POP3提供程序。Sun公司拥有一个独立于 JavaMail 工具的提供程序。在下载并解压缩pop31_1_1.zip文件后,也还需要把pop3.jar添加到典型安装路径下。
安装完JavaMail 1.1.3后,再安装JavaBeans Activation Framework。
安装JavaBeans Activation Framework
JavaMail API的所有版本都需要JavaBeans Activation Framework(JavaBeans激活框架),这种框架提供了对输入任意数据块的支持,并能相应地对其进行处理。看上去效果好像不太好,但该框架是在当今的许多浏览器和邮件工具中可以找到的基本MIME类型支持。下载该框架后,解压缩jaf1_0_1.zip文件,并将activation.jar文件添加到典型安装路径下。
对于JavaMail 1.2用户,现在应该把mail.jar和activation.jar文件添加到典型安装路径下。
对于JavaMail 1.1.3用户,现在应该把mail.jar、pop3.jar和activation.jar添加到典型安装路径下。如果您不打算使用POP3,就不需要把pop3.jar文件添加到典型安装路径下。
如果您不想更改安装路径环境变量,可以把JAR文件复制到Java运行时环境(JRE)目录下的lib/ext目录下。例如,对于J2SE 1.3版本,Windows平台上的默认目录应该是C:\jdk1.3\jre\lib\ext。
使用Java 2企业版
如果您使用的是J2EE,则在使用基本JavaMail API时,不需要做什么特殊的工作;JavaMail API带有J2EE类。只要确保j2ee.jar文件位于典型安装路径下,并完成了所有的设置工作。
对于J2EE 1.2.1,POP3提供程序是单独提供的,因此需要下载该提供程序,并按安装JavaMail 1.1.3的步骤,在J2EE 1.2.1中包含POP3提供程序。J2EE 1.3的用户会获得J2EE和POP3提供程序,因而不需要对POP3提供程序执行独立安装。使用这两种版本的J2EE用户,都不需要安装JavaBeans Activation Framework。
练习
设置您的 JavaMail 环境。
复习核心类
在开始深入研究JavaMail类之前,首先让用户浏览一下构成API的核心类:会话、消息、地址、验证程序、传输,存储和文件夹。所有这些类都可以在JavaMail API即javax.mail的顶层包中找到,尽管您将频繁地发现您自己使用的子类是在javax.mail.internet包中找到的。
Session类
Session类定义了一个基本的邮件会话。通过该会话可让别的工作顺利执行。Session对象利用java.util.Properties对象获取诸如邮件服务器、用户名、密码等信息,以及其他可在整个应用程序中共享的信息。
Session类的构造器是私有的。您可以获得一个可被getDefaultInstance()方法共享的单一的默认会话:
Properties props = new Properties();
// fill props with any information
Session session = Session.getDefaultInstance(props, null);
或者,您可以用getInstance()方法创建一个独特的会话:
Properties props = new Properties();
// fill props with any information
Session session = Session.getInstance(props, null);
这两种情形下的null参数都是一种Authenticator对象,它不是在此时使用的。详细信息请参阅其后的“Autherticator”一节。
在大多数情况下,使用共享会话就足够了,即使为多个用户邮箱处理邮件会话也是如此。您可以在通信过程的后面一步添加上用户名和密码的组合,并保持所有的一切是独立的。
Message类
一旦创建了自己的Session对象,就是该去创建要发送的消息的时候了。这时就要用到消息类型。作为一个抽象类,您必须操作一个子类,在大多数情况下,该子类是javax.mail.internet.MimeMessage。一个MimeMessage是一种理解MIME类型和报头(在不同的RFC文档中均有定义)的消息。消息的报头被严格限制成只能使用US-ASCII字符,尽管非ASCII字符可以被编码到某些报头字段中。
可以通过将Session对象传递给MimeMessage构造器的方法来创建消息:
MimeMessage message = new MimeMessage(session);
注意:还有其他的构造器,像用于创建消息的源于RFC822格式化的输入流的构造器。
一旦创建了消息,就可以设置其各个部分,如Message(消息)实现Part(部分)接口(以MimeMessage实现MimePart)。设置内容的基本机制是setContent()方法,它带有表示内容和MIME类型的参数:
message.setContent("Hello", "text/plain");
但是,如果正在使用 MimeMessage,并且您的消息是纯文本,那么您就可以使用setText()方法。该方法只需要一个表示实际内容的参数,默认的MIME类型为纯文本:
message.setText("Hello");
对于纯文本消息,setText()方法更常常被用来设置内容。要发送其他类型的消息,如HTML消息,就要使用setContent方法()。现在用的更多的是HTML消息。
要设置主题,可以使用setSubject()方法:
message.setSubject("First");
Address类
一旦创建了会话和消息,并为消息填充了内容,就需要用Address类为您的信件标上地址了。同Message类一样,Address类也是一种抽象类。您可以使用javax.mail.internet.InternetAddress类。
要创建只带有电子邮件地址的地址,可以把电子邮件地址传递给Address类的构造器:
Address address = new InternetAddress("president@whitehouse.gov");
如果想让一个名字出现在电子邮件地址后,也可以将其传递给构造器:
Address address = new InternetAddress("president@whitehouse.gov", "George Bush");
您要为消息的from(发送者)字段和to(接收者)字段创建地址对象。除非您的邮件服务器阻止这样做,否则要在发送的消息中注明该消息的发送者。
一旦创建好了地址,有两种方法可让您将地址与消息连接起来。为了鉴别发送者,您可以使用setFrom()和setReplyTo()方法。
message.setFrom(address)
如果您的消息需要显示多个地址来源,则可以使用addFrom()方法:
Address address[] = ...;
message.addFrom(address);
为了鉴别消息接收者,您可以使用addRecipient()方法。该方法除了需要一个地址参数外,还需要一个Message.RecipientType属性(消息的接收类型)。
message.addRecipient(type, address)
地址的3种预定义类型如下:
· Message.RecipientType.TO
· Message.RecipientType.CC
· Message.RecipientType.BCC
因此,如果一条消息将发送给副总统,同时还将发送该消息的副本给第一夫人,则采用下面的代码:
Address toAddress = new InternetAddress("vice.president@whitehouse.gov");
Address ccAddress = new InternetAddress("first.lady@whitehouse.gov");
message.addRecipient(Message.RecipientType.TO, toAddress);
message.addRecipient(Message.RecipientType.CC, ccAddress);
JavaMail API没有提供检查电子邮件地址有效性的机制。您可以自己编写支持扫描有效字符(在RFC 822文档中所定义的)的程序或检验MX(邮件交换)记录,这些都超越了JavaMail API的范围。
Authenticator类
与java.net类一样,JavaMail API可以利用Authenticator(验证程序)类通过用户名和密码来访问受保护的资源。对于JavaMail API来说,这种受保护的资源是指邮件服务器。JavaMail的Authenticator类可以在javax.mail包中找到,并有别于同名的java.net类。当JavaMail API在Java 1.1下工作时,JavaMail和java.net不会共享同一个Authenticator类名称,这是因为Java 1.1中不含有java.net。
要使用Authenticator类,您可以使用该抽象类的子类,并通过getPasswordAuthentication()方法返回一个PasswordAuthentication实例。在创建时,您必须用会话记录Authentication类。其后,当需要进行身份验证时,会通知您的Authenticator。会弹出一个窗口,或从一个配置文件(尽管不加密就不安全)中读取用户名和密码,并把它们作为一个PasswordAuthentication对象返回给调用程序。
Properties props = new Properties();
// fill props with any information
Authenticator auth = new MyAuthenticator();
Session session = Session.getDefaultInstance(props, auth);
Transport类
发送消息的最后一步操作是使用Transport类。该类使用特定于协议(通常是SMTP)的语言来发送消息。它是一个抽象类,其操作与Session类有些相似。您可以通过只调用静态的send()方法来使用该类的默认版本:
Transport.send(message);
或者,您可以从用于您的协议的会话中获取一个特定的实例,然后传递用户名和密码(不必要时可以为空)并发送消息,最后关闭连接:
message.saveChanges(); // implicit with send()
Transport transport = session.getTransport("smtp");
transport.connect(host, username, password);
transport.sendMessage(message, message.getAllRecipients());
transport.close();
当您需要发送多个消息时,建议采用后一种方法,因为它将保持消息间活动服务器的连接。而基本的send()机制会为每一个方法调用都建立一条独立的连接。
注意:要查看经过邮件服务器邮件命令,可以用session.setDebug(true)方法设置调试标志。
Store和Folder类
使用Session类来获取消息,开始时与发送消息很相似。但是,在获取会话后,很有可能使用用户名和密码或Authenticator类来连接Store类。与Transport类一样,您要告诉Store类将使用什么协议:
// Store store = session.getStore("imap");
Store store = session.getStore("pop3");
store.connect(host, username, password);
在连接Store类后,就可以获取一个Folder类,在读取其中的消息前必须先打开该类。
Folder folder = store.getFolder("INBOX");
folder.open(Folder.READ_ONLY);
Message message[] = folder.getMessages();
对于POP3协议,惟一可用的文件夹是INBOX。如果使用的是IMAP协议,则可以使用其他的文件夹。
注意:Sun公司的提供程序本来想提供方便。而Message message[]=folder.getMessages();这条语句却是一种从服务器逐条读取消息的缓慢操作,所以仅当您确实需要获取消息部分(该内容是所检索消息的内容)时可以使用这条语句。
一旦读取消息,就可以使用getContent()方法获取其内容,或使用writeTo()方法将其内容写到一个流中。getContent()方法只获取消息内容,而writeTo()方法则还会输出报头。
System.out.println(((MimeMessage)message).getContent());
一旦您阅读完邮件,就可以关闭对文件夹和存储的连接。
folder.close(aBoolean);
store.close();
传递给文件夹的close()方法的布尔变量指定了是否通过清除已删除的消息来更新文件夹。
继续前进
实际上,理解使用这7个类的方式,是使用JavaMail API处理几乎所有事情所需要的全部内容。用这7个类以外的方式构建的JavaMail API,其大多数功能都是以几乎完全相同或特定的方式来执行任务的,就好像内容是附件。特定的任务,如:搜索、隔离等将在后面进行介绍。
使用JavaMail API
您已经看到了如何操作JavaMail API的核心部分。在下面几节中,您将学习如何连接几个部分以执行特定的任务。
发送消息
发送电子邮件消息涉及到获取会话、创建和填充消息并发送消息这些操作。您可以在获取Session时,通过为要传递的Properties对象设置mail.smtp.host属性来指定您的SMTP服务器。
String host = ...;
String from = ...;
String to = ...;
// Get system properties
Properties props = System.getProperties();
// Setup mail server
props.put("mail.smtp.host", host);
// Get session
Session session = Session.getDefaultInstance(props, null);
// Define message
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(from));
message.addRecipient(Message.RecipientType.TO,
new InternetAddress(to));
message.setSubject("Hello JavaMail");
message.setText("Welcome to JavaMail");
// Send message
Transport.send(message);
您应该在try-catch块中编写代码,以在创建消息并发送它时可以抛出一个异常。
练习
发送您的第一个消息
获取消息
对于阅读邮件来说,首先您要获取一个会话,然后获取并连接到一个相应的用于您的收件箱的存储上,接着打开相应的文件夹,再获取消息。同时,不要忘记了操作完成后关闭连接。
String host = ...;
String username = ...;
String password = ...;
// Create empty properties
Properties props = new Properties();
// Get session
Session session = Session.getDefaultInstance(props, null);
// Get the store
Store store = session.getStore("pop3");
store.connect(host, username, password);
// Get folder
Folder folder = store.getFolder("INBOX");
folder.open(Folder.READ_ONLY);
// Get directory
Message message[] = folder.getMessages();
for (int i=0, n=message.length; i<n; i++) {
System.out.println(i + ": " + message[i].getFrom()[0]
+ "\t" + message[i].getSubject());
}
// Close connection
folder.close(false);
store.close();
每一条消息执行何种操作取决于自己决定。上面的代码块只是显示了消息的发送者和主题。从技术上讲,发送者地址列表可以为空,此时getFrom()[0]调用会抛出一个异常。
为了显示整条消息,您可以提示用户在看完消息的发送者和主题字段后,如果想看到消息的内容,可以再调用消息的writeTo()方法。
BufferedReader reader = new BufferedReader (
new InputStreamReader(System.in));
// Get directory
Message message[] = folder.getMessages();
for (int i=0, n=message.length; i<n; i++) {
System.out.println(i + ": " + message[i].getFrom()[0]
+ "\t" + message[i].getSubject());
System.out.println("Do you want to read message? " +
"[YES to read/QUIT to end]");
String line = reader.readLine();
if ("YES".equals(line)) {
message[i].writeTo(System.out);
} else if ("QUIT".equals(line)) {
break;
}
}
练习
检查邮件
删除消息和标志
删除消息涉及到操作与消息关联的标志。对不同的状态有不同的标志,有些标志是系统定义的,有些则是由用户定义的。预定义的标志都是在内部类Flags.Flag中定义的,如下所示:
· Flags.Flag.ANSWERED
· Flags.Flag.DELETED
· Flags.Flag.DRAFT
· Flags.Flag.FLAGGED
· Flags.Flag.RECENT
· Flags.Flag.SEEN
· Flags.Flag.USER
仅仅因为标志存在,并不表示标志为所有的邮件服务器/提供程序所支持。例如,除了删除消息外,POP协议对它们都不支持。检查新邮件不是POP的任务,但它已内置到邮件客户程序中。要搞清楚什么标志受到支持,可以使用getPermanentFlags()方法来询问文件夹。
要删除消息,需要为消息设置DELETE标志:
message.setFlag(Flags.Flag.DELETED, true);
第一次以READ_WRITE(读-写)模式打开文件夹:
folder.open(Folder.READ_WRITE);
然后,处理完了所有的消息,请关闭文件夹,并传递true值以擦去删除的消息。
folder.close(true);
用户可使用Folder类的expunge()方法来删除消息。但是,该方法对Sun公司的POP3提供程序不起作用。其他提供程序或许能也或许不能实现其功能。它更有可能适用于IMAP提供程序。由于POP只支持对收件箱的简单访问,使用Sun公司的提供程序时,您将不得不关闭文件夹以删除消息。
要移去标志,只需传递一个false值给setFlag()方法。要看看是否设置了某个标志,可以使用isSet()进行检查。
自我验证
先前学到的是使用Authenticator类,以在需要时提示输入用户名和密码,而不是以字符串的形式传入它们。这里,您将真正看到如何更加充分地使用验证。
不需使用主机、用户名和密码连接到Store,您可以配置Properties带有主机,并告诉Session关于您自定义的Authenticator实例,如下所示:
// Setup properties
Properties props = System.getProperties();
props.put("mail.pop3.host", host);
// Setup authentication, get session
Authenticator auth = new PopupAuthenticator();
Session session = Session.getDefaultInstance(props, auth);
// Get the store
Store store = session.getStore("pop3");
store.connect();
然后您可以使用Authenticator类的子类,并通过getPasswordAuthentication()方法返回一个PasswordAuthentication对象。下面是这种实现的一个例子,其中一个字段同时适用于两部分内容。它不是一个Project Swing指南,只是在一个字段中输入了两部分内容,它们是用逗号隔开的。
import javax.mail.*;
import javax.swing.*;
import java.util.*;
public class PopupAuthenticator extends Authenticator {
public PasswordAuthentication getPasswordAuthentication() {
String username, password;
String result = JOptionPane.showInputDialog(
"Enter 'username,password'");
StringTokenizer st = new StringTokenizer(result, ",");
username = st.nextToken();
password = st.nextToken();
return new PasswordAuthentication(username, password);
}
}
由于PopupAuthenticator依赖于Swing,因而将会启动用于AWT的事件处理线程。这在本质上要求您在代码中添加一个对System.exit()的调用,以终止程序的执行。
回复消息
Message类包含一个reply()方法,以用正确的接收者和主题(添加“Re::”,如果没有的话)配置一条新消息。该方法不会为消息添加任何内容,只是为新的接收者复制发送者或回复到的报头。该方法使用一个布尔型参数,提示是否只回复给发送者(false)或回复给所有人(true)。
MimeMessage reply = (MimeMessage)message.reply(false);
reply.setFrom(new InternetAddress("president@whitehouse.gov"));
reply.setText("Thanks");
Transport.send(reply);
在发送消息时要配置回复到地址,可使用setReplyTo()方法。
练习
回复邮件
转发消息
转发消息涉及的内容要稍微多一点,没有一个专门用于转发消息的方法,您可以通过处理组成消息的各个部分来创建要转发的消息。
一条邮件消息可由多个部分组成,每一部分是一个BodyPart(报文部分),或更特殊一点,在操作MIME消息时则是MimeBodyPart。不同的报文部分组合到一个称为Multipart的容器中,或者又更特殊一点,是一个MimeMultipart容器。要转发消息,您要创建一个用于消息文本的部分,和用于要转发的消息的第二个部分,并将这两个部分组合成一个multipart(多个部分)。然后您可以把这个multipart添加到一个合适的注明地址的消息中并发送它。
这就是转发消息的本质。要把一条消息的内容复制给另一条消息,只需通过它的DataHandler类复制即可,它是出自于JavaBeans Activation Framework的一个类。
// Create the message to forward
Message forward = new MimeMessage(session);
// Fill in header
forward.setSubject("Fwd: " + message.getSubject());
forward.setFrom(new InternetAddress(from));
forward.addRecipient(Message.RecipientType.TO,
new InternetAddress(to));
// Create your new message part
BodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setText(
"Here you go with the original message:\n\n");
// Create a multi-part to combine the parts
Multipart multipart = new MimeMultipart();
multipart.addBodyPart(messageBodyPart);
// Create and fill part for the forwarded content
messageBodyPart = new MimeBodyPart();
messageBodyPart.setDataHandler(message.getDataHandler());
// Add part to multi part
multipart.addBodyPart(messageBodyPart);
// Associate multi-part with message
forward.setContent(multipart);
// Send message
Transport.send(forward);
操作附件
附件是与邮件消息关联的资源,通常保存在消息之外,如:一个文本文件,电子表格或图片。对于像Eudora和Pine之类的常用邮件程序,您可以通过JavaMail API把资源附加到邮件消息上,并在您接收消息时获取附件。
发送附件
发送附件与转发消息非常相似,您要创建组成完整消息的各个部分。在创建好第一个部分即消息文本之后,您添加的用DataHandler类处理的其他部分就是您的附件,而不是转发消息中的共享处理程序。当您从一个文件读取附件时,附件的数据资源是FileDataSource;从URL读取时,则是URLDataSource。一旦您有了自己的DataSource,在将其通过setDataHandler()方法最终附加到BodyPart上之前,只需将其传递给DataHandler类的构造器即可。假定您想保留附件的原始文件名,要做的最后一件事就是用BodyPart类的setFileName()方法设置与附件关联的文件名。所有这些操作如下所示:
// Define message
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(from));
message.addRecipient(Message.RecipientType.TO,
new InternetAddress(to));
message.setSubject("Hello JavaMail Attachment");
// Create the message part
BodyPart messageBodyPart = new MimeBodyPart();
// Fill the message
messageBodyPart.setText("Pardon Ideas");
Multipart multipart = new MimeMultipart();
multipart.addBodyPart(messageBodyPart);
// Part two is attachment
messageBodyPart = new MimeBodyPart();
DataSource source = new FileDataSource(filename);
messageBodyPart.setDataHandler(new DataHandler(source));
messageBodyPart.setFileName(filename);
multipart.addBodyPart(messageBodyPart);
// Put parts in message
message.setContent(multipart);
// Send the message
Transport.send(message);
在消息中包含附件时,如果您的程序是一个servlet,您的用户就必须上传附件,并告诉您要把消息发送到什么位置。上传的每一个文件都可以用一个表单来处理,该表单是以multipart/表单数据(form-data)来编码的。
<FORM ENCTYPE="multipart/form-data"
method=post action="/myservlet">
<INPUT TYPE="file" NAME="thefile">
<INPUT TYPE="submit" VALUE="Upload">
</FORM>
注意:消息的大小要受到您的SMTP服务器的限制,而不是由JavaMail API限制的。如果出现了问题,可以通过设置ms和mx参数来考虑增加Java堆区的空间尺寸。
练习
发送附件
获取附件
从消息中取出附件比发送附件涉及的操作要稍微多一点,而MIME没有简单的附件概念。当消息带有附件时,消息的内容就是一个Multipart对象。然后需要处理各个部分,以获取主要内容和附件。通过part.getDisposition()方法标记上Part.ATTACHMENT配置的部分显然就是附件。同时,附件也可以不带有配置(和非文本MIME类型)或Part.INLINE配置。当配置是Part.ATTACHMENT或Part.INLINE时,您可以脱离该消息部分的内容将其保存起来。只需通过getFileName()方法获取原始文件名,并通过getInputStream()方法获取输入流即可。
Multipart mp = (Multipart)message.getContent();
for (int i=0, n=multipart.getCount(); i<n; i++) {
Part part = multipart.getBodyPart(i));
String disposition = part.getDisposition();
if ((disposition != null) &&
((disposition.equals(Part.ATTACHMENT) ||
(disposition.equals(Part.INLINE))) {
saveFile(part.getFileName(), part.getInputStream());
}
}
saveFile()方法只用于根据文件名创建一个文件,从输入流中读取字节,并将它们写入一个文件中去。如果文件已存在,将在文件名后添加一个编号,直到找到一个不存在的文件为止。
// from saveFile()
File file = new File(filename);
for (int i=0; file.exists(); i++) {
file = new File(filename+i);
}
上面的代码介绍了消息的各个部分被标上相应的标志的一个最简单的例子。要想包含所有的情况,还要对disposition值为null及消息部分为MIME类型的情况作相应处理。
if (disposition == null) {
// Check if plain
MimeBodyPart mbp = (MimeBodyPart)part;
if (mbp.isMimeType("text/plain")) {
// Handle plain
} else {
// Special non-attachment cases here of image/gif, text/html, ...
}
...
}
处理HTML消息
发送基于HTML的消息比发送纯文本消息要稍微复杂一点,尽管它不需要做大量的工作。它全部取决于您特定的需求。
发送HTML消息
如果您所要做的全部工作是发送一个等价的HTML文件作为消息,并让邮件阅读者忧心于取出任何嵌入的图片或相关片段,那么就可以使用消息的setContent()方法,以字符串形式传递消息内容,并把内容类型设置为text/html。
String htmlText = "<H1>Hello</H1>" +
"<img src=http://www.chinaitpower.com/"http://www.jguru.com/images/logo.gif\">";
message.setContent(htmlText, "text/html"));
在接收端,如果您用JavaMail API获取消息,在该API中没有内置任何用于以HTML格式显示消息的功能。JavaMail API只以字节流的形式来查看消息。要以HTML格式显示消息,您必须使用Swing JeditorPane或某些第3方HTML阅读器组件。
if (message.getContentType().equals("text/html")) {
String content = (String)message.getContent();
JFrame frame = new JFrame();
JEditorPane text = new JEditorPane("text/html", content);
text.setEditable(false);
JScrollPane pane = new JScrollPane(text);
frame.getContentPane().add(pane);
frame.setSize(300, 300);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.show();
}
在消息中包含图片
另一方面,如果您的HTML消息中嵌入了作为消息一部分的图片,并且您想保持消息内容的完整,就必须把图片看作附件,并用特殊的通信标识符URL引用该图片,该通信标识符引用的是图片附件的内容ID报文。
嵌入图片的处理与附加一个文件到消息上非常相似,惟一的不同之处在于:您必须区分MimeMultipart中,哪些部分是在构造器(或通过setSubType()方法)通过设置其子类型而使之相关的,以及将图片的内容ID报头设置成任意字符串,它将在img标记中用作图片的源路径。下面显示了一个完整的示例:
String file = ...;
// Create the message
Message message = new MimeMessage(session);
// Fill its headers
message.setSubject("Embedded Image");
message.setFrom(new InternetAddress(from));
message.addRecipient(Message.RecipientType.TO,
new InternetAddress(to));
// Create your new message part
BodyPart messageBodyPart = new MimeBodyPart();
String htmlText = "<H1>Hello</H1>" +
"<img src=\"cid:memememe\">";
messageBodyPart.setContent(htmlText, "text/html");
// Create a related multi-part to combine the parts
MimeMultipart multipart = new MimeMultipart("related");
multipart.addBodyPart(messageBodyPart);
// Create part for the image
messageBodyPart = new MimeBodyPart();
// Fetch the image and associate to part
DataSource fds = new FileDataSource(file);
messageBodyPart.setDataHandler(new DataHandler(fds));
messageBodyPart.setHeader("Content-ID","memememe");
// Add part to multi-part
multipart.addBodyPart(messageBodyPart);
// Associate multi-part with message
message.setContent(multipart);
练习
发送带有图片的 HTML 消息
用SearchTerm搜索
JavaMail API包含一种可用于创建SearchTerm(搜索条件)的筛选机制,它可以在javax.mail.search包中找到。一旦创建了SearchTerm,您就可以询问某个文件夹匹配的消息,并检索出消息对象数组:
SearchTerm st = ...;
Message[] msgs = folder.search(st);
有22种不同的类可用于帮助创建搜索条件。
· AND条件(AndTerm类)
· OR条件(OrTerm类)
· NOT条件(NotTerm类)
· SENT DATE条件(SentDateTerm类)
· CONTENT条件(BodyTerm类)
· HEADER条件(FromTerm / FromStringTerm, RecipientTerm / RecipientStringTerm, SubjectTerm, etc.)
本质上,您可以为匹配的消息创建一个逻辑表达式,然后进行搜索。例如,下面显示了一条消息的条件搜索示例,该消息带有(部分带有)一个ADV主题字符串,其发送者字段为friend@public.com。您可能考虑定期运行该查询,并自动删除任何返回的消息。
SearchTerm st =
new OrTerm(
new SubjectTerm("ADV:"),
new FromStringTerm("friend@public.com"));
Message[] msgs = folder.search(st);
CNBIE BLOG
--------------------------------------------------------------------------------
JavaMail API简介
原文:JavaMail API简介
JavaMail API简介
JavaMail API是一种可选的、能用于读取、编写和发送电子消息的包(标准扩展)。您可使用这种包创建邮件用户代理(Mail User Agent ,MUA) 类型的程序,它类似于Eudora、Pine及Microsoft Outlook这些邮件程序。其主要目的不是像发送邮件或其他邮件传输代理(Mail Transfer Agent,MTA)类型的程序那样用于传输、发送和转发消息。换句话说,用户可以与MUA类型的程序交互,以阅读和撰写电子邮件。MUA依靠MTA处理实际的发送任务。
JavaMail API的设计是,为收发信息提供与协议无关的访问。方式是把该API划分成两个部分:
· 该API的第一个部分是本课程的重点。基本上是如何发送和接收独立于提供程序/协议的消息。
· 第二个部分则使用特定的协议语言,如:SMTP、POP、IMAP和NNTP。如果要让JavaMail API与服务器通信,就需要为之提供协议。由于Sun公司对特定协议提供程序有充分的介绍,用户可以免费获取,所以本课程没有介绍创建特定协议提供程序的内容。
复习相关协议
在学习JavaMail API的深层知识之前,让我们回过头来看一看在该API中使用的协议,本质上有4种人们常用的协议:
· SMTP
· POP
· IMAP
· MIME
您还需要了解NNTP及其他一些协议。理解这些协议的基本原理有助于您理解如何使用JavaMail API。而该API的设计要与协议无关,所以不能克服这些基础协议的限制。如果选用的协议不支持某种功能,那么JavaMail API也无法在其上添加这种功能。(正如您一会儿就会看到的,在操作POP协议时,常常会碰到这种问题)。
SMTP
简单邮件传输协议(SMTP)是用于传送电子邮件的机制。在JavaMail API环境中,您的基于JavaMail的程序将与您公司或Internet服务提供商(ISP)的SMTP服务器通信。该SMTP服务器将会把消息转发给用作接收消息的SMTP服务器,最后用户可通过POP或IMAP协议获取该消息。由于支持身份验证,所以不需要SMTP服务器是一种开放的转发器,但需要确保SMTP服务器配置正确。JavaMail API中没有集成用于处理诸如配置服务器以转发消息或添加/删除电子邮件帐户这一类任务的功能。
POP
POP的含义是邮局协议,当前的版本为3,也称作POP3,该协议是在RFC 1939中定义的。POP是Internet上的大多数人用来接收邮件的机制。它为每个用户的每个邮箱定义支持,这是它所做的全部工作,也是大多数问题的根源。在使用POP协议时,人们熟悉的很多功能,如查看收到了多少新邮件消息的功能,POP根本不支持。这些功能都内置到诸如Eudora或Microsoft Outlook之类的邮件程序中,能为您记住接收的上一封邮件,以及计算有多少新邮件这类信息。因此,使用JavaMail API时,如果想获取这类信息,将需要由自己进行计算。
IMAP
IMAP是用于接收消息的更加高级的协议,它是在RFC 2060中定义的。IMAP的含义是“Internet消息访问协议”,当前版本是第4版,也称作IMAP4。使用IMAP时,您的邮件服务器必须支持该协议。您不能只是简单地把程序转变为支持IMAP,而不是支持POP,就指望能支持IMAP中的一切。假定您的邮件服务器支持IMAP,那么基于JavaMail的程序就可利用在服务器上拥有多个文件夹的用户,并且这些文件夹可以被多个用户共享的功能。
由于IMAP协议具有更高级的功能,您也许会想IMAP应该被每一个人使用,但事实不是这样。因为IMAP会加重邮件服务器的负荷,它需要服务器接收新消息,发送消息给请求的用户,并在多个文件夹中为每个用户维护这些消息。而这要集中备份,因而长期下去用户的文件夹会变得越来越大,当磁盘空间用光了时,每个人都会遭受损失。而使用POP协议时,已保存消息可以解除服务器的重负。
MIME
MIME的含义是“多用途的网际邮件扩充协议”。它不是一种邮件传输协议,相反,它定义传输的内容:消息的格式、附件等。许多文档都定义了MIME协议,包含:RFC 822、RFC 2045、RFC 2046和RFC 2047。作为JavaMail API的用户,一般不需要担心这些格式。但是,这些格式确实存在,并为您的程序所用。
NNP和其他协议
由于JavaMail API分开了提供程序和其他部分,所以您可以轻松地为附加协议添加支持。Sun公司提供第3方提供程序清单,这些提供程序要利用 Sun公司不支持的少见的协议。在这份清单中,您将会看到对NNTP(asp.cn/ class=wordstyle>网络新闻传输协议)[新闻组]、S/MIME(安全多用途的网际邮件扩充协议)及其他协议的提供支持的第3方提供程序。
安装
目前有两种版本的JavaMail API最常用:1.2和1.1.3。本课程中的所有例子都适用于这两种版本。其中JavaMail API 1.2是最新的,而JavaMail API 1.1.3中包含了Java 2企业版(J2EE)平台1.2.1版,所以它仍然很常用。使用JavaMail API的版本会对您的下载和安装产生一些影响。这两种版本的JavaMail API都能与JDK 1.1.6、Java 2标准版(J2SE)平台1.2.x和1.3.x协同工作。
注意:在安装了Sun公司的JavaMail工具后,会在演示目录下看到许多示例程序。
安装JavaMail 1.2
要使用JavaMail 1.2 API,可以下载JavaMail 1.2工具,然后解压缩javamail-1_2.zip文件,并把mail.jar文件添加到典型安装路径下。JavaMail 1.2工具带有SMTP、IMAP4和POP3提供程序以及核心类。
安装完JavaMail 1.2后,再安装JavaBeans Activation Framework。
安装JavaMail 1.1.3
要使用JavaMail 1.1.3 API,可以下载JavaMail 1.1.3工具,然后解压缩javamail1_1_3.zip文件,并把mail.jar文件添加到典型安装路径下。JavaMail 1.1.3工具带有SMTP和IMAP4提供程序以及核心类。
如果您想用JavaMail 1.1.3访问POP服务器,需要下载并安装POP3提供程序。Sun公司拥有
CNBIE BLOG
--------------------------------------------------------------------------------
JavaMail 之 杜绝邮件乱码
原文:JavaMail 之 杜绝邮件乱码
我一直想不通,RFC822 标准都发布多少年了,为什么还有那么多不符合规范的email 出现呢?一来也许是服务器的问题,二来就是不负责任的程序员的错了。所以我突然意识到,不是只有冷血无情的老板和咄咄逼人的客户让程序员的身心饱受摧残,同行的不经意或经意也在加剧着伤害。
我面对着一份邮件原文发出以上的感慨,客户抱怨该邮件收到的时候在系统中正文显示是乱码。这是一份典型的不合规范的邮件: Content-Type : text/plain ,没有说明 charset,而接下来的正文直接就是未进行任何编码的中文文字。不过 Subject 却是符合规范的(=?gb2312?B?xxxxxxx?=)。
行行色色的不合规范的邮件见过不少,最常见的就是某些header未编码,还有的可能就是,Body编码了而Subject 未编码,最讨厌的就是整份Email都没有编码信息。
恨归恨,问题还是得解决,我修改了代码,处理逻辑如下:
1. 在最开始解析邮件的时候,先解析某些可能带有编码信息的header,并记录为 headerCharset;部分代码如下:
private static Pattern encodeStringPattern = Pattern.compile("=\\?(.+)\\?(B|Q)\\?(.+)\\?=", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
private final String[] CHARTSET_HEADER = new String[] { "Subject", "From", "To", "Cc", "Delivered-To" };
..........
Enumeration enum = message.getMatchingHeaderLines(CHARTSET_HEADER);
while (enum.hasMoreElements()) {
String header = (String) enum.nextElement();
Matcher m = encodeStringPattern.matcher(header);
if (m.find()) {
this.headCharset = m.group(1);
log.debug("guess mail charset is " + this.headCharset);
break;
}
}
2. 接着解析邮件体,找到 Body 的时候,看看是否指明 charset 信息;如果指定了,记录为 bodyCharset;如果没有,使用 headerCharset,如果 headerCharset 也是 null,使用默认charset。通常是 ISO-8859-1。
3. 最后再处理邮件 header,如果没有charset 信息,使用 bodyCharset,否则使用默认charset。
以上的解决方案,只要邮件的Body或者Header中的一个提供了编码信息,那么都可能可以避免乱码的产生,如果哪份遭千杀的邮件,Body 用 gb2312 编码,Subject 却是未编码的日文,那我只能长叹被击败了。如果整份邮件都没有编码信息的话,除非你确定邮件都是指定的编码并进行转码,否则只有听天由命。
最后还是要呼吁一声,请遵循规范!
CNBIE BLOG
--------------------------------------------------------------------------------
JavaMailAPI及其应用——一个邮件列表服务器的实现(三)(转贴)
原文:JavaMailAPI及其应用——一个邮件列表服务器的实现(三)(转贴)
相关资源
1.Java Mail API软件包下载(版本1.1.2)
ftp://usmt.java.sun.com/pub/javamail/tyo39/javamail1_1_2.zip
缺省POP3实现软件包下载:
ftp://usmt.java.sun.com/pub/javamial/tyo39/pop31_1.zip
以上文件也可以从Sun的Java Mail API主页下载
http://java.sun.com/products/javamail/
2.JavaBeans Activation Framework(JAF)主页
http://java.sun.com/beans/glasgow/jaf.html
3.用Java Mail API开发的第三方产品列表,部分可以下载源代码
http://java.sun.com/products/javamail/Third_Party.html
4.Java meets e-mail
http://www.developer.com/journal/techworkshop/112498_jmail.html
5.Cutting Edge Java Technology: The JavaMail API
http://www.sigs.com/
6.订阅由Sun维护的关于Java Mail API的邮件列表
发信给listserv@java.sun.com,内容:
subscribe javamail-interest
CNBIE BLOG
--------------------------------------------------------------------------------
JavaMail操作的总结(2)
原文:JavaMail操作的总结(2)
//此段代码用来进行服务器对用户的认证
public class Email_Autherticator extends Authenticator
{
public Email_Autherticator()
{
super();
}
public PasswordAuthentication getPasswordAuthentication()
{
return new PasswordAuthentication(username,password);
}
}
//该程序为接收邮件
void jButton2_actionPerformed(ActionEvent e) {
try
{
Properties props = System.getProperties(); //获取系统变量
Authenticator auth = new Email_Autherticator();
props.put("mail.smtp.host",host);
props.put("mail.smtp.auth","true");
Session session = Session.getDefaultInstance(props,auth); //建立session
Store store = session.getStore("pop3");
store.connect(host,username,password);
//After connecting to the Store,you can get a Folder,which must be opened before you can read messages from it:
Folder folder = store.getFolder("INBOX");//连接到Store后,取得一个文件夹,一般默认的是INDEX
folder.open(Folder.READ_WRITE);//READ_ONLY为打开方式
Message message[] = folder.getMessages();//从文件夹获取邮件信息
//可以用两种方式去获得邮件信息,getContent()用来获得邮件的主体信息。而WriteTo()可以用来获得邮件的全部信息,包括头部信息
// System.out.println(((MimeMessage)message).getContent());
for (int i=0,n=message.length;i {
String out_from_person = ((InternetAddress)message[i].getFrom()[0]).getPersonal();
String out_from_address = ((InternetAddress)message[i].getFrom()[0]).getAddress();
System.out.println("From:"+out_from_person+" ");
System.out.println("Address:"+out_from_address+" ");
String out_subject = message[i].getSubject();
System.out.println("Subject:"+out_subject+" ");
//以下代码用来获得邮件的正文信息
Part messagePart = message[i];
Object out_content = messagePart.getContent();
if (out_content instanceof Multipart)
{
messagePart = ((Multipart)out_content).getBodyPart(0);
System.out.println("[ Multipart Message ]");
}
String out_content_type = messagePart.getContentType();
System.out.println("CONTENT:"+out_content_type);
if (out_content_type.startsWith("text/plain") | | out_content_type.startsWith("text/html"))
{
InputStream ipstm = messagePart.getInputStream();
BufferedReader bufreader = new BufferedReader(new InputStreamReader(ipstm));
String thisLine = bufreader.readLine();
while (thisLine != null)
{
System.out.println("thisLine: "+thisLine);
thisLine = bufreader.readLine();
}
}
System.out.println("------------------------------------------------------------");
message[i].setFlag(Flags.Flag.DELETED,true);//最后删除服务器端的邮件
}
//DELETED,ANSWERED,DRAFT,FLAGGED,RECENT,SEEN,USER
folder.close(true);//true的话,彻底删除已经标记为DELETE的邮件,如果为false的话,就不删除
store.close();//关闭
}
catch(Exception ej2)
{
System.out.println(ej2);
}
}
void jButton4_actionPerformed(ActionEvent e) {
try
{//该程序为回复邮件
Properties props = System.getProperties(); //获取系统变量
Authenticator auth = new Email_Autherticator(); //取得?uFFFD衿魅现?
props.put("mail.smtp.host",host);
props.put("mail.smtp.auth","true");
Session session = Session.getDefaultInstance(props,auth); //建立session
Store store = session.getStore("pop3");
store.connect(host,username,password);
Folder folder = store.getFolder("INBOX");
folder.open(Folder.READ_WRITE);
Message message[] = folder.getMessages();
for (int i=0,n=message.length;i {
// String out_from_person = ((InternetAddress)message[i].getFrom()[0]).getPersonal();//获取邮件发信人的署名
String out_from_address = ((InternetAddress)message[i].getFrom()[0]).getAddress();
System.out.println(out_from_address);
Message forward = new MimeMessage(session);
forward.setSubject("Fwd:"+message[i].getSubject());
forward.setFrom(new InternetAddress(mail_to));
forward.addRecipient(Message.RecipientType.TO,new InternetAddress(out_from_address));
BodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setText("Here you go with the original message: ");
Multipart multipart = new MimeMultipart();
multipart.addBodyPart(messageBodyPart);
messageBodyPart = new MimeBodyPart();
messageBodyPart.setDataHandler(message[i].getDataHandler());
multipart.addBodyPart(messageBodyPart);
forward.setContent(multipart);
Transport.send(forward);
message[i].setFlag(Flags.Flag.DELETED,true);//DELETED,ANSWERED,DRAFT,FLAGGED,RECENT,SEEN,USER
}
folder.close(true);
store.close();//关闭
}
catch(Exception ej2)
{
System.out.println(ej2);
}
}
void jButton5_actionPerformed(ActionEvent e) {
try
{
Properties props = new Properties();
Authenticator auth = new Email_Autherticator();
props.put("mail.smtp.host",host);
props.put("mail.smtp.auth","true");
Session session = Session.getDefaultInstance(props,auth);
MimeMessage message = new MimeMessage(session);
message.setContent("Hello","text/plain");
message.setSubject(mail_subject);
message.setText(mail_body);
message.setHeader(mail_head_name,mail_head_value);
message.setSentDate(new Date());
message.setFrom(new InternetAddress(mail_from,"sunxiaoming")); //to signal man
CNBIE BLOG
--------------------------------------------------------------------------------
JavaMail的标题的中文问题最新版已经解决
原文:JavaMail的标题的中文问题最新版已经解决
记得老版本的标题(发送/接收)中有中文会出现编码的问题,现在用新的JavaMail版本没有这个问题了。
另外如果内容为HTML的话,一般要设置编码为GBK,即在addPart的时候要addPart(part, "text/html; charset=\"GBK\"");asp?id=34339" width="1" height="1">
CNBIE BLOG
--------------------------------------------------------------------------------
JavaMail访问Hotmail邮箱
原文:JavaMail访问Hotmail邮箱
相信许多人都有MSN聊天工具的帐号,例如abc@hotmail.com ,这个账号其实也是一个邮件地址,可以进行正常的邮件收发功能,你可以通过网址 http://www.hotmail.com 来访问和操作这个邮箱进行邮件的收发。但是这个邮箱有一个限制就是它并不提供POP3或者SMTP服务,也就是说一般的邮件客户端工具无法使用这个邮箱,虽然现在有不少的邮件工具可以支持Hotmail邮箱,但是如果我们需要在我们的Java项目中增加对Hotmail邮箱的支持,那往往会不知从什么地方开始入手,因为SUN公司提供的JavaMail 本身并不提供对除了POP3/IMAP以及SMTP外其他协议的支持。
但事实上,JavaMail只是定义了一组平台无关、独立于通讯协议的邮件程序框架,或者说是接口,它是作为Java的可选包存在的。因此可以这样说:JavaMail并不关心所使用的协议,不管是POP3、SMTP、IMAP当然还有Hotmail用的HTTP协议。对于JavaMail的客户端而言也是这样的,下图就是一个最简单的JavaMail结构图,通过Transport进行邮件的发送,通过Store实现对邮件的收取,而不同协议的实现,JavaMail里的概念叫做Provider,只需要根据JavaMail定义的接口实现自己的Provider即可。
为了实现通过JavaMail访问Hotmail邮箱,我们就需要架起JavaMail接口与Hotmail服务所提供的访问接口之间的桥梁。在这之前我们必须首先了解Hotmail所使用的接口协议。
Hotmail通过地址 http://services.msn.com/svcs/hotmail/httpmail.asp 提供基于HTTP协议的服务,它不同于我们通过浏览器访问 http://www.hotmail.com 的这个地址,后面这个地址是提供给用户一个基于浏览器的访问界面以便对邮箱进行操作,你当然可以通过编写HTTP客户端程序来解析页面并获取邮件的信息,但是这样做不仅实现的复杂程度非常高,而且每次页面风格发生变化后程序都要做相应的调整,因此不具备通用性,显然是不可行的。而地址 http://services.msn.com/svcs/hotmail/httpmail.asp 则不同,它提供了固定的应用程序接口。当你使用浏览器访问该地址的时候,会弹出下面要求登录的窗体:
而当输入正确的用户名和口令后会再弹出一次或两次该提示窗口,但提示信息不同,操作完成后出现页面无法显示的错误,错误码是 405 (资源不允许)。因此该服务并不允许通过浏览器来访问。
事实上Hotmail使用的是WebDAV来提供基于HTTP协议的应用编程接口,WebDAV(Web 分布式创作和版本控制)已经成为重要的 Web 通讯协议。WebDAV所要解决的问题是:
1.改写保护:HTTP 1.1 无法确保客户端可以保护资源,并且可以在其他客户端同时编辑它们的情况下进行更改。 使用 WebDAV,可以通过多种方式来锁定资源,以便让其他客户端知道您对所讨论的资源感兴趣,或者防止其他客户端访问该资源。
2.资源管理:HTTP 只能直接访问单个资源。 WebDAV 提供一种更有效地组织数据的方法。 WebDAV 引入了可包含资源 的集合(类似于文件系统文件夹)概念。 通过 WebDAV 进行的资源管理包括如下功能:创建、移动、复制和删除集合,以及集合中的资源或文件。
3.文档属性:不同类型的数据具有唯一的属性,这有助于描述数据。 例如,在电子邮件中,这些属性可能是发件人的姓名和接收邮件的时间。 在协作文档中,这些属性可能是文档原始作者的姓名和最后一个编辑者的姓名。 因为人们使用的文档类型各不相同,所以可能的属性类型列表也变得无限大。 XML 是 WebDAV 所需的一种可扩展通讯工具。
关于WebDAV的更多资料可以参阅本文最后的参考资料。
相对于自行解析Hotmail的浏览器页面而言,基于特定协议WebDAV的接口使Hotmail邮件客户端程序的实现更加稳定和可靠。在了解了JavaMail API的基本接口以及Hotmail所提供接口的协议,本文的题目就变得非常具体了,就是如果编写基于WebDAV的JavaMail Provider(JavaMail提供者)并使用这个提供者来实现Hotmail邮件收发功能,但这题目太大而且已经超过了一篇文章所能叙述的范围,因此我们接下来将引入一个开放源码项目JDAVMail,并详细介绍如何通过这个项目实现Hotmail邮件的收发功能。
JDAVMail 是一个开放源码的JavaMail的提供者项目,可以通过它来访问基于WebDAV协议的邮件服务,有如Hotmail邮箱。你可以到 http://sourceforge.net/projects/jdavmail 这个网址下载到最新版本的程序。JDAVMail使用了Jakarta上的commons httpclient 作为HTTP客户端组件与服务器进行通讯,同时使用了jdom进行XML数据的解析,另外由于httpclient项目用到了commons logging进行日记的输出,因此commons-logging也是必须的,这三个组件都可以在JDAVMail的压缩包中的lib目录中找到,压缩包中同时还包括已编译的jar文件以及所有的源码。
为了让大家对JDAVMail有个大概的认识,下面我们给出两段代码用于Hotmail邮件的收发:
1. 邮件收取
/*
* 文件名:HotmailDemo.java
* 创建时间:2004-9-14
* 创建者:liudong
*/
package com.clickcom.mail;
import java.util.Date;
import java.util.Properties;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
/**
* Hotmail邮件的收发器
* @author liudong
*/
public class HotmailDemo {
public static void main(String[] args) {
receive();
}
/**
* 邮件接收
*/
protected static void receive() {
try {
Properties prop = new Properties();
Session ses = Session.getInstance(prop);
//使用JDAVMail Provider
Store store = ses.getStore("davmail");
//无需指定服务器地址
store.connect(null, "你的帐号","密码");
if (store.isConnected()) {
Folder inbox = store.getFolder("INBOX");
if (inbox.exists()) {
inbox.open(Folder.READ_ONLY);
int nCount = inbox.getMessageCount();
System.out.println("Inbox contains " + nCount + " messages");
// 依次显示收件箱中的每封邮件
for(int i=1;i<=nCount;i++){
MimeMessage msg = (MimeMessage) inbox.getMessage(i);
System.out.println("Subject : " + msg.getSubject());
System.out.println("From : " + msg.getFrom()[0].toString());
System.out.println("Content type : " + msg.getContentType());
System.out.println(msg.getContent());
}
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
从上面这段代码我们发现除了 Store store = ses.getStore("davmail"); 这个语句,同时不需要指定服务器地址外,其他的都跟一个普通的JavaMail应用程序没有两样,也没有引入任何跟JDAVMail有关的类。没有指定服务器地址这个比较容易理解,因为Hotmail提供HttpMail服务的地址是固定的。事实上仅有getStore这个语句跟常规邮件客户端程序有点不同的是它使用davmail字符串作为参数,而我们在使用JavaMail连接POP3服务器的时候指定参数值为pop3。怎么回事呢?因为JavaMail是通过协议名来加载Provider类的。打开JDAVMail.0.9.006.jar这个jar文件就会发现在META-INF目录下有一个文件javamail.providers,该文件内容如下:
protocol=davmail;
type=store; class=com.posisoft.jdavmail.JDAVMailStore;
vendor=Positive Software;
protocol=davmail_xmit;
type=transport;
class=com.posisoft.jdavmail.JDAVMailTransport;
vendor=Positive Software;
因此当我们指定协议名为davmail时,JavaMail就会自动加载com.posisoft.jdavmail.JDAVMailStore类进行处理,因此通过传入davmail字符串所得到的store实例实际上是类com.posisoft.jdavmail.JDAVMailStore的一个实例,这样我想大家应该了解了前面这段程序的工作原理。
我们再来看JDAVMail发送邮件的代码片段:
/**
* 邮件发送
*/
protected static void send() {
try {
Properties prop = new Properties();
//邮件发送者地址
prop.setProperty("mail.davmail.from","abc@hotmail.com");
Session ses = Session.getInstance(prop);
//获得JDAVMail的邮件发送实例
Transport transport = ses.getTransport("davmail_xmit");
//连接到Hotmail服务器,请替换为自己的用户名和口令
transport.connect(null, "用户名","口令");
// 准备要发送的邮件
MimeMessage txMsg = new MimeMessage(ses);
txMsg.setSubject("This is the subject");
//邮件发送者地址
InternetAddress addrFrom = new InternetAddress("abc@hotmail.com");
txMsg.setFrom(addrFrom);
//邮件接收者地址
InternetAddress addrTo = new InternetAddress("cdef@hotmail.com", "cdef");
txMsg.addRecipient(Message.RecipientType.TO, addrTo);
//邮件内容
txMsg.setText("Hello world !");
txMsg.setSentDate(new Date());
//发送邮件
transport.sendMessage(txMsg, txMsg.getAllRecipients());
} catch (Exception ex) {
ex.printStackTrace();
}
}
同样这段代码使用了davmail_xmit协议来获取JDAVMail的邮件发送实例,通过前面提到的javamail.providers文件不难知道JDAVMail使用类com.posisoft.jdavmail.JDAVMailTransport进行邮件的发送处理。
在实际测试中发现不管是JDAVMail还是其他的邮件工具,使用Hotmail进行邮件的发送速度比较慢,差不多有几分钟后才能收到相应的信息,因此如果测试中发现该问题请耐心等待。
我想通过本文介绍如何利用JDAVMail收发Hotmail邮件,以及研究JDAVMail的源码,不仅可以更加深入了解JavaMail的体系结构,对编写JavaMail Provider也有更加具体的参照。另外希望为正困挠于如何在Java程序中访问Hotmail邮件的朋友们排忧解难。同时也非常欢迎通过我的网站 http://www.javayou.com 跟我交流在使用中遇到的问题。
CNBIE BLOG
--------------------------------------------------------------------------------
JavaMail邮件主题乱码的解决方法
原文:JavaMail邮件主题乱码的解决方法
很多朋友都使用过JavaMail进行邮件发送,在邮件正文中的乱码容易解决。但邮件主题的乱码无论怎样转码总是显示一堆乱码。到底应该怎么处理
CNBIE BLOG
--------------------------------------------------------------------------------
JavaMail之杜绝邮件乱码
原文:JavaMail之杜绝邮件乱码
我一直想不通,RFC822 标准都发布多少年了,为什么还有那么多不符合规范的email 出现呢?一来也许是服务器的问题,二来就是不负责任的程序员的错了。所以我突然意识到,不是只有冷血无情的老板和咄咄逼人的客户让程序员的身心饱受摧残,同行的不经意或经意也在加剧着伤害。
我面对着一份邮件原文发出以上的感慨,客户抱怨该邮件收到的时候在系统中正文显示是乱码。这是一份典型的不合规范的邮件: Content-Type : text/plain ,没有说明 charset,而接下来的正文直接就是未进行任何编码的中文文字。不过 Subject 却是符合规范的(=?gb2312?B?xxxxxxx?=)。
行行色色的不合规范的邮件见过不少,最常见的就是某些header未编码,还有的可能就是,Body编码了而Subject 未编码,最讨厌的就是整份Email都没有编码信息。
恨归恨,问题还是得解决,我修改了代码,处理逻辑如下:
1. 在最开始解析邮件的时候,先解析某些可能带有编码信息的header,并记录为 headerCharset;部分代码如下:
private static Pattern encodeStringPattern = Pattern.compile("=\\?(.+)\\?(B|Q)\\?(.+)\\?=", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
private final String[] CHARTSET_HEADER = new String[] { "Subject", "From", "To", "Cc", "Delivered-To" };
..........
Enumeration enum = message.getMatchingHeaderLines(CHARTSET_HEADER);
while (enum.hasMoreElements()) {
String header = (String) enum.nextElement();
Matcher m = encodeStringPattern.matcher(header);
if (m.find()) {
this.headCharset = m.group(1);
log.debug("guess mail charset is " + this.headCharset);
break;
}
}
2. 接着解析邮件体,找到 Body 的时候,看看是否指明 charset 信息;如果指定了,记录为 bodyCharset;如果没有,使用 headerCharset,如果 headerCharset 也是 null,使用默认charset。通常是 ISO-8859-1。
3. 最后再处理邮件 header,如果没有charset 信息,使用 bodyCharset,否则使用默认charset。
以上的解决方案,只要邮件的Body或者Header中的一个提供了编码信息,那么都可能可以避免乱码的产生,如果哪份遭千杀的邮件,Body 用 gb2312 编码,Subject 却是未编码的日文,那我只能长叹被击败了。如果整份邮件都没有编码信息的话,除非你确定邮件都是指定的编码并进行转码,否则只有听天由命。
最后还是要呼吁一声,请遵循规范!
Javamail中的常见中文乱码问题与解决办法(综合)
JSP教程-Java技巧及代码
在使用javamail api开发邮件服务系统时,我们常常会碰到很多中文乱码问题,下面就分别介绍如何解决这些问题。
1.发送名称含中文的附件到邮件服务器,用别的邮件接收程序接收到的附件名显示为乱码
解决办法:
在调用MimeBodyPart的setFileName()时使用Base64编码。例如:
BASE64Encoder enc = new BASE64Encoder();//该类位于jre/lib/rt.jar中
//fds为FileDataSource实例
mbp.setFileName("=?GBK?B?"+enc.encode((fds.getName()).getByte ())+"?=");
2.接收邮件时,获取某些邮件发送程序发送的email地址,发送地址显示为乱码
解决办法:
对含有中文的发送地址,使用MimeUtility.decodeTex方法,对其他则把地址从ISO8859_1编码转换成gbk编码,见下例
public static String getFrom(Message msg){
String from="";
try{
if(msg.getFrom()[0]!=null)
from=msg.getFrom()[0].toString();
if(from.startsWith("=?GB")||from.startWith(“=?gb”)){
from=MimeUtility.decodeText(from);
}else{
from=StringUtil.toChinese(from);
}
}catch(Exception e){
e.printStackTrace();
}
from=StringUtil.replaceStr(from,“<”,“<”);// replaceStr为字符串替换函数
from=StringUtil.replaceStr(from,">",">");
return from;
}
///////////////////StringUtil的toChinese方法//////////////////////////
public static String toChinese(String strvalue){
try{
if(strvalue==null)
return null;
else{
strvalue = new String(strvalue.getBytes("ISO8859_1"), "GBK");
return strvalue;
}
}catch(Exception e){
return null;
}
}
3.接收邮件时,获取某个邮件的中文附件名,出现乱码
解决办法:
对于用base64编码过的中文,则采用base64解码,否则对附件名进行ISO8859_1到gbk的编码转换,例如:
String temp=part.getFileName();//part为Part实例
if((temp.startsWith("=?GBK?B?")&&temp.endsWith("?="))
||(temp.startsWith("=?gbk?b?")&&temp.endsWith("?="))){
temp=StringUtil.getFromBASE64(temp.substring(8,temp.indexOf("?=")-1));
}else{
temp=StringUtil.toChinese(temp);//该方法如前所叙
}
/////////////StringUtil的getFromBASE64方法/////////
public static String getFromBASE64(String s) {
if (s == null) return null;
BASE64Decoder decoder = new BASE64Decoder();
try {
byte[] b = decoder.decodeBuffer(s);
return new String(b);
} catch (Exception e) {
return null;
}
}
乱码问题的调试步骤总结:
基本上在javamail中碰到的中文乱码问题就这么多了,如果你的程序出现了中文乱码,首先不要惊慌,可用多个其他的邮件发送或接收程序进行验证,看是在哪个环节出现了问题,然后再仔细对照原文和乱码,调用相应的编码解码方法就行了。
------------------------------------------------------------------------------------------------------------------
使用javamail发送html邮件比较复杂
package org.tatan.mail;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;
import javax.mail.Session;
import javax.mail.MessagingException;
import javax.mail.Transport;
public class SendHtmlMail {
public static void sendMessage(String smtpHost,
String from, String to,
String subject, String messageText)
throws MessagingException,java.io.UnsupportedEncodingException {
// Step 1: Configure the mail session
System.out.println("Configuring mail session for: " + smtpHost);
java.util.Properties props = new java.util.Properties();
props.setProperty("mail.smtp.auth", "true");//指定是否需要SMTP验证
props.setProperty("mail.smtp.host", smtpHost);//指定SMTP服务器
props.put("mail.transport.protocol", "smtp");
Session mailSession = Session.getDefaultInstance(props);
mailSession.setDebug(true);//是否在控制台显示debug信息
// Step 2: Construct the message
System.out.println("Constructing message - from=" + from + " to=" + to);
InternetAddress fromAddress = new InternetAddress(from);
InternetAddress toAddress = new InternetAddress(to);
MimeMessage testMessage = new MimeMessage(mailSession);
testMessage.setFrom(fromAddress);
testMessage.addRecipient(javax.mail.Message.RecipientType.TO, toAddress);
testMessage.setSentDate(new java.util.Date());
testMessage.setSubject(MimeUtility.encodeText(subject,"gb2312","B"));
testMessage.setContent(messageText, "text/html;charset=gb2312");
System.out.println("Message constructed");
// Step 3: Now send the message
Transport transport = mailSession.getTransport("smtp");
transport.connect(smtpHost, "webmaster", "password");
transport.sendMessage(testMessage, testMessage.getAllRecipients());
transport.close();
System.out.println("Message sent!");
}
public static void main(String[] args) {
String smtpHost = "localhost";
String from = "webmaster@mymail.com";
String to = "mfc42d@sohu.com";
String subject = "html邮件测试"; //subject javamail自动转码
StringBuffer theMessage = new StringBuffer();
theMessage.append("<h2><font color=red>这倒霉孩子</font></h2>");
theMessage.append("<hr>");
theMessage.append("<i>年年失望年年望</i>");
try {
SendHtmlMail.sendMessage(smtpHost, from, to, subject, theMessage.toString());
}
catch (javax.mail.MessagingException exc) {
exc.printStackTrace();
}
catch (java.io.UnsupportedEncodingException exc) {
exc.printStackTrace();
}
}
}
邮件头(参见RFC822,RFC2047)只能包含US-ASCII字符。
邮件头中任何包含非US-ASCII字符的部分必须进行编码,使其只包含US-ASCII字符。
但是java mail可以根据JVM发送中文邮件自行编码,,用它自带的MimeUtility类的encodeText方法对中文信息进行编码也可以。
邮件正文必须有charset=gb2312否则为
Content-Type: text/html; charset=us-ascii
Content-Transfer-Encoding: 7bit
打开邮件为乱码,设置charset=gb2312后
Content-Type: text/html;charset=gb2312
Content-Transfer-Encoding: quoted-printable
它不能用MimeUtility里的方法来编码。
邮件正文的编码方式的信息是要放在Content-Transfer-Encoding这个邮件头参数中的,
而MimeUtility里面的方法是将编码方式的信息放在编码后的正文内容中。
所以如果你对正文也用MimeUtility进行处理,那么其他邮件程序就不会正常显示你编码的邮件,
因为其他邮件软件如outlook,foxmail只会根据Content-Transfer-Encoding这个里面的信息来对邮件正文进行解码。