从问题开始
用SMTP发送邮件和POP3介绍邮件时分别遇到上述连个错误,本质原因是没有进行SSL加密配置。
直接看解决方法:
Unrecognized SSL message
DEBUG POP3: authentication command failed/ A secure connection is requiered(such as ssl)
源代码如下:
package mail;
import java.util.Date;
import java.util.Properties;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMessage.RecipientType;
class MyAuthor extends Authenticator{
private String username;
private String password;
public MyAuthor(String username,String password){
this.username = username;
this.password = password;
}
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username,password);
}
}
public class JavaMail {
public static void main(String[] a){
//属性类(Properties)操作
Properties props = new Properties(); //保存服务器的地址和发送协议及端口号
props.put("mail.transport.protocol","smtp");//确定发送协议
props.put("mail.smtp.host", "smtp.qq.com");//服务器地址
props.put("mail.smtp.port", "25");//服务器发送邮件的端口号
props.put("mail.smtp.auth", "true");//确定使用安全认证
props.put("mail.smtp.ssl.enable", "true");//使用ssl加密
//会话类(Session)操作
String username = "40****@qq.com";
String password = "************";//登录的授权码而非密码
MyAuthor auth = new MyAuthor(username, password);//构造认证对象
Session session = Session.getDefaultInstance(props,auth);//根据连接服务器的信息和认证对象来获取与服务器的会话
session.setDebug(true);
//消息类/多媒体消息类(Message/MimeMessage)操作
Message message = new MimeMessage(session);//根据会话来构造信息
try{
message.setFrom(new InternetAddress(username));//设置该消息的发件人地址
message.setRecipient(RecipientType.TO,new InternetAddress(username));//设置该消息的接收人地址
message.setSentDate(new Date());
message.setSubject("Helloworld");//设置消息主题
String m = "Hello world!";
message.setText(m);//设置消息发送的文本
//传输类(Transport)操作
Transport.send(message);//传送消息
}catch (Exception e) {
e.printStackTrace();
}
}
}
这是因为SMTP使用的端口号25是不使用SSL加密的,而上面又设置需要进行SSL加密,产生冲突。
解决方法:
1.端口号25改成465,SMTP的465端口支持SSL加密
props.put("mail.smtp.port", "465");
2.将传输协议改成SMTPS(SMTP-over-SSL),端口号可以不用设置了,因为SMTPS使用的就是465端口号。
props.put("mail.transport.protocol","smtps");
关于SMTP/POP3/IMTP协议可参考:
http://blog.csdn.net/lake121/article/details/42099995
http://edm.ishang.net/faq/detail/the-imap-protocol.html
关于JavaMail的API内容可参考:
http://www.360doc.com/content/14/0306/17/16088576_358273704.shtml
关于JAF的作用可参考:
http://blog.csdn.net/kissqw/article/details/6555860
下面介绍一下关于JavaMail的其他内容
发送有附件的邮件
直接上代码:
MailBean类用于保存和邮件相关的信息。
package mail;
public class MailBean {
private String username;
private String password;
private String sender;
private String reciver;
private String message;
private String paths[];
public MailBean(){//测试用的信息
this.username = "******@qq.com";
this.password = "***********";
this.sender = "*****.com";
this.reciver = "****.com";
this.message = "test";
this.paths = new String[2];
paths[0] = "e:\\test10\\auto.html";//附件内容
paths[1] = "e:\\2.bmp";
}
public MailBean(String username,String password,String sender,String reciver,String message,String paths[]){
this.username = username;
this.password = password;
this.sender = sender;
this.reciver = reciver;
this.message = message;
this.paths = paths;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public String getSender() {
return sender;
}
public String getReciver() {
return reciver;
}
public String getMessage() {
return message;
}
public String[] getPaths() {
return paths;
}
}
MailService类提供处理邮件信息的方法,并发送邮件。
package mail;
import java.util.Date;
import java.util.Properties;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMessage.RecipientType;
import javax.mail.internet.MimeMultipart;
public class MailService {
private Properties sendParameter;
public Properties getsendParameter(){
if (sendParameter==null) {
sendParameter = new Properties();
sendParameter.put("mail.transport.protocol","smtps");//确定发送协议
sendParameter.put("mail.smtp.host", "smtp.qq.com");//服务器地址
sendParameter.put("mail.smtp.auth", "true");//确定使用安全认证
sendParameter.put("mail.smtp.ssl.enable", "true");//使用SSL加密
}
return sendParameter;
}
public void sendMail(MailBean bean){
Properties props =this.getsendParameter(); //保存服务器的地址和发送协议及端口号
String username = bean.getUsername();
String password = bean.getPassword();//登录的授权码而非密码
MyAuthor auth = new MyAuthor(username, password);//构造认证对象
Session session = Session.getDefaultInstance(props,auth);//根据连接服务器的信息和认证对象来获取与服务器的会话
session.setDebug(true);
MimeMessage message = new MimeMessage(session);//根据会话来构造信息
try{
message.setFrom(new InternetAddress(bean.getSender()));//设置该消息的发件人地址
message.setRecipient(RecipientType.TO,new InternetAddress(bean.getReciver()));//设置该消息的接收人地址
Multipart filePart = new MimeMultipart();//构造带附件的消息体
MimeBodyPart body = null;
body = new MimeBodyPart(); //这个body用于显示正文内容
body.setText(bean.getMessage());
filePart.addBodyPart(body);
for(int i=0;i<bean.getPaths().length;i++){
body = new MimeBodyPart();
DataSource source = new FileDataSource(bean.getPaths()[i]);//获取附件的路径
DataHandler handle = new DataHandler(source);
body.setDataHandler(handle);
String filename = bean.getPaths()[i].substring(bean.getPaths()[i].lastIndexOf("\\")+1,bean.getPaths()[i].length());
body.setFileName(filename);//设置附件名
filePart.addBodyPart(body);
}
message.setContent(filePart);
message.setSentDate(new Date());
message.setSubject("Mutipart");//设置消息主题
Transport.send(message);//传送消息
}catch (Exception e) {
e.printStackTrace();
}
}
}
SendServlet类用于获取用户提交的信息,并调用前面所编写的业务类完成邮件的发送。此处用MailBean默认的信息。需要在web.xml中进行注册,这里不赘述了。
package mail;
import java.io.IOException;
import java.rmi.ServerException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/SendMail")
public class SendServlet extends HttpServlet{
protected void doGet(HttpServletRequest request,HttpServletResponse response)throws ServerException,IOException {
/*request.setCharacterEncoding("gbk");
String username = request.getParameter("username");
String password = request.getParameter("password");
String sender = request.getParameter("sender");
String reciver = request.getParameter("reciver");
String message = request.getParameter("message");
String paths[] =request.getParameterValues("path");
MailBean bean = new MailBean(username,password,sender,reciver,message,paths);*/
//如果要借助浏览器来动态获得则使用以上内容,并且需要编写界面表单的参数如上
MailBean bean = new MailBean();//测试时创建的默认信息
MailService service = new MailService();
service.sendMail(bean);
}
}
收取并解析邮件
解析和显示邮件
解析和显示一封邮件就是把封装在Message对象中的数据解析出来,包括邮件头中的邮件发送者地址、邮件主体、发送时间,邮件正文中的文本信息、内嵌资源,邮件体中的附件信息等,然后把解析出来的这些信息交给数据显示软件显示。
JavaMail解析邮件的流程如下:
- 调用Message对象的getForm,getSubject等方法,可以得到邮件发送人和主题等信息,调用getContentType方法得到邮件的类型。
- 通过Message.getContentType方法的返回值判断邮件类型,并调用Message.getContent方法得到邮件内容。如果邮件类型为“text/plain”或”text/html”,表示邮件为纯文本,此时调用Message对象的getContent方法得到邮件内容,然后将返回对象的类型转换成String输出给显示软件即可。如果邮件类型为”multipart/*”,表示邮件内容是一个复合类型,此时需将Message.getContent方法返回的对象转换成Multipart。
- 调用Multipart对象的getCount方法检测Multipart对象中封装了多少个BodyPart对象,并通过for循环逐一取出Multipart对象中的每个BodyPart对象进行处理。
- 在处理每个BodyPart对象时,首先调用BodyPart对象的getContentType方法得到他的MIME类型,然后根据MIME类型做出如下三种情况的处理:
- 当MIME类型表示的是图片、声音或附件等二进制数据时,此时应调用BodyPart对象的getDataHandler方法得到封装了数据的DataHandler对象,然后调用DataHandler对象的getInputStream方法获得与数据相关的InputStream对象,通过这个InputStream对象中即可获得原始的二进制数据内容。(也可以直接part.getInputStream)
类似这样:
Content-Type: application/octet-stream; name=2.bmp
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=2.bmp
Qk02DDAAAAAAADYAAAAoAAAAVgUAAAADAAABABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA......- 当MIME类型为”text/*”时,表示BodyPart对象中保存的是纯文本数据,此时调用BodyPart对象的getContent方法并将返回的对象转换成String输出给显示软键显示即可。
- 当MIME类型是”multipart/mixed”时,表示BodyPart对象中保存的是一个复合MIME消息,此时调用BodyPart对象中的getContent方法得到封装复合MIME消息的对象并将它转换成Multipart类型。
这里实现了读取该账户中的邮件,将附件以文件形式保存,将文本内容显示出来。
@WebServlet("/MailReceives")
public class MailReceives extends HttpServlet {
private static final long serialVersionUID = 1L;
public MailReceives() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("message/rfc822;charset=utf-8");
PrintWriter out = response.getWriter();
Properties props = System.getProperties();
final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
props.setProperty("mail.pop3.socketFactory.class", SSL_FACTORY);
props.setProperty("mail.pop3.port", "995");//需要指明使用995端口,否则默认的110端口不支持SSL
String host = "pop.qq.com";//确定服务器地址
String username = "******@qq.com";
String password = "******";//授权码
Session session = Session.getDefaultInstance(props);//获取与服务器 之间的会话连接
Store store = null;
Folder fold = null;
try{
store = session.getStore("pop3");//通过会话获取Store对象
store.connect(host, username, password);//登录到指定服务器
fold = store.getFolder("inbox");
fold.open(Folder.READ_ONLY);//只读方式打开文件夹
Message[] mess = fold.getMessages();//获取文件夹下面的邮件
for(int i=0;i<mess.length;i++){//遍历每份邮件
Message m = mess[i];
String path = "e:\\mail\\"+m.getSubject();
File folder = new File(path);//本地创建文件夹
folder.mkdirs();
Object content = m.getContent();// getContent() 是获取包裹内容
if (content instanceof Part) {
Part body = (Part)content;
rePart(body,path);
}
else if (content instanceof Multipart) {
Multipart multipart = (Multipart) content ;
reMultipart(multipart,path);
}
else {
System.out.println("类型" + m.getContentType());
System.out.println("内容" + m.getContent());
}
}
}catch (Exception e) {
e.printStackTrace();
}finally{
try {
if(fold!=null)
fold.close(false);//不删除邮件
if(store!=null)
store.close();
}catch (MessagingException e) {
e.printStackTrace();
}
}
}
//part是最小的单位,可以直接操作了
private void rePart(Part part,String path)throws MessagingException,
UnsupportedEncodingException, IOException, FileNotFoundException{
if(part.getDisposition()!=null&&part.getDisposition().equals(Part.ATTACHMENT)){
//是附件形式存在的,可以直接提取出文件名,若part.getDisposition()是INLINE形式的(内嵌的)就按照文本方式输出而不保存成文件
String strFileName = MimeUtility.encodeText(part.getFileName());
System.out.println("附件名: " + strFileName);
System.out.println("内容类型: " + MimeUtility.decodeText(part.getContentType()));
System.out.println("附件内容:" + part.getContent());
InputStream in = part.getInputStream();// 打开附件的输入流
// 读取附件字节并存储到文件中
FileOutputStream out = new FileOutputStream(path+"\\"+strFileName);
int data;
while((data = in.read()) != -1) {
out.write(data);
}
in.close();
out.close();
}else {
if(part.getContentType().startsWith("text/plain")) {
System.out.println("文本内容:" + part.getContent());
} else {
System.out.println("HTML内容:" + part.getContent());
}
}
}
//Multipart可以再分成Part或者Mutipart
private void reMultipart(Multipart multipart,String path) throws Exception {
System.out.println("邮件"+path.split("\\\\")[2]+"此块共有" + multipart.getCount() + "部分组成");
// 依次处理各个部分
for (int j = 0, n = multipart.getCount(); j < n; j++) {
//System.out.println("处理第" + j + "部分");
Part part = multipart.getBodyPart(j);//解包, 取出 MultiPart的各个部分, 每部分可能是邮件内容,
// 也可能是另一个小包裹(MultipPart)
// 判断此包裹内容是不是一个小包裹, 一般这一部分是 正文 Content-Type: multipart/alternative
if (part.getContent() instanceof Multipart) {
Multipart p = (Multipart) part.getContent();// 转成小包裹
//递归迭代
reMultipart(p,path);
} else {
rePart(part,path);
}
}
}
原本报错:
DEBUG POP3: authentication command trace suppressed
DEBUG POP3: authentication command failed
解决方法:
加上下面几句(代码内已加上):
final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
props.setProperty("mail.pop3.socketFactory.class", SSL_FACTORY);
props.setProperty("mail.pop3.port", "995");
//需要指明使用995端口,否则默认的110端口不支持SSL