SMTP (Simple Mail Transfer Protocol),即简单邮件传输协议
默认端口是25,通过SSL协议加密之后的默认端口是465
用户必须首先设置 SMTP 服务器,然后才能配置电子邮件客户端与其连接。完成此操作后,用户按下电子邮件上的“发送”按钮,并在客户端和服务器之间建立 SMTP 连接以允许发送电子邮件。SMTP 连接建立在传输控制协议 (TCP)连接之上。
SMTP发件服务器,如smtp.163.com、smtp.qq.com
POP3是Post Office Protocol 3的简称,即邮局协议的第3个版本。
默认端口是110,通过SSL协议加密之后的默认端口是995。
POP3允许用户从服务器上把邮件存储到本地主机(即自己的计算机)上,但是对邮件的操作并不会反馈到邮箱服务器上。
POP3收件服务器,如pop.163.com、pop.qq.com
IMAP (Internet Mail Access Protocol),即交互式邮件存取协议,是一个应用层协议,
默认端口是143,通过SSL协议加密之后的默认端口是993。
开启了IMAP后,您在电子邮件客户端收取的邮件仍然保留在服务器上,同时在客户端上的操作都会反馈到服务器上,如:删除邮件,标记已读等,服务器上的邮件也会做相应的动作。
IMAP收件服务器,如imap.163.com、imap.qq.com
现在邮箱大多为邮箱客户端设置了独立密码或授权码(即通过Smtp方式发送邮件密码处不是填邮箱登录密码,而是要填授权码)
以163邮箱为例,开通过程如下:
进去后,点击开启。开启完成后状态变为”已开启“
JavaMail是Sun发布的用来处理email的API。它可以方便地执行一些常用的邮件传输。
JavaMail提供的是一些标准的邮件管理接口,有如下几个核心组件。
虽然JavaMail是Sun的API之一,但它还没有被加在标准的java开发工具包中(Java Development Kit),使用JavaMail进行邮件的开发,因此如果需要使用的话你需要首先从Oracle官网上下载JavaMail的开发包。
我们可以在pom文件中引入如下依赖
javax.mail
mail
1.4.7
由于我使用commons-email做邮件发送,而且它会自动引入javax.mail依赖,就不单独引入了
commons-email它构建在Java Mail API 之上,主要是为了简化它。
pom文件依赖
org.apache.commons
commons-email
1.5
Commons-Email的核心是Email类,它是一个抽象类,提供了发送邮件的核心功能。具体有以下几个实现类
HtmlEmail
HtmlEmail类通常用来发送html格式的邮件,他也支持邮件携带普通文本、附件和内嵌图片。
ImageHtmlEmail
ImageHtmlEmail类通常是用来发送Html格式并内嵌图片的邮件,它拥有所有HtmlEmail的功能,但是图片主要是以html内嵌的为主。
下面开始,是使用Commons Email发送邮件的举例
SimpleEmail:发送简单邮件,即纯文本邮件
public static void main(String[] args) throws EmailException {
Email email = new SimpleEmail();
//设置邮箱服务器,这里我使用的163邮箱
email.setHostName("smtp.163.com");
//填写邮件服务器端口:465和25选填
email.setSmtpPort(465);
//开启debug日志
//email.setDebug(true);
//设置用户名(邮箱)和授权码(授权码是用于登录第三方邮件客户端的专用密码)
//邮箱开启授权只需要登陆邮件,在里边设置一下就行了.
email.setAuthenticator(new DefaultAuthenticator("[email protected]", "your_auth_password"));
//开启ssl连接
email.setSSLOnConnect(true);
//填写发送者的邮箱
email.setFrom("[email protected]");
//填写发送日期
email.setSentDate(new Date());
//填写邮件标题
email.setSubject("TestMail-1月20号");
//邮件内容
email.setMsg("This is a test mail ... :-)");
//填写接收者的邮箱,我是发给了自己的qq邮箱
email.addTo("[email protected]");
//发送
email.send();
}
执行结果:
MultiPartEmail类通常用来发送流媒体类型的邮件,允许附件和文本类型数据一起发送。
public static void main(String[] args) throws EmailException {
MultiPartEmail email = new MultiPartEmail();
//设置邮箱服务器,在这里我使用的163邮箱
email.setHostName("smtp.163.com");
//填写邮件服务器端口:465和25选填
email.setSmtpPort(465);
//开启debug日志
//email.setDebug(true);
//设置用户名(邮箱)和授权码(授权码是用于登录第三方邮件客户端的专用密码)
//邮箱开启授权只需要登陆邮件,在里边设置一下就行了.
email.setAuthenticator(new DefaultAuthenticator("[email protected]", "your_auth_password"));
//开启ssl连接
email.setSSLOnConnect(true);
//填写发送者的邮箱
email.setFrom("[email protected]");
//填写发送日期
email.setSentDate(new Date());
//填写邮件标题
email.setSubject("TestMail-Attachment-1月20号");
//邮件内容
email.setMsg("我的附件发给你了.");
//填写接收者的邮箱,我是发给了自己的qq邮箱
email.addTo("[email protected]");
//email.addCc("抄送人邮箱");
// 添加附件
EmailAttachment attachment = new EmailAttachment();
//填写附件位置
attachment.setPath("C:\\Users\\Administrator\\Desktop\\问题收集.txt");
attachment.setDisposition(EmailAttachment.ATTACHMENT);
//填写附件描述
attachment.setDescription("附件描述");
//填写附件名称,要与上面一致填写路径中的附件名称一致,要不然收到附件的时候会有问题
attachment.setName("问题收集.txt");
email.attach(attachment);
//发送
email.send();
}
执行结果:
接收邮件常用的协议有pop3,imap和exchange。exchange是微软的邮箱协议,Jakarta Mail暂不支持。
JavaMail API中定义了一个java.mail.Store类,应用程序调用这个类的方法就可以获得用户邮箱中的各个邮件夹的信息。
javaMail中的使用Folder对象表示邮件夹,通过Folder对象的方法应用程序进而又可以获得该邮件夹中的所有邮件信息。
而每封邮件信息JavaMail又分别使用了一个Message对象进行封装
public static void main(String[] args) throws Exception {
// 创建一个有具体连接信息的Properties对象
Properties prop = new Properties();
//prop.setProperty("mail.debug", "true");
prop.setProperty("mail.store.protocol", "pop3");
//pop3服务器,我这里使用163邮箱
prop.setProperty("mail.pop3.host", "pop.163.com");
//pop默认110端口
prop.setProperty("mail.pop3.port", "110");
// 1、创建session
Session session = Session.getInstance(prop);
// 2、通过session得到Store对象
Store store = session.getStore();
// 3、连上邮件服务器
store.connect("[email protected]", "your_auth_password");
// 4、获得邮箱内的邮件夹
Folder folder = store.getFolder("INBOX");
folder.open(Folder.READ_ONLY);
// 由于POP3协议无法获知邮件的状态,所以getUnreadMessageCount得到的是收件箱的邮件总数
System.out.println("未读邮件数: " + folder.getUnreadMessageCount());
// 获得邮件夹Folder内的所有邮件Message对象
Message[] messages = folder.getMessages();
for (int i = 0; i < messages.length; i++) {
String subject = messages[i].getSubject();
String from = (messages[i].getFrom()[0]).toString();
System.out.println("第 " + (i + 1) + "封邮件的主题:" + subject);
//还可以从Message对象中获取: 发件人、收件人、发送时间、邮件正文...
}
// 5、关闭
folder.close(false);
store.close();
}
1、由于POP3协议无法获知邮件的状态,所以getUnreadMessageCount()获取未读邮件数得到的是收件箱的邮件总数
2、通过POP3协议获得的Store对象调用这个方法时,邮件夹名称只能指定为"INBOX"。
执行结果:
同样需要先开启授权码
邮箱的收件服务器地址,可以是smtp.qq.com吗??
final static String USER = "[email protected]"; // 用户名
final static String PASSWORD = "your_auth_password"; // 密码
public final static String MAIL_SERVER_HOST = "imap.163.com"; // 邮箱服务器
public static void main(String[] args) throws Exception {
// 创建一个有具体连接信息的Properties对象
Properties prop = new Properties();
prop.setProperty("mail.debug", "true");
//指定接收的邮件协议
prop.setProperty("mail.store.protocol", "imap");
prop.setProperty("mail.imap.host", MAIL_SERVER_HOST);
prop.setProperty("mail.imap.port", "143");
// 1、创建session
Session session = Session.getInstance(prop);
// 2、通过session得到Store对象
Store store = session.getStore("imaps");
// 3、连上邮件服务器
store.connect(MAIL_SERVER_HOST, USER, PASSWORD);
// 4、获得邮箱内的邮件夹 POP协议的话,这里只能是INBOX
Folder folder = store.getFolder("INBOX");
System.out.println("INBOX exist:" + folder.exists());
//
Folder defaultFolder = store.getDefaultFolder();
Folder[] folders = defaultFolder.list("*");
for (Folder folder1 : folders) {
IMAPFolder imapFolder = (IMAPFolder) folder1;
//javamail中使用id命令校验chekOpened,去掉
imapFolder.doCommand(new IMAPFolder.ProtocolCommand() {
@Override
public Object doCommand(IMAPProtocol p) throws ProtocolException {
p.id("FUTONG");
return null;
}
});
}
//以只读方式打开收件箱
folder.open(Folder.READ_ONLY);
System.out.println("邮件总数:" + folder.getMessageCount());
//由于POP3协议无法获知邮件的状态,所以getUnreadMessageCount()得到的是收件箱的邮件总数
System.out.println("未读邮件数:" + folder.getUnreadMessageCount());
// 获得邮件夹Folder内的所有邮件Message对象
Message[] messages = folder.getMessages();
for (int i = 0; i < messages.length; i++) {
String subject = messages[i].getSubject();
String from = (messages[i].getFrom()[0]).toString();
System.out.println("第 " + (i + 1) + "封邮件的主题:" + subject);
System.out.println("第 " + (i + 1) + "读写标识:" + messages[i].getFlags().contains(Flags.Flag.SEEN));
}
// 5、关闭
folder.close(false);
store.close();
}
执行结果:
根据MimeType类型的不同执行不同的操作,一步一步的解析
if (message.isMimeType("TEXT/*")) { // 仅包含正文的简单邮件
System.out.println("邮件正文: " + message.getContent());
} else {
parseMessage((MimeMultipart) message.getContent()); // 解析稍复杂邮件
}
/**
* 解析邮件
*/
public static void parseMessage (MimeMultipart part) throws MessagingException, IOException {
byte[] bytes = new byte[1024];
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
for (int i = 0; i < part.getCount(); i++) {
BodyPart body = part.getBodyPart(i);
if (body.isMimeType("text/html")) {
System.out.println("html格式正文: " + (String) body.getContent());
} else if (body.isMimeType("text/plain")) {
System.out.println("纯文本格式正文: " + (String) body.getContent());
} else if (body.isMimeType("multipart/*")) {
MimeMultipart multipart = (MimeMultipart) body.getContent();
parseMessage(multipart);
} else { // 附件
InputStream inputStream = body.getDataHandler().getInputStream();
int len = 0;
while( (len = inputStream.read(bytes)) != -1 ){
outStream.write(bytes, 0, len);
}
inputStream.close();
byte[] data = outStream.toByteArray();
String fileName = body.getFileName();
File tempFile = new File(FileUtils.getTempDirectoryPath() + File.separator + System.currentTimeMillis() + fileName );
FileOutputStream fileOutputStream = new FileOutputStream(tempFile);
fileOutputStream.write(data);
fileOutputStream.close();
System.out.println("邮件附件本地路径: " + tempFile.getAbsolutePath());
}
}
}
// 获得邮件夹Folder内的所有邮件Message对象
Message[] messages = folder.getMessages();
for (int i = 0; i < messages.length; i++) {
String subject = messages[i].getSubject();
String from = (messages[i].getFrom()[0]).toString();
System.out.println("第 " + (i + 1) + "封邮件的主题:" + subject);
System.out.println("第 " + (i + 1) + "封邮件的发件人地址:" + from);
System.out.println("第 " + (i + 1) + "读写标识:" + messages[i].getFlags().contains(Flags.Flag.SEEN));
Part mp = (Part) messages[i];
boolean containAttach = isContainAttach(mp);
if (containAttach) {
//包含附件,进行附件下载
System.out.println("第 " + (i + 1) + "封邮件,包含附件");
saveAttachMent(mp, "C:\\Users\\Administrator\\Desktop\\Attach");
}
}
这个不一定是static方法,我是为了在main方法里测试,才这么写的
/**
* * 判断此邮件是否包含附件
*
* @throws MessagingException
*/
public static boolean isContainAttach(Part part) throws Exception {
boolean attachflag = false;
String contentType = part.getContentType();
if (part.isMimeType("multipart/*")) {
Multipart mp = (Multipart) part.getContent();
// 获取附件名称可能包含多个附件
for (int j = 0; j < mp.getCount(); j++) {
BodyPart mpart = mp.getBodyPart(j);
String disposition = mpart.getDescription();
if ((disposition != null)
&& ((disposition.equals(Part.ATTACHMENT)) || (disposition
.equals(Part.INLINE)))) {
attachflag = true;
} else if (mpart.isMimeType("multipart/*")) {
attachflag = isContainAttach((Part) mpart);
} else {
String contype = mpart.getContentType();
if (contype.toLowerCase().indexOf("application") != -1)
attachflag = true;
if (contype.toLowerCase().indexOf("name") != -1)
attachflag = true;
}
}
} else if (part.isMimeType("message/rfc822")) {
attachflag = isContainAttach((Part) part.getContent());
}
return attachflag;
}
/**
* * 【保存附件】
*
* @throws Exception
* @throws IOException
* @throws MessagingException
* @throws Exception
*/
public static void saveAttachMent(Part part, String saveAttachPath) throws Exception {
String fileName = "";
if (part.isMimeType("multipart/*")) {
Multipart mp = (Multipart) part.getContent();
for (int j = 0; j < mp.getCount(); j++) {
BodyPart mpart = mp.getBodyPart(j);
String disposition = mpart.getDescription();
if ((disposition != null)
&& ((disposition.equals(Part.ATTACHMENT)) || (disposition
.equals(Part.INLINE)))) {
fileName = mpart.getFileName();
if (fileName.toLowerCase().indexOf("GBK") != -1) {
fileName = MimeUtility.decodeText(fileName);
}
saveFile(fileName, mpart.getInputStream(), saveAttachPath);
} else if (mpart.isMimeType("multipart/*")) {
fileName = mpart.getFileName();
} else {
fileName = mpart.getFileName();
if ((fileName != null)) {
fileName = MimeUtility.decodeText(fileName);
saveFile(fileName, mpart.getInputStream(), saveAttachPath);
}
}
}
} else if (part.isMimeType("message/rfc822")) {
saveAttachMent((Part) part.getContent(), saveAttachPath);
}
}
/**
* * 【真正的保存附件到指定目录里】
*/
private static void saveFile(String fileName, InputStream in, String storedir) throws Exception {
String osName = System.getProperty("os.name");
String separator = "";
if (osName == null)
osName = "";
if (osName.toLowerCase().indexOf("win") != -1) {
// 如果是window 操作系统
separator = "/";
if (storedir == null || storedir.equals(""))
storedir = "c:\tmp";
} else {
// 如果是其他的系统
separator = "/";
storedir = "/tmp";
}
File strorefile = new File(storedir + separator + fileName);
BufferedOutputStream bos = null;
BufferedInputStream bis = null;
try {
bos = new BufferedOutputStream(new FileOutputStream(strorefile));
bis = new BufferedInputStream(in);
int c;
while ((c = bis.read()) != -1) {
bos.write(c);
bos.flush();
}
} catch (Exception e) {
// TODO: handle exception
} finally {
bos.close();
bis.close();
}
}