为什么80%的码农都做不了架构师?>>>   

为了不想到处去下载jar包,我使用maven为我管理,在开始编码这些东西之前,我们先在pom.xml文件中<dependencies>标签内加入以下内容:

<!-- Following jars are involved by MailSender.java -->
<dependency><groupId>com.sun.mail</groupId><artifactId>javax.mail</artifactId><version>1.5.2</version>
</dependency>
<dependency><groupId>javax.activation</groupId><artifactId>activation</artifactId><version>1.1.1</version>
</dependency>

为了方便,我抽象了一个MailMessage对象,此对象代表了邮件对象,内封装了收信人、发信人、邮件内容、抄送人、密件抄送等等诸多代表邮件的属性,如下:

package com.abc.common.mail;/*** Represents a Mail message object which contains all the massages needed* by an email.*/
class MailMessage {private String subject;private String from;private String[] tos;private String[] ccs;private String[] bccs;private String content;private String[] fileNames;/*** No parameter constructor.*/public MailMessage(){}/*** Construct a MailMessage object.*/public MailMessage(String subject, String from, String[] tos, String[] ccs, String[] bccs, String content, String[] fileNames) {this.subject = subject;this.from = from;this.tos = tos;this.ccs = ccs;this.bccs = bccs;this.content = content;this.fileNames = fileNames;}/*** Construct a simple MailMessage object.*/public MailMessage(String subject, String from, String to, String content) {this.subject = subject;this.from = from;this.tos = new String[]{to};this.content = content;}public String getSubject() {return subject;}public void setSubject(String subject) {this.subject = subject;}public String getFrom() {return from;}public void setFrom(String from) {this.from = from;}public String[] getTos() {return tos;}public void setTos(String[] tos) {this.tos = tos;}public String[] getCcs() {return ccs;}public void setCcs(String[] ccs) {this.ccs = ccs;}public String[] getBccs() {return bccs;}public void setBccs(String[] bccs) {this.bccs = bccs;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public String[] getFileNames() {return fileNames;}public void setFileNames(String[] fileNames) {this.fileNames = fileNames;}
}

另外,我们还需要一个对象来描述发送者的授权问题。即,发送者在发送邮件直线需要获取SMTP服务器的授权,只有经过授权的账户才能发送邮件,这个对象如下:

package com.abc.common.mail;import javax.mail.Authenticator;
import javax.mail.PasswordAuthentication;public class MailAuthenticator extends Authenticator {/*** Represents the username of sending SMTP server.* <p>For example: If you use smtp.163.com as your smtp server, then the related* username should be: <br>'<b>testname@163.com</b>', or just '<b>testname</b>' is OK.*/private String username = null;/*** Represents the password of sending SMTP sever.* More explicitly, the password is the password of username.*/private String password = null;public MailAuthenticator(String user, String pass) {username = user;password = pass;}protected PasswordAuthentication getPasswordAuthentication() {return new PasswordAuthentication(username, password);}
}

最后,是最重要的主类了。调用此类的sendEmail(MailMessage mail)方法可以发送邮件,这封邮件中可以包含一个或多个附件。

但是在发送附件之前,我们需要了解附件名和内容乱码的问题:MIME要解决的一个问题就是将SMTP协议不支持的字节流转换成为SMTP 协议支持的字节流。比如我们要通过邮件传输一个附件文档,该附件文档就是一个8bit 字节流,如果简单的直接通过SMTP 发送,其最高位信息将被丢失。MIME规定可以用两种编码方式将8bit 的字节流编码成为低于8bit 的字节流,它们分别是BASE64 编码(BASE64 将8bit 字节流编码成6bit 字节流)和QP 编码。这两种编码方式同样应用在对中文的编码上。例如如果邮件中文题目叫做“CVS 介绍”,那么其编码后的形式可能为:

Subject: =?gb2312?B?Q1ZTLS3QpMX0LnBwdA==?=

其中,标题字符串以”=?”开始,以”?=”结束。”gb2312”表示字符串的字符集,而以”?”分隔的”B”就表示此字符串的编码方式为BASE64。那么,此编码从何而来的呢?查阅相关资料后,发现MimeUtility.encodeWord()和MimeUtility.encodeText()等方法就是用来编码中文等特殊字符的:

//solve encoding problem of attachments file name.
try {fileName = MimeUtility.encodeText(fileName);
} catch (UnsupportedEncodingException e) {LOGGER.error("Cannot convert the encoding of attachments file name.", e);
}

同样的, 我们处理此标题时就要先将BASE64编码的6bit 字节流转换为原来的8bit 字节流,再根据字符集”gb2312”转换为Java 中的String 类型。这里可以简单的使用JavaMail 提供的MimeUtility.decodeWord()或者MimeUtility.decodeText()静态方法将编码后的字符串解码。当然,不是每个情况都会出现乱码的,所以,不要对所有的乱码都执行这个操作,因此,我们需要判断其内容是不是符合某些规则,满足这些规则的字符串,我们可以视其为乱码,并执行相应的解码操作。于是,我封装了一个方法,方法内部进行了内容的判断,如果满足规则,则进行解码,否则不进行:

/*** For receiving an email, the sender, receiver, reply-to and subject may * be messy code. The default encoding of HTTP is ISO8859-1, In this situation, * use MimeUtility.decodeTex() to convert these information to GBK encoding.* @param res The String to be decoded.* @return A decoded String.*/
private static String mimeDecodeString(String res) {if(res != null) {String s = res.trim();try {if (s.startsWith("=?GB") || s.startsWith("=?gb")|| from.startsWith("=?UTF") || s.startsWith("=?utf")) {s = MimeUtility.decodeText(from);}} catch (Exception e) {LOGGER.error("Decode string error. Origin string is: " + res, e);}return from;}return null;
}

另外,这个类中还有一个发送匿名邮件的API叫sendAnonymousEmail(MailMessage mail)(仅供交流学习研究使用,不要拿去做坏事哦)。注意,此处的匿名,并不是不写发送者的邮箱,这里的匿名是指我们可以输入任何有效的邮箱地址,这个地址不一定存在,只需要满足邮箱格式的地址即可。比如noreply@sina.cc,又比如111111@111.com,通过这类地址实现隐藏发送者地址的目的。事实上,我们也无需输入真实的发送者地址,因为这封邮件将跳过发送者的SMTP服务器而直接发送到接收者的服务器上。要想明白这个道理,我们得先说说MX。

MX(Mail Exchanger)记录是邮件交换记录,它指向一个邮件服务器,用于电子邮件系统发邮件时根据收信人的地址后缀来定位邮件服务器。例如,当Internet上的某用户要发一封信给 user@mydomain.com 时,该用户的邮件系统通过本机DNS查找mydomain.com这个域名的MX记录,如果MX记录存在,用户计算机就将邮件发送到MX记录所指定的邮件服务器上。

简单的说,MX记录就是用于为发送的邮件指路的记录,它直接指向收件人邮箱所在的域的邮件接收服务器。有了这个邮件接收服务器地址,我们的机器就可以直接向该服务器传送邮件了。

话不多说,看看这个发送匿名邮件的API吧:

/*** Send anonymous email. Note that although we could give any address as from address,* (for example: <b>'a@a.a' is valid</b>), the from of MailMessage should always be the * correct format of email address(for example the <b>'aaaa' is invalid</b>). Otherwise * an exception would be thrown say that username is invalid.* @param mail The MailMessage object which contains at least all the required *        attributes to be sent.*/
public static void sendAnonymousEmail(MailMessage mail) {String dns = "dns://";Hashtable<String, String> env = new Hashtable<String, String>();env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");env.put(Context.PROVIDER_URL, dns);String[] tos = mail.getTos();try {DirContext ctx = new InitialDirContext(env);for(String to:tos) {String domain = to.substring(to.indexOf('@') + 1);//Get MX(Mail eXchange) records from DNSAttributes attrs = ctx.getAttributes(domain, new String[] { "MX" });if (attrs == null || attrs.size() <= 0) {throw new java.lang.IllegalStateException("Error: Your DNS server has no Mail eXchange records!");}@SuppressWarnings("rawtypes")NamingEnumeration servers = attrs.getAll();String smtpHost = null;boolean isSend = false;StringBuffer buf = new StringBuffer();//try all the mail exchange server to send the email.while (servers.hasMore()) {Attribute hosts = (Attribute) servers.next();for (int i = 0; i < hosts.size(); ++i) {//sample: 20 mx2.qq.comsmtpHost = (String) hosts.get(i);//parse the string to get smtpHost. sample: mx2.qq.comsmtpHost = smtpHost.substring(smtpHost.lastIndexOf(' ') + 1);try {sendEmail(smtpHost, mail, true);isSend = true;return;} catch (Exception e) {LOGGER.error("", e);buf.append(e.toString()).append("\r\n");continue;}}}if (!isSend) {throw new java.lang.IllegalStateException("Error: Send email error."+ buf.toString());}}} catch (NamingException e) {LOGGER.error("", e);}
}

这个API中,先从邮件中封装的收件人地址里取出收件人所在发服务器地址,然后通过该地址查找本地DNS记录,如果未找到,则抛出IllegalStateException,因为没法知道收件人的邮件服务器地址就没法发送匿名邮件了。如果找到,则尝试依次向每个邮件服务器发送该邮件,如果发送成功,则立即返回,不再尝试下一个邮件服务器地址。如果发送失败,则会抛出异常,提醒失败。注意到这个API中间的

sendEmail(smtpHost, mail, true);

此方法是我封装的用于发送邮件的基础方法。话不多说,先上代码:

/*** Send Email. Use string array to represents attachments file names.* @see #sendEmail(String, String, String[], String[], String[], String, File[])*/
private static void sendEmail(String smtpHost, MailMessage mail, boolean isAnonymousEmail) {if(mail == null) {throw new IllegalArgumentException("Param mail can not be null.");}String[] fileNames = mail.getFileNames();//only needs to check the param: fileNames, other params would be checked through//the override method.File[] files = null;if(fileNames != null && fileNames.length > 0) {files = new File[fileNames.length];for(int i = 0; i < files.length; i++) {File file = new File(fileNames[i]);files[i] = file;}}sendEmail(smtpHost, mail.getSubject(), mail.getFrom(), mail.getTos(), mail.getCcs(), mail.getBccs(), mail.getContent(), files, isAnonymousEmail);
}

为了重用有些代码,我特意提取了一部分公共的部分,因此此方法是一个重载方法,也是最核心的方法了。需要注意的是,发送匿名邮件时,需要将mail.smtp.auth属性设置为false,并且在获取邮件session时,不需要提供邮件验证器Authenticator:

if(isAnonymousEmail) {//only anonymous email needs param smtpHostprops.put("mail.smtp.host", smtpHost);props.put("mail.smtp.auth", "false");session = Session.getInstance(props, null);
}

下面再看看这个被调用的sendEmail方法吧:

/*** Send Email. Note that content and attachments cannot be empty at the same time.* @param smtpHost The SMTPHost. This param is needed when sending an anonymous email.*        When sending normal email, the param is ignored and the default SMTPServer*        configured is used.* @param subject The email subject.* @param from The sender address. This address must be available in SMTPServer.* @param tos The receiver addresses. At least 1 address is valid.* @param ccs The 'copy' receiver. Can be empty.* @param bccs The 'encrypt copy' receiver. Can be empty.* @param content The email content.* @param attachments The file array represent attachments to be send.* @param isAnonymousEmail If this mail is send in anonymous mode. When set to true, the *        param smtpHost is needed and sender's email address from should be in correct*        pattern.*/
private static void sendEmail(String smtpHost, String subject, String from, String[] tos, String[] ccs, String[] bccs, String content, File[] attachments, boolean isAnonymousEmail) {//parameter checkif(isAnonymousEmail && smtpHost == null) {throw new IllegalStateException("When sending anonymous email, param smtpHost cannot be null");}if(subject == null || subject.length() == 0) {subject = "Auto-generated subject";}if(from == null) {throw new IllegalArgumentException("Sender's address is required.");}if(tos == null || tos.length == 0) {throw new IllegalArgumentException("At lease 1 receive address is required.");}if(content == null && (attachments == null || attachments.length == 0)) {throw new IllegalArgumentException("Content and attachments cannot be empty at the same time");}if(attachments != null && attachments.length > 0) {List<File> invalidAttachments = new ArrayList<>();for(File attachment:attachments) {if(!attachment.exists() || attachment.isDirectory() || !attachment.canRead()) {invalidAttachments.add(attachment);}}if(invalidAttachments.size() > 0) {String msg = "";for(File attachment:invalidAttachments) {msg += "\n\t" + attachment.getAbsolutePath();}throw new IllegalArgumentException("The following attachments are invalid:" + msg);}}Session session;Properties props = new Properties();props.put("mail.transport.protocol", "smtp");if(isAnonymousEmail) {//only anonymous email needs param smtpHostprops.put("mail.smtp.host", smtpHost);props.put("mail.smtp.auth", "false");session = Session.getInstance(props, null);} else {//normal email does not need param smtpHost and //uses the default host SMTPServerprops.put("mail.smtp.host", SMTPServer); props.put("mail.smtp.auth", "true");session = Session.getInstance(props, new MailAuthenticator(SMTPUsername, SMTPPassword));}//create messageMimeMessage msg = new MimeMessage(session);try {//Multipart is used to store many BodyPart objects.Multipart multipart=new MimeMultipart();BodyPart part = new MimeBodyPart();part.setContent(content,"text/html;charset=gb2312");//add email content part.multipart.addBodyPart(part);//add attachment parts.if(attachments != null && attachments.length > 0) {for(File attachment: attachments) {String fileName = attachment.getName();DataSource dataSource = new FileDataSource(attachment);DataHandler dataHandler = new DataHandler(dataSource);part = new MimeBodyPart();part.setDataHandler(dataHandler);//solve encoding problem of attachments file name.try {fileName = MimeUtility.encodeText(fileName);} catch (UnsupportedEncodingException e) {LOGGER.error("Cannot convert the encoding of attachments file name.", e);}//set attachments the original file name. if not set, //an auto-generated name would be used.part.setFileName(fileName);multipart.addBodyPart(part);}}msg.setSubject(subject);msg.setSentDate(new Date());//set sendermsg.setFrom(new InternetAddress(from));//set receiver, for(String to: tos) {msg.addRecipient(RecipientType.TO, new InternetAddress(to));}if(ccs != null && ccs.length > 0) {for(String cc: ccs) {msg.addRecipient(RecipientType.CC, new InternetAddress(cc));}}if(bccs != null && bccs.length > 0) {for(String bcc: bccs) {msg.addRecipient(RecipientType.BCC, new InternetAddress(bcc));}}msg.setContent(multipart);//save the changes of email first.msg.saveChanges();//to see what commands are used when sending a email, //use session.setDebug(true)//session.setDebug(true);//send emailTransport.send(msg); LOGGER.info("Send email success.");System.out.println("Send html email success.");} catch (NoSuchProviderException e) {LOGGER.error("Email provider config error.", e);} catch (MessagingException e) {LOGGER.error("Send email error.", e);}
}

有了《JavaMail发送和接收邮件API(详解)》一文的基础和前文的叙述,我想里面的逻辑应该不用多解释了吧。下面主要讲讲里面的几个变量。

正如你所见,里面的几个变量SMTPServer、SMTPUsername和SMTPPassword是需要配置的。如果是发送匿名邮件,那么SMTPUsername和SMTPPassword两个变量可以不用配置。这里,我是将这些内容搬到了项目的一个配置文件里,并在初始化这个对象时就去读取指定的配置文件获得这些值:

private static String SMTPServer;
private static String SMTPUsername;
private static String SMTPPassword;
static {loadConfigProperties();
}
/*** Load configuration properties to initialize attributes.*/
private static void loadConfigProperties() {//get current pathFile f = new File("");String absolutePath = f.getAbsolutePath();String propertiesPath = "";String OSName = System.getProperty("os.name");if(OSName.contains("Windows")) {propertiesPath = absolutePath + "\\..\\src\\main\\resources\\project.properties";} else if(OSName.contains("unix")) {propertiesPath = absolutePath + "/../src/main/resources/project.properties";}f = new File(propertiesPath);if(!f.exists()) {throw new RuntimeException("Porperties file not found at: " + f.getAbsolutePath());}Properties props = new Properties();try {props.load(new FileInputStream(f));SMTPServer = props.getProperty("AbcCommon.mail.SMTPServer");SMTPUsername = props.getProperty("AbcCommon.mail.SMTPUsername");SMTPPassword = props.getProperty("AbcCommon.mail.SMTPPassword");POP3Server = props.getProperty("AbcCommon.mail.POP3Server");POP3Username = props.getProperty("AbcCommon.mail.POP3Username");POP3Password = props.getProperty("AbcCommon.mail.POP3Password");} catch (FileNotFoundException e) {LOGGER.error("File not found at " + f.getAbsolutePath(), e);} catch (IOException e) {LOGGER.error("Error reading config file " + f.getName(), e);}
}

说了这么多发送邮件,下面再说说接收邮件。

首先,接收邮件,是肯定需要用户名和密码的,因此此方法至少含有两个参数。由于各大邮件服务公司的邮件服务器命名不统一,因此还需要一个参数来指定接收邮件的服务器,于是,此方法将含有三个参数。接收邮件的思路是:用username和password创建一个邮件验证器Authenticator,通过这个Authenticator来获得一个邮件Session。拿到Session后,通过

Store store = session.getStore("pop3");
Folder inbox = store.getFolder("INBOX");

一句,可以获得该账户的收件箱。《JavaMail发送和接收邮件API(详解)》一文中有提到,Store即是用来接收邮件的对象。然后可以通过

Message[] messages = inbox.getMessages();

来获得收件箱中的所有信息。接下来就可以迭代这个数组获取需要的内容了。整个方法如下:

/*** Receive Email from POPServer. Use POP3 protocal by default. Thus,* call this method, you need to provide a pop3 mail server address.* @param emailAddress The email account in the POPServer.* @param password The password of email address.*/
public static void receiveEmail(String host, String username, String password) {//param check. If param is null, use the default configured value.if(host == null) {host = POP3Server;}if(username == null) {username = POP3Username;}if(password == null) {password = POP3Password;}Properties props = System.getProperties();//MailAuthenticator authenticator = new MailAuthenticator(username, password);try {Session session = Session.getDefaultInstance(props, null);// Store store = session.getStore("imap");Store store = session.getStore("pop3");// Connect POPServerstore.connect(host, username, password);Folder inbox = store.getFolder("INBOX");if (inbox == null) {throw new RuntimeException("No inbox existed.");}// Open the INBOX with READ_ONLY mode and start to read all emails.inbox.open(Folder.READ_ONLY);System.out.println("TOTAL EMAIL:" + inbox.getMessageCount());Message[] messages = inbox.getMessages();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");for (int i = 0; i < messages.length; i++) {Message msg = messages[i];String from = InternetAddress.toString(msg.getFrom());String replyTo = InternetAddress.toString(msg.getReplyTo());String to = InternetAddress.toString(msg.getRecipients(Message.RecipientType.TO));String subject = msg.getSubject();Date sent = msg.getSentDate();Date ress = msg.getReceivedDate();String type = msg.getContentType();System.out.println((i + 1) + ".---------------------------------------------");System.out.println("From:" + mimeDecodeString(from));System.out.println("Reply To:" + mimeDecodeString(replyTo));System.out.println("To:" + mimeDecodeString(to));System.out.println("Subject:" + mimeDecodeString(subject));System.out.println("Content-type:" + type);if (sent != null) {System.out.println("Sent Date:" + sdf.format(sent));}if (ress != null) {System.out.println("Receive Date:" + sdf.format(ress));}
//                //Get message headers.
//                @SuppressWarnings("rawtypes")
//                Enumeration headers = msg.getAllHeaders();
//                while (headers.hasMoreElements()) {
//                    Header h = (Header) headers.nextElement();
//                    String name = h.getName();
//                    String val = h.getValue();
//                    System.out.println(name + ": " + val);
//                }//                //get the email content.
//                Object content = msg.getContent();
//                System.out.println(content);
//                //print content
//                Reader reader = new InputStreamReader(
//                        messages[i].getInputStream());
//                int a = 0;
//                while ((a = reader.read()) != -1) {
//                    System.out.print((char) a);
//                }}// close connection. param false represents do not delete messaegs on server.inbox.close(false);store.close();
//        } catch(IOException e) {
//            LOGGER.error("IOException caught while printing the email content", e);} catch (MessagingException e) {LOGGER.error("MessagingException caught when use message object", e);}
}

注意到我们在处理邮件中可能出现乱码的内容时,调用了前文提到的自定义的mimeDecodeString()方法:

System.out.println("Subject:" + mimeDecodeString(subject));

还有,这里面的几个变量:POP3Server、POP3Username和POP3Password也是需要配置的,并会在初始化这个工具类的时候读取。

话不多说,让我们先发送一封试试。为了方便,随便找几个文件放入C:\\根目录下:

直接在MailUtil类中加入main方法:

Ctrl+F11执行这个程序,首先可以在控制台看见以下内容(注意执行的程序和发送邮件的时间):

然后再进入邮箱,看到收到的邮件:

打开邮件后,会看到以下内容(注意标题,发件人,收件人和邮件内容,发送时间):

在看看附件的内容(注意附件名字):

匿名邮件的发送也类似,我已经测试过了,这里不再贴出。好了,最后再贴出工具类的完整代码:

package com.abc.common.mail;import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.util.Properties;import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.BodyPart;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.Message.RecipientType;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.NoSuchProviderException;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;import org.apache.log4j.Logger;public class MailUtil {private static final Logger LOGGER = Logger.getLogger(MailUtil.class);private static String SMTPServer;private static String SMTPUsername;private static String SMTPPassword;private static String POP3Server;private static String POP3Username;private static String POP3Password;static {loadConfigProperties();}public static void main(String[] args) {//发送邮件MailMessage mail = new MailMessage("test-subject", "xxxx@163.com", "yyyy@126.com", "This is mail content");//set attachmentsString[] attachments = new String[]{"C:\\AndroidManifest.xml", "C:\\ic_launcher-web.png", "C:\\光良 - 童话.mp3", "C:\\文档测试.doc", "C:\\中文文件名测试.txt"};mail.setFileNames(attachments);sendEmail(mail);//接收邮件receiveEmail(POP3Server, POP3Username, POP3Password);//发送匿名邮件MailMessage anonymousMail = new MailMessage("subject", "a@a.a", "zzzz@qq.com", "content");anonymousMail.setFileNames(attachments);sendAnonymousEmail(anonymousMail);}/*** Load configuration properties to initialize attributes.*/private static void loadConfigProperties() {File f = new File("");//this path would point to AbcCommonString absolutePath = f.getAbsolutePath();String propertiesPath = "";String OSName = System.getProperty("os.name");if(OSName.contains("Windows")) {propertiesPath = absolutePath + "\\..\\src\\main\\resources\\project.properties";} else if(OSName.contains("unix")) {propertiesPath = absolutePath + "/../src/main/resources/project.properties";}f = new File(propertiesPath);if(!f.exists()) {throw new RuntimeException("Porperties file not found at: " + f.getAbsolutePath());}Properties props = new Properties();try {props.load(new FileInputStream(f));SMTPServer = props.getProperty("AbcCommon.mail.SMTPServer");SMTPUsername = props.getProperty("AbcCommon.mail.SMTPUsername");SMTPPassword = props.getProperty("AbcCommon.mail.SMTPPassword");POP3Server = props.getProperty("AbcCommon.mail.POP3Server");POP3Username = props.getProperty("AbcCommon.mail.POP3Username");POP3Password = props.getProperty("AbcCommon.mail.POP3Password");} catch (FileNotFoundException e) {LOGGER.error("File not found at " + f.getAbsolutePath(), e);} catch (IOException e) {LOGGER.error("Error reading config file " + f.getName(), e);}}/*** Send email. Note that the fileNames of MailMessage are the absolute path of file.* @param mail The MailMessage object which contains at least all the required *        attributes to be sent.*/public static void sendEmail(MailMessage mail) {sendEmail(null, mail, false);}/*** Send anonymous email. Note that although we could give any address as from address,* (for example: <b>'a@a.a' is valid</b>), the from of MailMessage should always be the * correct format of email address(for example the <b>'aaaa' is invalid</b>). Otherwise * an exception would be thrown say that username is invalid.* @param mail The MailMessage object which contains at least all the required *        attributes to be sent.*/public static void sendAnonymousEmail(MailMessage mail) {String dns = "dns://";Hashtable<String, String> env = new Hashtable<String, String>();env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");env.put(Context.PROVIDER_URL, dns);String[] tos = mail.getTos();try {DirContext ctx = new InitialDirContext(env);for(String to:tos) {String domain = to.substring(to.indexOf('@') + 1);//Get MX(Mail eXchange) records from DNSAttributes attrs = ctx.getAttributes(domain, new String[] { "MX" });if (attrs == null || attrs.size() <= 0) {throw new java.lang.IllegalStateException("Error: Your DNS server has no Mail eXchange records!");}@SuppressWarnings("rawtypes")NamingEnumeration servers = attrs.getAll();String smtpHost = null;boolean isSend = false;StringBuffer buf = new StringBuffer();//try all the mail exchange server to send the email.while (servers.hasMore()) {Attribute hosts = (Attribute) servers.next();for (int i = 0; i < hosts.size(); ++i) {//sample: 20 mx2.qq.comsmtpHost = (String) hosts.get(i);//parse the string to get smtpHost. sample: mx2.qq.comsmtpHost = smtpHost.substring(smtpHost.lastIndexOf(' ') + 1);try {sendEmail(smtpHost, mail, true);isSend = true;return;} catch (Exception e) {LOGGER.error("", e);buf.append(e.toString()).append("\r\n");continue;}}}if (!isSend) {throw new java.lang.IllegalStateException("Error: Send email error."+ buf.toString());}}} catch (NamingException e) {LOGGER.error("", e);}} /*** Send Email. Use string array to represents attachments file names.* @see #sendEmail(String, String, String[], String[], String[], String, File[])*/private static void sendEmail(String smtpHost, MailMessage mail, boolean isAnonymousEmail) {if(mail == null) {throw new IllegalArgumentException("Param mail can not be null.");}String[] fileNames = mail.getFileNames();//only needs to check the param: fileNames, other params would be checked through//the override method.File[] files = null;if(fileNames != null && fileNames.length > 0) {files = new File[fileNames.length];for(int i = 0; i < files.length; i++) {File file = new File(fileNames[i]);files[i] = file;}}sendEmail(smtpHost, mail.getSubject(), mail.getFrom(), mail.getTos(), mail.getCcs(), mail.getBccs(), mail.getContent(), files, isAnonymousEmail);}/*** Send Email. Note that content and attachments cannot be empty at the same time.* @param smtpHost The SMTPHost. This param is needed when sending an anonymous email.*        When sending normal email, the param is ignored and the default SMTPServer*        configured is used.* @param subject The email subject.* @param from The sender address. This address must be available in SMTPServer.* @param tos The receiver addresses. At least 1 address is valid.* @param ccs The 'copy' receiver. Can be empty.* @param bccs The 'encrypt copy' receiver. Can be empty.* @param content The email content.* @param attachments The file array represent attachments to be send.* @param isAnonymousEmail If this mail is send in anonymous mode. When set to true, the *        param smtpHost is needed and sender's email address from should be in correct*        pattern.*/private static void sendEmail(String smtpHost, String subject, String from, String[] tos, String[] ccs, String[] bccs, String content, File[] attachments, boolean isAnonymousEmail) {//parameter checkif(isAnonymousEmail && smtpHost == null) {throw new IllegalStateException("When sending anonymous email, param smtpHost cannot be null");}if(subject == null || subject.length() == 0) {subject = "Auto-generated subject";}if(from == null) {throw new IllegalArgumentException("Sender's address is required.");}if(tos == null || tos.length == 0) {throw new IllegalArgumentException("At lease 1 receive address is required.");}if(content == null && (attachments == null || attachments.length == 0)) {throw new IllegalArgumentException("Content and attachments cannot be empty at the same time");}if(attachments != null && attachments.length > 0) {List<File> invalidAttachments = new ArrayList<>();for(File attachment:attachments) {if(!attachment.exists() || attachment.isDirectory() || !attachment.canRead()) {invalidAttachments.add(attachment);}}if(invalidAttachments.size() > 0) {String msg = "";for(File attachment:invalidAttachments) {msg += "\n\t" + attachment.getAbsolutePath();}throw new IllegalArgumentException("The following attachments are invalid:" + msg);}}Session session;Properties props = new Properties();props.put("mail.transport.protocol", "smtp");if(isAnonymousEmail) {//only anonymous email needs param smtpHostprops.put("mail.smtp.host", smtpHost);props.put("mail.smtp.auth", "false");session = Session.getInstance(props, null);} else {//normal email does not need param smtpHost and uses the default host SMTPServerprops.put("mail.smtp.host", SMTPServer); props.put("mail.smtp.auth", "true");session = Session.getInstance(props, new MailAuthenticator(SMTPUsername, SMTPPassword));}//create messageMimeMessage msg = new MimeMessage(session);try {//Multipart is used to store many BodyPart objects.Multipart multipart=new MimeMultipart();BodyPart part = new MimeBodyPart();part.setContent(content,"text/html;charset=gb2312");//add email content part.multipart.addBodyPart(part);//add attachment parts.if(attachments != null && attachments.length > 0) {for(File attachment: attachments) {String fileName = attachment.getName();DataSource dataSource = new FileDataSource(attachment);DataHandler dataHandler = new DataHandler(dataSource);part = new MimeBodyPart();part.setDataHandler(dataHandler);//solve encoding problem of attachments file name.try {fileName = MimeUtility.encodeText(fileName);} catch (UnsupportedEncodingException e) {LOGGER.error("Cannot convert the encoding of attachments file name.", e);}//set attachments the original file name. if not set, //an auto-generated name would be used.part.setFileName(fileName);multipart.addBodyPart(part);}}msg.setSubject(subject);msg.setSentDate(new Date());//set sendermsg.setFrom(new InternetAddress(from));//set receiver, for(String to: tos) {msg.addRecipient(RecipientType.TO, new InternetAddress(to));}if(ccs != null && ccs.length > 0) {for(String cc: ccs) {msg.addRecipient(RecipientType.CC, new InternetAddress(cc));}}if(bccs != null && bccs.length > 0) {for(String bcc: bccs) {msg.addRecipient(RecipientType.BCC, new InternetAddress(bcc));}}msg.setContent(multipart);//save the changes of email first.msg.saveChanges();//to see what commands are used when sending a email, use session.setDebug(true)//session.setDebug(true);//send emailTransport.send(msg); LOGGER.info("Send email success.");System.out.println("Send html email success.");} catch (NoSuchProviderException e) {LOGGER.error("Email provider config error.", e);} catch (MessagingException e) {LOGGER.error("Send email error.", e);}}/*** Receive Email from POPServer. Use POP3 protocal by default. Thus,* call this method, you need to provide a pop3 mail server address.* @param emailAddress The email account in the POPServer.* @param password The password of email address.*/public static void receiveEmail(String host, String username, String password) {//param check. If param is null, use the default configured value.if(host == null) {host = POP3Server;}if(username == null) {username = POP3Username;}if(password == null) {password = POP3Password;}Properties props = System.getProperties();//MailAuthenticator authenticator = new MailAuthenticator(username, password);try {Session session = Session.getDefaultInstance(props, null);// Store store = session.getStore("imap");Store store = session.getStore("pop3");// Connect POPServerstore.connect(host, username, password);Folder inbox = store.getFolder("INBOX");if (inbox == null) {throw new RuntimeException("No inbox existed.");}// Open the INBOX with READ_ONLY mode and start to read all emails.inbox.open(Folder.READ_ONLY);System.out.println("TOTAL EMAIL:" + inbox.getMessageCount());Message[] messages = inbox.getMessages();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");for (int i = 0; i < messages.length; i++) {Message msg = messages[i];String from = InternetAddress.toString(msg.getFrom());String replyTo = InternetAddress.toString(msg.getReplyTo());String to = InternetAddress.toString(msg.getRecipients(Message.RecipientType.TO));String subject = msg.getSubject();Date sent = msg.getSentDate();Date ress = msg.getReceivedDate();String type = msg.getContentType();System.out.println((i + 1) + ".---------------------------------------------");System.out.println("From:" + mimeDecodeString(from));System.out.println("Reply To:" + mimeDecodeString(replyTo));System.out.println("To:" + mimeDecodeString(to));System.out.println("Subject:" + mimeDecodeString(subject));System.out.println("Content-type:" + type);if (sent != null) {System.out.println("Sent Date:" + sdf.format(sent));}if (ress != null) {System.out.println("Receive Date:" + sdf.format(ress));}
//                //Get message headers.
//                @SuppressWarnings("rawtypes")
//                Enumeration headers = msg.getAllHeaders();
//                while (headers.hasMoreElements()) {
//                    Header h = (Header) headers.nextElement();
//                    String name = h.getName();
//                    String val = h.getValue();
//                    System.out.println(name + ": " + val);
//                }//                //get the email content.
//                Object content = msg.getContent();
//                System.out.println(content);
//                //print content
//                Reader reader = new InputStreamReader(
//                        messages[i].getInputStream());
//                int a = 0;
//                while ((a = reader.read()) != -1) {
//                    System.out.print((char) a);
//                }}// close connection. param false represents do not delete messaegs on server.inbox.close(false);store.close();
//        } catch(IOException e) {
//            LOGGER.error("IOException caught while printing the email content", e);} catch (MessagingException e) {LOGGER.error("MessagingException caught when use message object", e);}}/*** For receiving an email, the sender, receiver, reply-to and subject may * be messy code. The default encoding of HTTP is ISO8859-1, In this situation, * use MimeUtility.decodeTex() to convert these information to GBK encoding.* @param res The String to be decoded.* @return A decoded String.*/private static String mimeDecodeString(String res) {if(res != null) {String from = res.trim();try {if (from.startsWith("=?GB") || from.startsWith("=?gb")|| from.startsWith("=?UTF") || from.startsWith("=?utf")) {from = MimeUtility.decodeText(from);}} catch (Exception e) {LOGGER.error("Decode string error. Origin string is: " + res, e);}return from;}return null;}
}

转载于:https://my.oschina.net/itblog/blog/303435

Java发送邮件工具类(可发送匿名邮件)相关推荐

  1. java发送邮件工具类的编写直接复制版(兼补上篇邮箱改密发送邮件)

    上篇关于如何通过邮箱发送修改密码的链接的教程我取的名字叫作<炫酷又实用的发送邮箱链接修改密码保姆级教程>,文章发表之后发现炫酷呢?炫酷在那里?除了几段乏味的代码之外,什么也没有啊,甚至连怎 ...

  2. Hutool Java常用工具类汇总

    简介 Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以"甜甜的". Hu ...

  3. jmail组件 java_Jmail发送邮件工具类分享

    import lombok.extern.slf4j.Slf4j; import java.util.Properties; import javax.mail.*; import javax.mai ...

  4. day037 listener Timer 发送邮件工具类

    1 WEB监听器 1.1 概述 Web监听器就是一个组件,可以对其他的内容,进行监视控制,当被监视的内容,发生指定的行为时,web监听器就可以给出相应的处理方案. 事件源:被监视的内容. 监听器:监视 ...

  5. MailUtil-Java发送邮件工具类

    分享做项目时写的邮件的工具类: 一.准备工作: 1.使用java邮箱发送邮件,需要在邮箱设置中申请开通 POP3/SMTP 服务,以163邮箱为例: 在网页上打开登录163邮箱,点击设置-POP3/S ...

  6. java spring 开启SMTP服务发送QQ邮件

    首先进入自己的QQ邮箱,在设置中修改账户信息 然后来至底部 点击开启,再用手机发送对应信息到指定号码,然后点击我已发送 获取授权码 注意提示: 到这里,相信你已经开通了SMTP服务,这样就可以在jav ...

  7. 手工轻松发送匿名邮件

    发送匿名邮件通常需要工具软件和有该服务的服务器来实现.如果你想发送一封匿名邮件,一时又忘记了提供该项服务的网站服务器地址,或者手头又没有专门的发信工具软件,那么本文介绍的手工发送匿名邮件的方法或许对你 ...

  8. JAVA常用工具类(实用高效)

    JAVA常用工具类(根据GITHUB代码统计) 从Google你能搜索到大量的关于Struts,Spring,Hibernate,iBatis等比较大的框架的资料,但是很少有人去关注一些小的工具包,但 ...

  9. java 定时任务每月1号发送excel邮件

    java 定时任务每月1号发送excel邮件 1.生成excel 数据流 public ByteArrayInputStream exportxls(){String preDate = " ...

最新文章

  1. 神经科学家为视力受损的人研发新技术
  2. replugin源码解析之replugin-host-gradle(宿主的gradle插件)
  3. 请求接口获取到的数据其中出现null值,处理的时候导致了程序crash,解决方案如下:...
  4. 二维数组求子数组中最大的和
  5. SQLSever 存储过程创建
  6. 【概率论】1-0:介绍
  7. Django模板之显示QuerySet内容,字典中内容
  8. wsdl接口_DEBUG系列四:第三方接口debug
  9. WindowsXP操作系统进程详细介绍
  10. visual foxpro 程序员指南_1024程序员节:盘点小红书今年发生的重要bug
  11. 【转】Android 中的 Service 全面总结
  12. python查看数据库存在表_python sqlite3查看数据库所有表(table)
  13. 计算机犀牛建人体模型步骤,Clayoo加Rhino如何建模卡通人物2
  14. mysql命令更新数据库_命令操作MySQL数据库
  15. ER-X刷回原版固件方法(救砖)
  16. html5置顶标签css样式,html5 header标签 html header css布局教程 /header
  17. CSDN博客界面自定义之右侧博客栏目修改
  18. C++读写CSV文件
  19. Python数据处理课程设计-房屋价格预测
  20. 18天精读掌握《费曼物理学讲义卷一》 第2天 2019.6.13

热门文章

  1. RHEL5上配置VNCSERVER
  2. 编程方式刷新Squid缓存服务器的五种方法
  3. 在C#中使用COM+实现事务控制
  4. 吴恩达老师深度学习视频课笔记:序列模型和注意力机制
  5. Dlib库中实现正脸人脸检测的测试代码
  6. 2 拖拽模型_3Dmax插件 | 螺丝钉、挖洞、网格模型等可以分分钟搞定
  7. java 办公,中文JAVA技术网
  8. mysql实现sass_使用sass绘制三角形
  9. opengl正方形绕点旋转_一题十五种解法够不够? 旋转,构造,四点共圆乐不停...
  10. 敲的php代码怎么预览,php代码实现文件的预览