Java: JavaMail 初试(一)
前言:以前的我,很喜欢写东西,写一写所想所见所闻所感,但是工作之后,总不能写出让自己满意的文章,突发奇想,能否利用写博客的时机,将其写成类似散文似的博文呢?哈哈...
邮件功能尝试:作为一个小菜鸟,对于技术性的东西却有特殊的偏好,每每通过自己学习而获得新的知识,总会如孩童拥有一个新奇的玩意儿一样,欢欣雀跃。第一次写邮件功能,这一篇文章就是记录下,我在参考网上前辈的博文敲代码时遇到的情况,代码大部分都是参考的网上的博文,感谢!我尽量写的详细,以备后用和帮助后来者。
言归正传:
1.环境及工具:myeclipse 9.0 ; jdk7 ;
2.项目截图:
3.jar备注:需要的jar包如上图所示,javamail-1[1].4.2.zip ; jaf-1_1_1.zip ;dom4j-1.6.1.jar ;jaxen-1.1.6.jar ,可以在网上自己下载
4.代码示例:主要参考网上的博文 ,地址:http://www.cnblogs.com/java-pan/archive/2012/03/06/javamail.html
4.1定义邮件发送所需要的字段:
1 package com.email.emailtest; 2 3 /** 4 * 定义发送邮件的所有字段的javabean 5 * 6 * @author Administrator 7 * 8 */ 9 public class MailInfo { 10 private String host;// 邮件服务器域名或IP 11 private String from;// 发件人 12 private String to;// 收件人 13 private String cc;// 抄送人 14 private String username;// 发件人用户名 15 private String password;// 发件人密码 16 private String title;// 邮件的主题 17 private String content;// 邮件的内容 18 19 public String getHost() { 20 return host; 21 } 22 23 public void setHost(String host) { 24 this.host = host; 25 } 26 27 public String getFrom() { 28 return from; 29 } 30 31 public void setFrom(String from) { 32 this.from = from; 33 } 34 35 public String getTo() { 36 return to; 37 } 38 39 public void setTo(String to) { 40 this.to = to; 41 } 42 43 public String getCc() { 44 return cc; 45 } 46 47 public void setCc(String cc) { 48 this.cc = cc; 49 } 50 51 public String getUsername() { 52 return username; 53 } 54 55 public void setUsername(String username) { 56 this.username = username; 57 } 58 59 public String getPassword() { 60 return password; 61 } 62 63 public void setPassword(String password) { 64 this.password = password; 65 } 66 67 public String getTitle() { 68 return title; 69 } 70 71 public void setTitle(String title) { 72 this.title = title; 73 } 74 75 public String getContent() { 76 return content; 77 } 78 79 public void setContent(String content) { 80 this.content = content; 81 } 82 }
4.2对密码进行加解密
1 package com.email.emailtest; 2 3 /** 4 * 对密码进行加密和解密 5 * 6 * @author Administrator 7 * 8 */ 9 public class Encrypt { 10 11 public static final int pass1 = 10; 12 public static final int pass2 = 1; 13 14 public Encrypt() { 15 } 16 17 /** 18 * @Title: DoEncrypt 19 * @Description: 对密码进行加密的方法 20 * @param @param str 21 * @param @return 设定文件 22 * @return String 返回类型 23 * @throws 24 */ 25 public static String DoEncrypt(String str) { 26 StringBuffer enStrBuff = new StringBuffer(); 27 for (int i = 0; i < str.length(); i++) { 28 int tmpch = str.charAt(i); 29 tmpch ^= 1; 30 tmpch ^= 0xa; 31 enStrBuff.append(Integer.toHexString(tmpch)); 32 } 33 34 return enStrBuff.toString().toUpperCase(); 35 } 36 37 /** 38 * @Title: DoDecrypt 39 * @Description: 对密码进行解密的方法 40 * @param @param str 41 * @param @return 设定文件 42 * @return String 返回类型 43 * @throws 44 */ 45 public static String DoDecrypt(String str) { 46 String deStr = str.toLowerCase(); 47 StringBuffer deStrBuff = new StringBuffer(); 48 for (int i = 0; i < deStr.length(); i += 2) { 49 String subStr = deStr.substring(i, i + 2); 50 int tmpch = Integer.parseInt(subStr, 16); 51 tmpch ^= 1; 52 tmpch ^= 0xa; 53 deStrBuff.append((char) tmpch); 54 } 55 56 return deStrBuff.toString(); 57 } 58 59 public static void main(String args[]) { 60 String source = "123456"; 61 String s = DoEncrypt(source); 62 System.out.println("de=" + s); 63 64 source = DoDecrypt(s); 65 System.out.println("en=" + source); 66 67 } 68 69 }
4.3邮件授权类
1 package com.email.emailtest; 2 3 import javax.mail.PasswordAuthentication; 4 5 /** 6 * 邮件授权类 7 * 8 * @author Administrator 9 * 10 */ 11 public class MyAuthenticator extends javax.mail.Authenticator { 12 13 private String strUser; 14 private String strPwd; 15 16 public MyAuthenticator(String user, String password) { 17 this.strUser = user; 18 this.strPwd = password; 19 } 20 21 protected PasswordAuthentication getPasswordAuthentication() { 22 return new PasswordAuthentication(strUser, strPwd); 23 } 24 }
4.4邮件发送
1 package com.email.emailtest; 2 3 import java.io.BufferedReader; 4 import java.io.File; 5 import java.io.FileInputStream; 6 import java.io.FileNotFoundException; 7 import java.io.IOException; 8 import java.io.InputStreamReader; 9 import java.io.UnsupportedEncodingException; 10 import java.security.Security; 11 import java.util.Properties; 12 import java.util.logging.Logger; 13 14 import javax.mail.Message; 15 import javax.mail.MessagingException; 16 import javax.mail.Session; 17 import javax.mail.Transport; 18 import javax.mail.internet.AddressException; 19 import javax.mail.internet.InternetAddress; 20 import javax.mail.internet.MimeMessage; 21 22 import org.dom4j.Document; 23 import org.dom4j.DocumentException; 24 import org.dom4j.DocumentHelper; 25 import org.dom4j.Element; 26 27 import com.email.utils.LogManager; 28 29 /** 30 * 发送邮件 31 * 32 * @author Administrator 33 * 34 */ 35 public class SendMail { 36 private static final Logger log = LogManager.getLogger(SendMail.class); 37 // 配置文件xml的路径 38 private final static String XML_PATH = "src" + File.separator 39 + "com\\email\\emailtest" + File.separator + "SendMail.xml"; 40 41 // private final static String XML_PATH = 42 // "src\\com\\email\\emailtest\\SendMail.xml"; 43 44 // xml文件中字段对应的javabean对象 45 private static MailInfo mailInfo = new MailInfo(); 46 47 /** 48 * <p> 49 * Title: readXML 50 * </p> 51 * <p> 52 * Description:读取xml文件,把文件内容以字符串形式输出 53 * </p> 54 * 55 * @param xmlPath 56 * 配置文件的路径 57 * @return 58 * @throws Exception 59 */ 60 private String readXML(String xmlPath) throws Exception { 61 log.info("----xmlPath----" + xmlPath); 62 63 String fileContent = ""; 64 File file = new File(xmlPath); 65 if (file.isFile() && file.exists()) { 66 try { 67 InputStreamReader read = new InputStreamReader( 68 new FileInputStream(file), "UTF-8"); 69 BufferedReader reader = new BufferedReader(read); 70 String line; 71 try { 72 while ((line = reader.readLine()) != null) { 73 fileContent += line; 74 } 75 reader.close(); 76 read.close(); 77 } catch (IOException e) { 78 e.printStackTrace(); 79 } 80 } catch (UnsupportedEncodingException e) { 81 e.printStackTrace(); 82 } catch (FileNotFoundException e) { 83 e.printStackTrace(); 84 } 85 } else { 86 throw new Exception("config目录下的配置文件SendMail.xml不存在,请检查"); 87 } 88 log.info("xml=" + fileContent); 89 return fileContent; 90 } 91 92 /** 93 * <p> 94 * Title: parseXML 95 * </p> 96 * <p> 97 * Description:发送邮件给指定的收件人和抄送人,同时进行一些简单的校验判断,如必填项的字段、type的值 98 * </p> 99 * 100 * @param xml 101 * 配置文件的内容 102 * @param type 103 * 邮件类型 104 */ 105 private void parseXML(String xml, String type) { 106 107 type = type.toLowerCase();// 忽略type字段的大小写 108 log.info("===========类型:"+type); 109 try { 110 // 解析XML,获得doc对象 111 Document doc = DocumentHelper.parseText(xml); 112 113 // 判断type的值是否正确,type的值应是报文二级节点中的一个 114 String flag = doc.getRootElement().element(type) + ""; 115 if (null == flag || flag.equals("null")) 116 throw new DocumentException( 117 "传入的type值不对,type的值应是SendMail.xml报文中二级节点的一个,请查看文件然后重新传入type的值"); 118 119 // 设置主机名称 120 Element hostEle = (Element) doc.selectSingleNode("/mail/" + type 121 + "/host"); 122 if (null == hostEle || "".equals(hostEle.getTextTrim())) { 123 throw new DocumentException( 124 "邮件服务器域名或IP不能为空,请检查配置文件SendMail.xml"); 125 } 126 mailInfo.setHost(hostEle == null ? "" : hostEle.getTextTrim()); 127 log.info("=======mailInfo.getHost():"+mailInfo.getHost()); 128 // 设置发件人 129 Element fromEle = (Element) doc.selectSingleNode("/mail/" + type 130 + "/from"); 131 if (null == fromEle || "".equals(fromEle.getTextTrim())) { 132 throw new DocumentException("发件人地址不能为空,请检查配置文件SendMail.xml"); 133 } 134 mailInfo.setFrom(fromEle == null ? "" : fromEle.getTextTrim()); 135 136 // 设置邮件主题 137 Element titleEle = (Element) doc.selectSingleNode("/mail/" + type 138 + "/title"); 139 mailInfo.setTitle(titleEle == null ? "" : titleEle.getTextTrim()); 140 141 // 设置收件人,对多个收件人的处理放在后面 142 Element toEle = (Element) doc.selectSingleNode("/mail/" + type 143 + "/to"); 144 if (null == toEle || "".equals(toEle.getTextTrim())) { 145 throw new DocumentException("收件人地址不能为空,请检查配置文件SendMail.xml"); 146 } 147 mailInfo.setTo(toEle == null ? "" : toEle.getTextTrim()); 148 149 // 设置抄送,对多个抄送人的处理放在后面 150 Element ccEle = (Element) doc.selectSingleNode("/mail/" + type 151 + "/cc"); 152 mailInfo.setCc(ccEle == null ? "" : ccEle.getTextTrim()); 153 154 // 设置发件人用户名 155 Element nameEle = (Element) doc.selectSingleNode("/mail/" + type 156 + "/username"); 157 if (null == nameEle || "".equals(nameEle.getTextTrim())) { 158 throw new DocumentException("发件人用户名不能为空,请检查配置文件SendMail.xml"); 159 } 160 mailInfo.setUsername(nameEle == null ? "" : nameEle.getTextTrim()); 161 162 // 设置发件人密码 163 Element passEle = (Element) doc.selectSingleNode("/mail/" + type 164 + "/password"); 165 if (null == passEle || "".equals(passEle.getTextTrim())) { 166 throw new DocumentException("发件人密码不能为空,请检查配置文件SendMail.xml"); 167 } 168 mailInfo.setPassword(passEle == null ? "" : passEle.getTextTrim()); 169 170 } catch (DocumentException e) { 171 e.printStackTrace(); 172 } 173 } 174 175 /** 176 * <p> 177 * Title: sendMailOfValidate 178 * </p> 179 * <p> 180 * Description:发送邮件的方法,Authenticator类验证 181 * </p> 182 */ 183 private void sendMailOfValidate() { 184 Properties props = System.getProperties(); 185 props.put("mail.smtp.host", mailInfo.getHost());// 设置邮件服务器的域名或IP 186 props.put("mail.smtp.auth", "true");// 授权邮件,mail.smtp.auth必须设置为true 187 188 //---------安全 189 Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider()); 190 props.put("mail.smtp.socketFactory.fallback", "false"); 191 props.put("mail.smtp.starttls.enable", "true"); 192 193 194 String password = mailInfo.getPassword();// 密码 195 try { 196 password = Encrypt.DoDecrypt(password);// 如果密码经过加密用此方法对密码进行解密 197 } catch (NumberFormatException e1) { 198 // 如果密码未经过加密,则对密码不做任何处理 199 } 200 // 传入发件人的用户名和密码,构造MyAuthenticator对象 201 MyAuthenticator myauth = new MyAuthenticator(mailInfo.getUsername(), 202 password); 203 204 // 传入props、myauth对象,构造邮件授权的session对象 205 Session session = Session.getDefaultInstance(props, myauth); 206 207 // 将Session对象作为MimeMessage构造方法的参数传入构造message对象 208 Message message = new MimeMessage(session); 209 try { 210 message.setFrom(new InternetAddress(mailInfo.getFrom()));// 发件人 211 212 // 对多个收件人的情况进行处理,配置文件SendMail.xml中每个收件人之间必须用逗号隔开的 213 if (mailInfo.getTo() != null && !"".equals(mailInfo.getTo())) { 214 String to[] = mailInfo.getTo().split(","); 215 // for (int i = 0; i < to.length; i++) { 216 // message.addRecipient(Message.RecipientType.TO, 217 // new InternetAddress(to[i]));// 收件人 218 // } 219 for(String too : to){ 220 message.addRecipient(Message.RecipientType.TO, new InternetAddress(too)); 221 222 } 223 224 } 225 226 // 对多个抄送人的情况进行处理,每个抄送人之间用逗号隔开的 227 if (mailInfo.getCc() != null && !"".equals(mailInfo.getCc())) { 228 String cc[] = mailInfo.getCc().split(","); 229 for (int j = 0; j < cc.length; j++) { 230 message.addRecipient(Message.RecipientType.CC, 231 new InternetAddress(cc[j]));// 抄送 232 } 233 } 234 message.setSubject(mailInfo.getTitle());// 主题 235 236 message.setText(mailInfo.getContent());// 内容 237 238 Transport.send(message);// 调用发送邮件的方法 239 240 log.info("邮件发送成功"); 241 } catch (AddressException e) { 242 e.printStackTrace(); 243 } catch (MessagingException e) { 244 e.printStackTrace(); 245 } 246 } 247 248 /** 249 * Title: sendMail Description:外部程序调用的入口 250 * 251 * @param type 邮件的类型 252 * ,目前有三种,即logmessage、checkmessage、ordermessage, 253 * type只能传这三个值中一个,传其他的任何值都不行 254 * @param content邮件的内容 255 * @throws Exception 256 */ 257 public void sendMail(String type, String content) throws Exception { 258 log.info("入参type=" + type); 259 log.info("入参content=" + content); 260 if (null == type || "".equals(type) || null == content 261 || "".equals(content)) { 262 throw new Exception("方法的入参type和content字段的值都不能为空或null"); 263 } 264 265 String xml = readXML(XML_PATH);// 获得xml字符串 266 267 parseXML(xml, type);// 解析xml字符串,把对应字段的值放入到mailInfo对象中 268 269 mailInfo.setContent(content);// 设置发送的内容 270 271 sendMailOfValidate();// 发送邮件 272 273 } 274 275 /** 276 * 为了方便直接用main方法测试 277 * 278 * @param args 279 * @throws Exception 280 */ 281 public static void main(String args[]) throws Exception { 282 283 SendMail mail = new SendMail(); 284 285 // type类型,根据此字段去配置文件SendMail.xml中匹配,然后发到相应的邮箱 286 String type = "logmessage"; 287 288 // 邮件的内容,实际开发中这个内容是前台传到后台 289 String content = "你好,欢迎使用JavaMail包开发邮件功能。fuck,终于成功了.."; 290 291 // 在其他类中调用时只能看到sendMail方法,为了保护内部细节,其它的方法都声明为私有的 292 mail.sendMail(type, content); 293 // 这个项目中没有日志文件,所以我打印一句话来告诉自己程序已经成功运行 294 log.info("----邮件发送成功----"); 295 296 } 297 298 }
4.5日志记录类
1 package com.email.utils; 2 3 import java.util.logging.Logger; 4 5 public class LogManager { 6 /** 7 * 获取日志对象 8 * 9 * @param clazz 10 * 11 */ 12 public static Logger getLogger(Class clazz) { 13 Logger logger = Logger.getLogger(clazz.getName()); 14 return logger; 15 } 16 }
4.6xml文件,可以配置邮件发送的一些信息,代码中会解析这个xml文件。但是我自己的考虑是,如果是一个正常的邮件系统,可以用户在客户端进行填写,然后读取到代码中,包括收件人,抄送人等等信息。自己理解,欢迎指正!
<?xml version="1.0" encoding="UTF-8"?> <mail> <logmessage><!--报文的二级节点,为了使传入的type值对大小写不限制,故此处必须用小写 --> <host>smtp.qq.com</host><!-- 邮件服务器域名或IP,必填项 smtp.exmail.qq.com smtp.qq.com smtp.163.com --> <from>12345@qq.com</from><!-- 发件人 ,必填项 --> <to>123@qq.com,456@qq.com,789@qq.com,1245@163.com</to><!-- 收件人,多个收件人之间用英文状态下的逗号隔开,必填项 --> <cc>@qq.com</cc><!-- 抄送,多个抄送人之间用英文状态下的逗号隔开--> <title>Test JavaMail 001</title><!-- 邮件主题--> <username>12345@qq.com</username><!-- 发件人邮箱的用户名,即email的全地址,必填项 --> <password>mima</password><!-- 发件人邮箱的密码,像QQ邮箱如果设置了独立密码应输入独立密码,必填项,为了安全起见填写密码之前最好用Encrypt类的DoEncrypt方法加密 --> </logmessage>
<checkmessage> <host></host> <from></from> <to></to> <cc></cc> <title></title> <username></username> <password></password> </checkmessage> <ordermessage> <host></host> <from></from> <to></to> <cc></cc> <title></title> <username></username> <password></password> </ordermessage> </mail>
5.遇到的问题及解决方案:
先以163邮箱为例讲解:
为了方便起见,代码的测试时直接使用main()进行的,如果你按照上面的代码和xml配置,都准备完成后,直接执行main()方法,可能会有以下错误:
5.1: 550 User has no permission
这时你需要将你的163邮箱开启pop3\smtp\imap等服务,如图在163邮箱的设置中,在POP3/..处设置开启服务,设置完成后,163会强制你设置 ‘客户端授权密码’,这也是为了安全考虑。然后在程序的xml文件配置时候,这个邮箱的密码就是你设置的 ‘客户端授权密码’ 。
5.2 : javax.mail.AuthenticationFailedException: 535 Error: authentication failed 这里你需要检查你的账户密码是否正确,尤其是你设置的‘客户端授权码’,这个要代替你原先的邮箱密码。
5.3: :javax.mail.AuthenticationFailedException: 530 Error: A secure connection is requiered(such as ssl). More information at http://service.mail.qq.com/cgi-bin/help?id=28
这个问题是在qq邮箱中测试出来的。我在网上找了不少的资料看,包括设置邮箱的host,都是不可以的。最后在代码中增加了安全的认证,就ok了。代码中已添加,可以测试。
同样,使用qq邮箱时候,也需要在设置pop3\smtp\imap等服务,这个是在设置-->账户-->然后你就能刚看到了,同样开启哪一个服务,tx也会提供你一个客户端授权码,这个不同于网易的需要你自己设置,你需要记住系统提供你的,在使用客户端收发邮件时候,以其代替密码使用,就ok了。
5.4其他人总结的问题:
1 java.lang.NoClassDefFoundError: com/sun/mail/util/LineInputStream 2 at javax.mail.Session.loadProvidersFromStream(Session.java:928) 3 at javax.mail.Session.access$000(Session.java:174) 4 at javax.mail.Session$1.load(Session.java:870) 5 at javax.mail.Session.loadResource(Session.java:1084) 6 at javax.mail.Session.loadProviders(Session.java:889) 7 at javax.mail.Session.<init>(Session.java:210) 8 at javax.mail.Session.getDefaultInstance(Session.java:299) 9 at mail.SendMail.sendMail(SendMail.java:31) 10 at mail.SendMail.main(SendMail.java:50) 11 Exception in thread "main" 12 解决方案:在myeclipse的安装目录下搜索javaee.jar,然后用压缩软件打开,删除javax下的mail文件夹 13 14 java.lang.NoClassDefFoundError: com/sun/activation/registries/LogSupport 15 at javax.activation.MailcapCommandMap.<init>(MailcapCommandMap.java:140) 16 at javax.activation.CommandMap.getDefaultCommandMap(CommandMap.java:61) 17 at javax.activation.DataHandler.getCommandMap(DataHandler.java:153) 18 at javax.activation.DataHandler.getDataContentHandler(DataHandler.java:611) 19 at javax.activation.DataHandler.writeTo(DataHandler.java:315) 20 at javax.mail.internet.MimeUtility.getEncoding(MimeUtility.java:264) 21 at javax.mail.internet.MimeBodyPart.updateHeaders(MimeBodyPart.java:1299) 22 at javax.mail.internet.MimeMessage.updateHeaders(MimeMessage.java:2071) 23 at javax.mail.internet.MimeMessage.saveChanges(MimeMessage.java:2039) 24 at javax.mail.Transport.send(Transport.java:119) 25 at mail.SendMail.sendMailNoValidate(SendMail.java:48) 26 at mail.SendMail.main(SendMail.java:96) 27 Exception in thread "main" 28 解决方案:在myeclipse的安装目录下搜索javaee.jar,然后用压缩软件打开,删除javax文件夹下的activation文件夹 29 30 31 javax.mail.SendFailedException: Sending failed; 32 nested exception is: 33 javax.mail.MessagingException: 503 Error: need EHLO and AUTH first ! 34 35 at javax.mail.Transport.send0(Transport.java:219) 36 at javax.mail.Transport.send(Transport.java:81) 37 at com.asiainfo.bboss.sendmail.SendMailServiceImpl.sendMailNoValidate(SendMailServiceImpl.java:210) 38 at com.asiainfo.bboss.sendmail.SendMailServiceImpl.sendMail(SendMailServiceImpl.java:243) 39 at com.asiainfo.bboss.sendmail.SendMailServiceImpl.main(SendMailServiceImpl.java:261) 40 解决方案:props.put("mail.smtp.auth", "true"); 41 42 javax.mail.SendFailedException: Sending failed; 43 nested exception is: 44 javax.mail.AuthenticationFailedException 45 at javax.mail.Transport.send0(Transport.java:219) 46 at javax.mail.Transport.send(Transport.java:81) 47 at com.asiainfo.bboss.sendmail.SendMailServiceImpl.sendMailNoValidate(SendMailServiceImpl.java:211) 48 at com.asiainfo.bboss.sendmail.SendMailServiceImpl.sendMail(SendMailServiceImpl.java:244) 49 at com.asiainfo.bboss.sendmail.SendMailServiceImpl.main(SendMailServiceImpl.java:262) 50 解决方案: MyAuthenticator myauth = new MyAuthenticator(mailInfo.getUsername(),password); 51
6.其他
demo能跑起来,现在考虑:1.整合成一个简单的邮箱客户端,能够发送邮件;
2.增加邮件功能的其他功能项,包括附件,收发功能,增加数据库,登陆页面等等;
3.参考博文地址: http://www.cnblogs.com/java-pan/archive/2012/03/06/javamail.html
http://www.jspcn.net/htmlnews/1150019680500144.html
4.欢迎加群:413066190 ,交流。
5.路漫漫其修远兮,吾将上下而求索!