引言:
JavaMai下载地址l jar包:http://java.sun.com/products/javamail/downloads/index.html
此篇是紧随上篇文章而封装出来的,阅读本篇章建议先阅读上一篇 -->javamail模拟邮箱功能获取邮件内容-中级实战篇【内容|附件下载方法】(javamail API电子邮件实例)
在上一篇中,讲解了邮件获取内容的两个方法(HTML和附件),简单介绍了邮件发送和内容获取的相同和不同之处,并且引入了新类-->javax.mail.Store:容器类。同时对SMTP和POP3协议对应的大致功能进行说明。
本章节将来演示javamail的邮件回复功能,本来以为API会提供一个自动封装好的方法来供调用,找了好久,并网上查阅了不少资料,原来根本都没有实现回复功能,API只有一个方法:reply(Boolean)方法,来提示邮件回复时的收件人信息,但这个方法有点鸡肋,他仅在发送邮件的时候,除非你指明了回复时的邮件地址,才能回去到,并且在回复邮件时,完全可以新增收件人。
思路:
既然API没有给我们直接回复的方法,那就发挥我们自强不息的精神吧(吼吼),那么该怎么回复一封邮件呢?
看完思路总结,立马清晰了,原来邮件回复就是邮件查看和发送的合体!
作前感悟:
代码真的是写的越多,错的越多,功能越多,就会发现模块是否能够灵活运转。杯具不断在上演(有悲剧才能衬托喜剧嘛),从第一章节至今,不断的在对模块进行拆分,以便对功能细化和简单化,但是还是不够,看过上一篇的朋友会发现,在邮件接收业务类里面,我们把邮件的接收和读取|内容的提取封装在了一个方法里面,当初没有感觉到不妥,但是用在回复里面就露出马脚了,当你想获取邮件的时候,你用不到邮件查看业务类的方法,因为他把查看和内容提取耦合在一起,不能简单的提取出你想要的邮件。所以,这次在新增回复方法的同时,对邮件获取业务类进行拆分,拆分为获取邮件,和邮件内容的提取两大部分,这样在之后如果有要获取邮件的时候就能共用到它的部分代码。
示例结构:
本篇示例代码分了5类(把前一章节的获取业务类进行拆分,并且新增回复业务类,其余3类没有变动)
实例代码演示:
**************复制此五个类可直接跑测试**************注释部分我尽可能详细讲解每个方法的作用**************
EmailEntity 类 (有复制之前章节的可忽略此类)
package com.cplatform.movie.back.javamail; import javax.mail.Authenticator; import javax.mail.PasswordAuthentication; /** * 邮件基础实体类 * @author liuyt * @date 2014-10-24 下午2:12:10 */ public class EmailEntity extends Authenticator { /** * 用户名(登录邮箱) */ protected static String username; /** * 密码 */ protected static String password; /** * 初始化邮箱地址和密码 * @param username 邮箱 * @param password 密码 */ public EmailEntity(String username, String password) { EmailEntity.username = "这里填写发件箱地址"; EmailEntity.password = "这里填写发件箱密码"; } /** * 重写自我检验方法 */ @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(username, password); } String getPassword() { return password; } String getUsername() { return username; } public void setPassword(String password) { EmailEntity.password = password; } public void setUsername(String username) { EmailEntity.username = username; } }
EmailServiceEnity 类 (有复制之前章节的可忽略此类)
package com.cplatform.movie.back.javamail; import java.io.UnsupportedEncodingException; import java.util.Properties; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.NoSuchProviderException; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import com.cplatform.movie.back.javamail.SimpleEmailFunction.MyAuthenricator; /** * 邮件服务支持类 * @author liuyt * @date 2014-10-24 下午2:11:38 */ public class EmailServiceEnity extends EmailEntity{ // java.util.propertis 类 private transient Properties props; // 一个邮件会话 private transient Session session; // 邮件消息 private transient MimeMessage message; // 邮件传输对象 private Transport transport; // 邮件发送内容 private String content; // 邮件内容格式 private final String CONTENT_TYPE_HTML = "text/html;charset=utf-8"; // 端口号 private final static String MAIL_PORT = "25"; // 邮箱协议常量 public static final String MAIL_PROTOCOL_SMTP = "smtp"; public static final String MAIL_PROTOCOL_POP3 = "pop3"; // 邮箱所使用的协议 private String mailProtocol; // 邮箱服务器列表 private String hostPort; //////////////////分//////////////////界/////////////////////线///////////////////////////// /** * 实参构造 * @param mailToaddress 收件人地址,多个以逗号隔开 * @param content 邮件内容 * @throws MessagingException * @throws UnsupportedEncodingException */ public EmailServiceEnity(String mailToaddress, String content, String mailProtocol) throws UnsupportedEncodingException, MessagingException { super(username, password); this.setMailProtocol(mailProtocol); this.setHostPort(mailProtocol +"."+ username.split("@")[1]); this.content = content; this.session = this.initSession(); this.message = this.initMessage(this.getSession(), mailToaddress); // 这里需要对协议进行判断,SMTP:为发送协议(初始化Transport) POP3:则为接收协议(只能初始化Store,在接收邮件章节用到) if(this.getMailProtocol().equals(MAIL_PROTOCOL_SMTP)){ this.transport = this.initTransport(this.getSession()); } } /** * 初始化perps文件 * @return */ public Properties initPrope() { // 初始化props文件 props = new Properties(); props.setProperty("mail.transport.protocol", this.getMailProtocol());//发送邮件协议 props.put("mail.smtp.auth", "true"); //需要验证 props.put("mail.smtp.host", this.getHostPort()); //服务器地址 return props; } /** * 初始化session会话 * @return */ public Session initSession() { session = Session.getInstance(this.initPrope(),new MyAuthenricator(username, password)); session.setDebug(true); return session; } /** * 初始化Message消息 * @param session * @return * @throws MessagingException * @throws UnsupportedEncodingException */ public MimeMessage initMessage(Session session, String mailToaddress) throws MessagingException, UnsupportedEncodingException { message = new MimeMessage(session); // 设置发件人地址 message.setFrom(new InternetAddress(username, "要显示的发件人名称")); // 设置邮件主题 message.setSubject("主题:默认主题"); // 设置邮件发送内容和内容的content-type message.setContent(content.toString(),this.CONTENT_TYPE_HTML); // 设置邮件接收人地址 if(mailToaddress.trim().length() > 0) { String [] address = mailToaddress.split(","); for(int i=0; i<address.length; i++) { // addRecipient(该方法为添加收件人列表,参数一为类型:TO-收件人,CC-抄送,参数二为一个邮件地址) message.addRecipient(Message.RecipientType.TO, new InternetAddress(address[i].trim())); // 下面方法为传递一个收件地址字符串 (二者方法任选其一即可) message.addRecipients(Message.RecipientType.CC, address[i].trim()); } } return message; } /** * 初始化Transport * @param session * @return * @throws NoSuchProviderException */ public Transport initTransport(Session session) throws NoSuchProviderException { transport = session.getTransport(); return transport; } /***************** 提供必要的get set方法支持 ************飘逸的分割线****************/ public Session getSession() { return session; } public void setSession(Session session) { this.session = session; } public MimeMessage getMessage() { return message; } public void setMessage(MimeMessage message) { this.message = message; } public Properties getProps() { return props; } public String getContentTypeHtml() { return CONTENT_TYPE_HTML; } public static String getMailPort() { return MAIL_PORT; } public String getHostPort() { return hostPort; } public void setHostPort(String hostPort) { this.hostPort = hostPort; } public Transport getTransport() { return transport; } public void setTransport(Transport transport) { this.transport = transport; } public void setProps(Properties props) { this.props = props; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getCONTENT_TYPE_HTML() { return CONTENT_TYPE_HTML; } public static String getMailProtocolSmtp() { return MAIL_PROTOCOL_SMTP; } public static String getMailProtocolPop3() { return MAIL_PROTOCOL_POP3; } public String getMailProtocol() { return mailProtocol; } public void setMailProtocol(String mailProtocol) { this.mailProtocol = mailProtocol; } }
SendEmailService 类 (有复制之前章节的可忽略此类)
package com.cplatform.movie.back.javamail; import java.io.File; import java.io.UnsupportedEncodingException; import javax.activation.DataHandler; import javax.activation.FileDataSource; import javax.mail.BodyPart; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Multipart; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMultipart; import com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimeUtility; /** * 邮件发送业务类 * @author liuyt * @date 2014-10-26 上午11:17:45 */ public class SendEmailService { private EmailServiceEnity serviceEnity; /** * 发送HTML内容邮件 (包括TEXT格式) * @throws MessagingException */ public void sendHtmlOrText() throws MessagingException { this.send(); } /** * 附件发送 * @param file java.io.File * @param fileName 附件名 * @throws MessagingException * @throws UnsupportedEncodingException */ public void sendFile(File file, String fileName) throws MessagingException, UnsupportedEncodingException { // 获得Message实例 Message message = serviceEnity.getMessage(); // 创建multipart容器用来容纳bodyPart(部件) Multipart multipart = new MimeMultipart(); /** * 创建一个BodyPart内容报文 * 每个消息都有多个部分组成,每个部分是一个BodyPart报文部分,多个BodyPart部分又同时组成一个Multipart的容器 */ BodyPart messageBodyPart = new MimeBodyPart(); // 设置该报文的内容 messageBodyPart.setContent(serviceEnity.getContent(),serviceEnity.getCONTENT_TYPE_HTML()); // 添加bodyPart报文部分到multiPart容器 multipart.addBodyPart(messageBodyPart); // 创建一个附件报文 messageBodyPart = new MimeBodyPart(); // 文件源 FileDataSource fds = new FileDataSource(file); // 设置邮件的内含附件 (设置数据源为复件) messageBodyPart.setDataHandler(new DataHandler(fds)); // 设置附件的文件名,需进行编码,否则文件名会乱码 messageBodyPart.setFileName(MimeUtility.encodeText(fileName)); // 添加到容器 multipart.addBodyPart(messageBodyPart); // 添加报文容器到消息实例 message.setContent(multipart); // 发送消息 this.send(); } /** * 发送 * 推荐使用方法一,因为方法二如果收件人为多个的话,会为每个人都打开一个Transport通道再关闭 * 而方法一在发送过程中一直保持连接通常,所有操作完成后才关闭 * @throws MessagingException */ public void send() throws MessagingException { Message message = serviceEnity.getMessage(); // 方法一 serviceEnity.getTransport().connect(); serviceEnity.getTransport().sendMessage(message, message.getAllRecipients()); serviceEnity.getTransport().close(); // 方法二 // Transport.send(this.getMessage()); } // main 方法测试 public static void main(String[] args) { SendEmailService service; EmailServiceEnity enity; // 多个收件人中间以逗号间隔 String mailToaddress = "[email protected],[email protected]"; // 正文(内容)部分 String content = "点击进入» <a href='http://www.cnblogs.com/liuyitian'>刘一天的博客</a>"; try { service = new SendEmailService(); enity = new EmailServiceEnity(mailToaddress, content, EmailServiceEnity.MAIL_PROTOCOL_SMTP); service.setServiceEnity(enity); service.sendHtmlOrText(); // 测试HTML文本 /** * 切勿使用同一个EmailServiceEnity实例来发送不同内容,如有需要就再初始化一个新实例(否则附件发送失败且乱码) * 因为每个实例在发送完邮件后就会关闭Transport */ enity = new EmailServiceEnity(mailToaddress, content, EmailServiceEnity.MAIL_PROTOCOL_SMTP); service.setServiceEnity(enity); // 传入一个绝对位置的文件路径 File file = new File("d:/data/adimg/20141022/09/ad_20141022094708943.jpg"); service.sendFile(file,"测试附件发送.jpg"); // 测试复件发送 } catch (UnsupportedEncodingException e1) { e1.printStackTrace(); } catch (MessagingException e1) { e1.printStackTrace(); } } public EmailServiceEnity getServiceEnity() { return serviceEnity; } public void setServiceEnity(EmailServiceEnity serviceEnity) { this.serviceEnity = serviceEnity; } }
LookEmailService 类 (有功能的细分操作)
package com.cplatform.movie.back.javamail; import java.io.DataInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import javax.mail.BodyPart; import javax.mail.Folder; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Multipart; import javax.mail.Store; import javax.mail.internet.MimeUtility; /** * 查看|接收邮件业务类 * @author liuyt * @date 2014-10-24 下午2:04:48 */ public class LookEmailService { private transient EmailServiceEnity emailService; private transient Folder folder; private transient Store store; // 初始化对象时附带初始化必要参数 public LookEmailService() throws UnsupportedEncodingException, MessagingException { this.initStoreAndFolder(); } // 主方法,这里演示单个邮件的内容读取和附件下载 public void lookEmail() throws MessagingException, IOException, InterruptedException { // 获得邮件 Message[] messages = this.getAllMessage(); // 方法一,得到全部邮件数组 Message message = this.getMessageByIndex(messages.length); // 方法二 System.out.println("邮件接收时间:"+message.getSentDate()); System.out.println("邮件发送者:"+message.getFrom()[0]); System.out.println("邮件主题:"+message.getSubject()); System.out.println("邮件内容:"+message.getContent()); // 内存地址 System.out.println("***********************飘逸的分割线*****************************"); /** * 判断内容是否由多部件组成 */ if(!message.getContentType().contains("multipart/")) { return; } // 得到邮件的Multipart(内容总部件--【包涵附件】) Multipart multipart = (Multipart) message.getContent(); int count = multipart.getCount(); // 部件个数 for(int i=0; i<count; i++) { // 单个部件 注意:单个部件有可能又为一个Multipart,层层嵌套 BodyPart part = multipart.getBodyPart(i); // 单个部件类型 String type = part.getContentType().split(";")[0]; /** * 类型众多,逐一判断,其中TEXT、HTML类型可以直接用字符串接收,其余接收为内存地址 * 可能不全,如有没判断住的,请自己打印查看类型,在新增判断 */ if(type.equals("multipart/alternative")) { // HTML (文本和超文本组合) System.out.println("超文本:" + part.getContent().toString()); }else if(type.equals("text/plain")) { // 纯文本 System.out.println("纯文本:" + part.getContent().toString()); }else if(type.equals("text/html")){ // HTML标签元素 System.out.println("HTML元素:" + part.getContent().toString()); }else if(type.equals("multipart/related")){ // 内嵌资源 (包涵文本和超文本组合) System.out.println("内嵌资源:" + part.getContent().toString()); }else if(type.contains("application/")) { // 应用附件 (zip、xls、docx等) System.out.println("应用文件:" + part.getContent().toString()); }else if(type.contains("image/")) { // 图片附件 (jpg、gpeg、gif等) System.out.println("图片文件:" + part.getContent().toString()); } /*****************************************获取邮件内容方法***************************************************/ /** * 附件下载 * 这里针对image图片类型附件做下载操作,其他类型附件同理 */ if(type.contains("image/")) { // 打开附件的输入流 DataInputStream in = new DataInputStream(part.getInputStream()); // 一个文件输出流 FileOutputStream out = null; // 获取附件名 String fileName = part.getFileName(); // 文件名解码 fileName = MimeUtility.decodeText(fileName); // 根据附件名创建一个File文件 File file = new File("d:/data/" + fileName); // 查看是否有当前文件 Boolean b = file.exists(); if(!b) { out = new FileOutputStream(file); int data; // 循环读写 while((data=in.read()) != -1) { out.write(data); } System.out.println("附件:【" + fileName + "】下载完毕,保存路径为:" + file.getPath()); } // 关流 if(in != null) { in.close(); } if(out != null) { out.close(); } } /** * 获取超文本复合内容 * 他本是又是一个Multipart容器 * 此时邮件会分为TEXT(纯文本)正文和HTML正文(HTML标签元素) */ if(type.equals("multipart/alternative")) { Multipart m = (Multipart) part.getContent(); for (int k=0; k<m.getCount(); k++) { if(m.getBodyPart(k).getContentType().startsWith("text/plain")) { // 处理文本正文 System.out.println("TEXT文本内容:"+"\n" + m.getBodyPart(k).getContent().toString().trim()+"\n"); } else { // 处理 HTML 正文 System.out.println("HTML文本内容:"+"\n" + m.getBodyPart(k).getContent()+"\n"); } } } } /** * 最后千万别忘记了关闭 */ folder.close(false); // false为不更新邮件,true为更新,一般在删除邮件后使用 store.close(); } /** * 返回一个指定条目的邮件 * @return * @throws MessagingException */ public Message getMessageByIndex(int index) throws MessagingException { return folder.getMessage(index); } /** * 返回邮箱所有邮件 * @param args * @throws MessagingException */ public Message[] getAllMessage() throws MessagingException { return folder.getMessages(); } /** * 返回指定区间(起始条目和结束条目)的邮件 * @param args * @throws MessagingException */ public Message[] getMessageBySpace(int start, int end) throws MessagingException { return folder.getMessages(start, end); } /** * 初始化Store连接和获取Folder邮件夹 * @throws MessagingException * @throws UnsupportedEncodingException */ public void initStoreAndFolder() throws UnsupportedEncodingException, MessagingException { /** * 接收邮件时,邮箱的协议为POP3,SMTP为邮件传输协议,这里别搞混了 * 并且检查你的邮箱设置POP3功能是否开启 */ emailService = new EmailServiceEnity("", "",EmailServiceEnity.MAIL_PROTOCOL_POP3); /** * javax.mail.Store类用于连接邮件接收服务器,并访问邮件接收服务器上的各个邮箱夹 * javax.mail.Folder类表示邮件夹 * 通过一个Session我们可以拿到一个邮箱对应的Store */ store = emailService.getSession().getStore(emailService.getMailProtocol()); store.connect(emailService.getHostPort(), EmailServiceEnity.username, EmailServiceEnity.password); /** * 通过Store拿到收件箱文件夹 * INBOX 标识获取到邮件夹里的收件箱 (对于POP3协议仅INBOX可用--蛋疼哦) * 并以只读方式打开收件箱 */ folder = store.getFolder("INBOX"); folder.open(Folder.READ_ONLY); } // main 方法简单测试 public static void main(String[] args) { try { new LookEmailService().lookEmail(); } catch (IOException e) { e.printStackTrace(); } catch (MessagingException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } /***************GET********************SET***********************/ public EmailServiceEnity getEmailService() { return emailService; } public void setEmailService(EmailServiceEnity emailService) { this.emailService = emailService; } public Store getStore() { return store; } public void setStore(Store store) { this.store = store; } public Folder getFolder() { return folder; } public void setFolder(Folder folder) { this.folder = folder; } }
ReplyEmailService 类 (邮件回复业务类)
package com.cplatform.movie.back.javamail; import java.io.IOException; import java.io.UnsupportedEncodingException; import javax.mail.Address; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.internet.InternetAddress; /** * 邮件回复业务类 * @author liuyt * @date 2014-10-27 上午8:48:29 */ public class ReplyEmailService { // 服务实体类 private transient EmailServiceEnity emailService; // 回复内容 private transient String content; /** * 邮件回复方法 * 思路:先获取要回复的邮件,然后获取邮件的各个详情,把详情附加给新建邮件,发送 * @throws MessagingException * @throws IOException */ public void replyEmail () throws MessagingException, IOException { // 获取邮件,和查看邮件相同,调用方法即可 LookEmailService lookEmail = new LookEmailService(); emailService = new EmailServiceEnity("", "",EmailServiceEnity.MAIL_PROTOCOL_POP3); lookEmail.setEmailService(emailService); Message message = lookEmail.getMessageByIndex(lookEmail.getAllMessage().length); /*************上面为获取邮件************到此处是一个分水岭****************下面为发送邮件****************/ Message reply = this.newReplyMessage(message); // 布尔值,true为回复给所有人,false为只回复给发送者 reply.reply(true); // 发送回复 emailService.getTransport().connect(); emailService.getTransport().sendMessage(reply, reply.getAllRecipients()); emailService.getTransport().close(); } /** * 组装要回复的邮件 * @param message * @return * @throws MessagingException * @throws IOException */ public Message newReplyMessage(Message message) throws MessagingException, IOException { // 出新初始化一个服务类,因为之前为获取邮件,协议为POP3,当前要发送邮件,协议为SMTP emailService = new EmailServiceEnity("", "", EmailServiceEnity.MAIL_PROTOCOL_SMTP); // 新创建一个邮件 Message reply = emailService.getMessage(); // 获取原邮件的发件人--》为新邮件的接收人 Address[] addressTo = message.getFrom(); // 获取原邮件内容(暂支持文本和HTML单部件,多部件另需递归循环原邮件,并新增) Object content = message.getContent(); // 获取原邮件主题 String subObject = message.getSubject(); // 获取邮件发送时间 String date = message.getSentDate().toString(); /** 组装新邮件*/ // 设置回复内容,新内容+原文 reply.setContent(this.content + "<br/><br/><br/>" + date +","+ InternetAddress.toString(addressTo) + "写到:<br/>> " + content, "text/html;charset=utf-8"); // 设置邮件回复主题 reply.setSubject("Re:" + subObject); // 设置收件人:原发件人 reply.addRecipients(Message.RecipientType.TO, addressTo); return reply; } // 简单main方法测试 public static void main(String[] args) { try { ReplyEmailService replySend = new ReplyEmailService(); replySend.setContent("你好,邮件已收到! <a href='http://www.cnblogs.com/liuyitian'><<--点击进入我的博客-->></a>"); replySend.replyEmail(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (MessagingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /**********************GET***********************SET****************************/ public EmailServiceEnity getEmailService() { return emailService; } public void setEmailService(EmailServiceEnity emailService) { this.emailService = emailService; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } }
代码部分都是在之前的基础上进行累加的,没变化的类直接进行折叠。到目前:发、收、回基本解决。
本章小结:
至此重要功能都讲的差不多了,这几天来陆陆续续不停的再对代码进行增强,接下来等到这周末了再对其他功能进行封装,这周要休养生息了,看看别的值得学习的东西,换个思路,还会再回来的,还有>>>>如果对你有帮助,还请动下小指别忘推荐哦。
javamail API 系列篇:
写作不易,难免有疏漏和错误,还请慷慨指正,不错请推荐
ps:欢迎转载,转载请注明出处:http://www.cnblogs.com/liuyitian/p/4054437.html
每天多学一点点 代码少敲一点点