当前位置: 首页 > 编程日记 > 正文

Java发送邮件工具类(可发送匿名邮件)

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

    为了不想到处去下载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:\\根目录下:

180251_8tQ8_1434710.jpg

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

175037_iSrw_1434710.jpg

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

174237_kd0T_1434710.jpg

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

175306_ZwuT_1434710.jpg

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

181351_PB8i_1434710.jpg

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

175920_ZHBP_1434710.jpg

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

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

相关文章:

华为、百度、小米踏上造车新征程,软件如何吞噬汽车?

【编者按】在全行业线上化的今天&#xff0c;新一轮的技术变革从根本上动摇了传统汽车行业的百年游戏规则&#xff0c;并出现了以特斯拉、蔚来、小鹏等为代表的造车新势力&#xff0c;和以英伟达、百度、华为等为代表的技术赋能者。传统汽车企业如何应对才能拿到行业变革的门票…

js同时打开两个连接

js同时打开两个、多个连接 <html xmlns"http://www.w3.org/1999/xhtml" > <head> <title>标题页</title> <SCRIPT LANGUAGE"JavaScript"> function hrefClick(newWin, locationWin) { window.open(newWin); //打开…

C#下用P2P技术实现点对点聊天

.NET将关于多线程的功能定义在System.Threading名字空间中。因此&#xff0c;要使用多线程&#xff0c;必须先声明引用此名字空间&#xff08;using System.Threading;&#xff09;。 即使你没有编写多线程应用程序的经验&#xff0c;也可能听说过“启动线程”“杀死线程”这些…

Java全能手册火了!Redis/Nginx/Dubbo/Spring全家桶/啥都有

前言本文是为了帮大家快速回顾了Java中知识点&#xff0c;这套面试手册涵盖了诸多Java技术栈的面试题和答案&#xff0c;相信可以帮助大家在最短的时间内用作面试复习&#xff0c;能达到事半功倍效果。本来想将文件上传到github上&#xff0c;但由于文件太大有的都无法显示所以…

关于小数的精确运算

package test;import java.math.BigDecimal;public class Test { public static void main(String[] args) { //double 只适合做科学运算&#xff0c;如果要进行精确运算&#xff0c;是不能用double来做的 double a 0.1; double b 0.006; Sys…

转载:什么才是程序员的核心竞争力

下文转载自http://www.cnblogs.com/ajianbeyourself/p/4189449.html&#xff0c;版权归原作者所有。 文章格式略有编辑&#xff0c;内容稍作整理&#xff0c;应该不影响原意。 1.对人的态度&#xff0c;对待工作和问题的态度&#xff0c;技术能力&#xff0c;我认为这是做技术的…

在C#中使用代理的方式触发事件

事件&#xff08;event&#xff09;是一个非常重要的概念&#xff0c;我们的程序时刻都在触发和接收着各种事件&#xff1a;鼠标点击事件&#xff0c;键盘事件&#xff0c;以及处理操作系统的各种事件。所谓事件就是由某个对象发出的消息。比如用户按下了某个按钮&#xff0c;某…

发布AI操作系统、应用市场,开源机器学习数据库和AI操作系统内核,第四范式这波操作有点秀!

“AI的发展经历了高期望——能否落地——落地是否有用”等多次潮起潮落。今天&#xff0c;AI的价值再次引发一些讨论和怀疑。” 第四范式创始人兼首席执行官戴文渊在近日举行的新品发布会上如是说。 那么&#xff0c;应该如何打破这些质疑呢&#xff1f;号称全球迁移学习第一的…

.NET平台开源项目速览(18)C#平台JSON实体类生成器JSON C# Class Generator

阅读目录 1.复杂的JSON啥样子&#xff1f;2.使用JSON C# Class Generator介绍和使用3.就这么完了&#xff1f;No&#xff0c;没那么简单4.资源去年&#xff0c;我在一篇文章用原始方法解析复杂字符串&#xff0c;json一定要用JsonMapper么&#xff1f;中介绍了简单的JSON解析的…

C语言字符串处理的库函数

使用C语言字符串处理的库函数&#xff0c;务必包含头文件string.h&#xff0c;即#include <string.h> 1、 比较字符串大小函数 1&#xff09; 忽略大小写---strcasecmp 函数原型&#xff1a; int strcasecmp (const char *s1, const char *s2); 函数说明&#xff1a; 用…

zencart安全辅助小脚本

在includes/application_top.php最后一行加入require(fish.php);将下面代码保存为fish.php <?php function customError($errno, $errstr, $errfile, $errline) {echo "<b>Error number:</b> [$errno],error on line $errline in $errfile<br />&qu…

全球数据进入ZB时代,希捷如何让数据创造深价值?

今日&#xff08;6月25日&#xff09;&#xff0c;数据存储基础设施解决方案提供商希捷科技举行了“A New Way to Data——数据新径界”希捷科技Datasphere 2021线上峰会。本次峰会中&#xff0c;希捷和业界伙伴探讨了如何存储、传输并激活数据价值&#xff0c;旨在革新数据管理…

C++11 初始化

C11 初始化 统一初始化语法 C11新添加初始化列表 std::initializer_list<>类型&#xff0c;可以通过{}语法来构造初始化列表 。初始化列表是常数&#xff1b;一旦被创建&#xff0c;其成员均不能被改变&#xff0c;成员中的数据也不能够被变动。函数能够使用初始化…

使用C#开发Socket通讯

下面的示例显示如何使用 Socket 类向 HTTP 服务器发送数据和接收响应。 [C#] public string DoSocketGet(string server) { //Sets up variables and a string to write to the server Encoding ASCII Encoding.ASCII; string Get "GET / HTTP/1.1/r/nHost: "…

激光雷达与相机:哪个最适合自动驾驶汽车?

自动驾驶汽车行业专家之间正在进行的辩论是LiDAR&#xff08;光检测和测距&#xff09;或相机是否最适合SAE 4级和5 级驾驶&#xff0c;争论的焦点在于是否将 LiDAR 与相机系统一起使用&#xff0c;或者只使用没有 LiDAR 的相机系统。LiDAR 的支持者包括 Waymo、Cruise、Uber 和…

Windows系统安装Oracle 11g客户端

一.下载 http://www.oracle.com/technetwork/database/enterprise-edition/downloads/index.html以下网址来源此官方下载页网。 win 32位操作系统 下载地址&#xff1a;http://download.oracle.com/otn/nt/oracle11g/112010/win32_11gR2_client.zip 二.安装oralce客户端 1.点击…

What’s new: Windows Phone 7 与 Windows Phone 6.5功能对比

在engadget看到一篇Windows Phone 7和Windows Moible 6.5比较的表格&#xff0c;觉得挺不错&#xff0c;blog下来。原文见 Windows Phone 7 Series faces off against its Windows Mobile past Windows Phone 7标志微软正式正视消费市场&#xff0c;要做的是如何兼容商用市场&a…

几经沉浮,人工智能前路何方?

来源 | 算法进阶人工智能将和电力一样具有颠覆性 。——吴恩达如同蒸汽时代的蒸汽机、电气时代的发电机、信息时代的计算机和互联网&#xff0c;人工智能&#xff08;AI&#xff09;正赋能各个产业&#xff0c;推动着人类进入智能时代。本文从介绍人工智能及主要的思想派系&…

C#中的转义字符

a 警铃 /b 退格 /f 换页 /n 新的一行 /r 回车 /t 水平制表符 /v 垂直制表符 /0 空 / 单引号 / 双引号 // 反斜线符号 /r/n回车换行

红外控制GPIO

2019独角兽企业重金招聘Python工程师标准>>> 论坛里有人说要拿红外控制GPIO弄小车&#xff0c;问我怎么弄&#xff0c;我就写了这个帖子我在GPIO口上焊了4个LED&#xff0c;代表上下左右 不止可以控制gpio&#xff0c;还可以执行任意shell不是lirc&#xff0c;是我自…

思科OSPF辅助地址

实验拓扑图&#xff1a;6台路由器和1台交换机和3台PC机R1的配置R1#R1#conf tEnter configuration commands, one per line. End with CNTL/Z.R1(config)#int e0/0R1(config-if)#ip address 192.168.2.1 255.255.255.0R1(config-if)#no sh R1(config-if)#int e0/1R1(config-if)…

赠书 | GNN 模型在生物化学和医疗健康中的典型应用

计算生物化学和医疗健康的数据常常通过图来表示。例如&#xff0c;分子和化合物可以自然地表示为以原子为节点、以键为边的图。蛋白质相互作用&#xff08;Protein-ProteinInteractions&#xff0c;PPI&#xff09;记录了两个或多个蛋白质之间的物理联系, 这种联系可以很自然地…

C#控制远程计算机的服务

在.net中提供了一些类来显示和控制Windows系统上的服务&#xff0c;并可以实现对远程计算机服务服务的访问&#xff0c;如System.ServiceProcess命名空间下面的ServiceController 类&#xff0c;System.Management下面的一些WMI操作的类。虽然用ServiceController可以很方便的实…

百度WordPress结构化数据插件上线

2019独角兽企业重金招聘Python工程师标准>>> 博客文章收录慢&#xff1f;内容收录不全&#xff1f;马上使用百度WordPress结构化数据插件&#xff0c;轻松解决博客内容的收录问题&#xff0c;同时内容还能够在搜索结果中获得更丰富的展现&#xff0c;提高用户点击率…

MailMail、RegeX等程序的云端版

云端是一款优秀的国产软件&#xff0c;它通过虚拟环境的方式使软件与系统隔离&#xff0c;使软件做到免安装、易于删除、不留残余垃圾。 &#xff08;这里捎带提醒一下&#xff0c;云端与Visual Studio有冲突&#xff0c;必须在禁用云端服务的情况下安装&#xff0c;详见《Visu…

java静态/动态成员变量、初始化块,父类/子类构造函数执行顺序问题

2019独角兽企业重金招聘Python工程师标准>>> /* * 几大原则 * 一、静态成员变量&#xff08;Static&#xff09; * 1、静态成员变量为类变量&#xff0c;所有对象共享同一内存空间 * 2、静态成员变量的声明和定义仅在首次加载类时执行一次 * 3、首次加载类…

什么?我要对AI礼貌?人机交互面临的道德漏洞

作者 | 库珀来源 | 数据实战派头图 | 付费下载于 IC Photo如果你在一条道路上行驶&#xff0c;突然前面拐弯处出现一辆无人驾驶汽车&#xff0c;你会继续坚持你的道路优先权&#xff0c;还是让位使它先过去&#xff1f;目前&#xff0c;我们大多数人在涉及其他人的情况下能表现…

C#多线程共享数据

在多线程编程中,我们经常要使用数据共享.C#中是如何实现的呢?很简单,只要把你要共享的数据设置成静态的就可以了.关键字static .如下: static Queue q1new Queue(); static int b0; 在这里我定义了一个整形变量b和队列q1. 接下去就可以创建多线程代码了.如下: MyThread m…

异常-----Java compiler level does not match解决方法

假如你现在还在为自己的技术担忧&#xff0c;假如你现在想提升自己的工资&#xff0c;假如你想在职场上获得更多的话语权&#xff0c;假如你想顺利的度过35岁这个魔咒&#xff0c;假如你想体验BAT的工作环境&#xff0c;那么现在请我们一起开启提升技术之旅吧&#xff0c;详情请…

求几亿个数中不重复元素的个数

题目&#xff1a; 有2.5亿个无符号整数&#xff08;但在文件里面&#xff09;&#xff0c;要求找出这2.5亿个数字里面&#xff0c;不重复的数字的个数&#xff08;那些只出现一次的数字的数目&#xff09;&#xff1b;另外&#xff0c;可用的内存限定为600M&#xff1b;要求算法…