📄 smtpmailsender.java
字号:
/**
* @CreatedDate Nov 5, 2008
*/
/**
* @CreatedDate Nov 5, 2008
*/
package com.jdev.mail;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.Locale;
import java.util.regex.Pattern;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.*;
import com.jdev.security.Base64;
import com.jdev.util.LogManager;
/**
* SMTP邮件发送系统。
*
*
* 发件人和收件人的正确格式如下:
*
* 例1: "Sol"<sol@gameeden.org>
* 例2: Sol<sol@gameeden.org>
* 例3: <sol@gameeden.org>
* 例4: sol@gameeden.org
*
* @author Sol
* @since 1.5
*/
public final class SmtpMailSender {
/**
* 发送成功的常量。
*/
public final static boolean SUCCESSFUL = true;
/**
* 发送失败的常量。
*/
public final static boolean FAILED = false;
private final static int PORT = 25;//服务器端口(SMTP服务器和邮件接收服务器的端口均为25)
private final static int RETRY = 3;//当连接SMTP服务器失败后尝试重新连接的次数(仅用于发送ESMTP邮件)
private final static int INTERVAL = 1000;//当连接SMTP服务器失败后重新连接的时间间隔(仅用于发送ESMTP邮件)
private final static int TIMEOUT = 10000;//网络连接的超时时间
private final static String BOUNDARY;//MIME分格符
private final static String CHARSET;//虚拟机的默认编码
private final static Pattern PATTERN;//用于效验邮箱地址的正确性
private static InitialDirContext dirContext;//用于查询DNS记录
private final ArrayList<LogManager> logManager;//日志管理器
private boolean isEsmtp;//发送类型
private String smtp;//SMTP服务器地址(仅用于发送ESMTP邮件)
private String user;//用户名(仅用于发送ESMTP邮件)
private String password;//密码(仅用于发送ESMTP邮件)
private String sender;//发件人名字
private String senderAddress;//发件人的E-Mail地址
static {
BOUNDARY = "Boundary-=_hMbeqwnGNoWeLsRMeKTIPeofyStu";
CHARSET = Charset.defaultCharset().displayName();
PATTERN = Pattern.compile(".+@[^.@]+(\\.[^.@]+)+$");//此处放弃了传统匹配方式,这是为了兼容非英文域名的电子邮箱
Hashtable<String, String> hashtable = new Hashtable<String, String>();
hashtable.put("java.naming.factory.initial",
"com.sun.jndi.dns.DnsContextFactory");
try {
dirContext = new InitialDirContext(hashtable);
} catch (NamingException e) {
}
}
private SmtpMailSender(String from) {
if (from == null) {
throw new IllegalArgumentException("参数from不能为null。");
}
int leftSign = (from = from.trim()).charAt(from.length() - 1) == '>' ? from
.lastIndexOf('<')
: -1;
senderAddress = leftSign > -1 ? from.substring(leftSign + 1,
from.length() - 1).trim() : from;
if (!PATTERN.matcher(senderAddress).find()) {
throw new IllegalArgumentException("参数from不正确。");
}
sender = leftSign > -1 ? from.substring(0, leftSign).trim() : null;
logManager = new ArrayList<LogManager>();
isEsmtp = false;
if (sender != null) {
if (sender.length() == 0) {
sender = null;
} else if (sender.charAt(0) == '"'
&& sender.charAt(sender.length() - 1) == '"') {
sender = sender.substring(1, sender.length() - 1).trim();
}
}
}
private SmtpMailSender(String address, String from, String user,
String password) {
this(from);
isEsmtp = true;
this.smtp = address;
this.user = Base64.encode(user.getBytes());
this.password = Base64.encode(password.getBytes());
}
/**
* 创建SMTP邮件发送系统实例。
* @param from 发件人
* @return SMTP邮件发送系统的实例
* @throws IllegalArgumentException 如果参数from为null或格式不正确
*/
public static SmtpMailSender createSmtpMailSender(String from)
throws IllegalArgumentException {
return new SmtpMailSender(from);
}
/**
* 创建ESMTP邮件发送系统实例。
* @param smtp SMTP服务器地址
* @param from 发件人
* @param user 用户名
* @param password 密码
* @return SMTP邮件发送系统的实例
* @throws IllegalArgumentException 如果参数from为null或格式不正确
*/
public static SmtpMailSender createESmtpMailSender(String smtp,
String from, String user, String password)
throws IllegalArgumentException {
return new SmtpMailSender(smtp, from, user, password);
}
/**
* 发送邮件。
* @param to 收件人
* @param subject 主题
* @param content 正文
* @param attachments 附件
* @param isHtml 使用网页形式发送
* @param isUrgent 紧急邮件
* @return 是否发送成功
* @throws IllegalArgumentException 如果参数to为null或格式不正确
*/
public boolean sendMail(String to, String subject, String content,
File[] attachments, boolean isHtml, boolean isUrgent)
throws IllegalArgumentException {
if (to == null) {
throw new IllegalArgumentException("参数to不能为null。");
}
int leftSign = (to = to.trim()).charAt(to.length() - 1) == '>' ? to
.lastIndexOf('<') : -1;
String addresseeAddress = leftSign > -1 ? to.substring(leftSign + 1,
to.length() - 1).trim() : to;//收件人的E-Mail地址
if (!PATTERN.matcher(addresseeAddress).find()) {
throw new IllegalArgumentException("参数to不正确。");
}
String addressee = leftSign > -1 ? to.substring(0, leftSign).trim()
: null;//收件人名字
boolean needBoundary = attachments != null && attachments.length > 0;
Socket socket = null;
InputStream in = null;
OutputStream out = null;
byte[] data;
try {
if (addressee != null) {
if (addressee.length() == 0) {
addressee = null;
} else if (addressee.charAt(0) == '"'
&& addressee.charAt(addressee.length() - 1) == '"') {
addressee = addressee.substring(1, addressee.length() - 1)
.trim();
}
}
if (isEsmtp) {
for (int k = 1;; k++) {
try {
log("连接: 主机:\"" + smtp + "\" 端口:\"" + PORT + "\"");
socket = new Socket(smtp, PORT);
break;
} catch (IOException e) {
log("错误: 连接失败" + k + "次");
if (k == RETRY) {
return FAILED;
}
try {
Thread.sleep(INTERVAL);
} catch (InterruptedException ie) {
}
}
}
in = socket.getInputStream();
out = socket.getOutputStream();
if (response(in) != 220) {
return FAILED;
}
} else {
log("状态: 创建邮件接收服务器列表");
String[] address = parseDomain(parseUrl(addresseeAddress));
if (address == null) {
return FAILED;
}
for (int k = 0; k < address.length; k++) {
try {
log("连接: 主机:\"" + address[k] + "\" 端口:\"" + PORT + "\"");
socket = new Socket(address[k], PORT);
in = socket.getInputStream();
out = socket.getOutputStream();
if (response(in) != 220) {
return FAILED;
}
break;
} catch (IOException e) {
log("错误: 连接失败");
}
}
}
if (in == null || out == null) {
return FAILED;
}
socket.setSoTimeout(TIMEOUT);
sendString("HELO " + parseUrl(senderAddress), out);
sendNewline(out);
if (response(in) != 250) {
return FAILED;
}
if (isEsmtp) {
sendString("AUTH LOGIN", out);
sendNewline(out);
if (response(in) != 334) {
return FAILED;
}
sendString(user, out);
sendNewline(out);
if (response(in) != 334) {
return FAILED;
}
sendString(password, out);
sendNewline(out);
if (response(in) != 235) {
return FAILED;
}
}
sendString("MAIL FROM: <" + senderAddress + ">", out);
sendNewline(out);
if (response(in) != 250) {
return FAILED;
}
sendString("RCPT TO: <" + addresseeAddress + ">", out);
sendNewline(out);
if (response(in) != 250) {
return FAILED;
}
sendString("DATA", out);
sendNewline(out);
if (response(in) != 354) {
return FAILED;
}
sendString("From: "
+ (sender == null ? senderAddress : getBase64String(sender)
+ " <" + senderAddress + ">"), out);
sendNewline(out);
sendString("To: "
+ (addressee == null ? addresseeAddress
: getBase64String(addressee) + " <"
+ addresseeAddress + ">"), out);
sendNewline(out);
sendString("Subject: " + getBase64String(subject), out);
sendNewline(out);
sendString("Date: "
+ new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z (z)",
Locale.US).format(new Date()), out);
sendNewline(out);
sendString("MIME-Version: 1.0", out);
sendNewline(out);
if (needBoundary) {
sendString("Content-Type: multipart/mixed; BOUNDARY=\""
+ BOUNDARY + "\"", out);
sendNewline(out);
} else {
if (isHtml) {
sendString("Content-Type: text/html; charset=\"" + CHARSET
+ "\"", out);
sendNewline(out);
} else {
sendString("Content-Type: text/plain; charset=\"" + CHARSET
+ "\"", out);
sendNewline(out);
}
}
sendString("Content-Transfer-Encoding: base64", out);
sendNewline(out);
if (isUrgent) {
sendString("X-Priority: 1", out);
sendNewline(out);
} else {
sendString("X-Priority: 3", out);
sendNewline(out);
}
sendString("X-Mailer: BlackFox Mail[Copyright(C) 2007 Sol]", out);
sendNewline(out);
log("发送: ");
sendNewline(out);
if (needBoundary) {
sendString("--" + BOUNDARY, out);
sendNewline(out);
if (isHtml) {
sendString("Content-Type: text/html; charset=\"" + CHARSET
+ "\"", out);
sendNewline(out);
} else {
sendString("Content-Type: text/plain; charset=\"" + CHARSET
+ "\"", out);
sendNewline(out);
}
sendString("Content-Transfer-Encoding: base64", out);
sendNewline(out);
log("发送: ");
sendNewline(out);
}
data = (content != null ? content : "").getBytes();
for (int k = 0; k < data.length; k += 54) {
sendString(Base64
.encode(data, k, Math.min(data.length - k, 54)), out);
sendNewline(out);
}
if (needBoundary) {
RandomAccessFile attachment = null;
int fileIndex = 0;
String fileName;
int k;
data = new byte[54];
try {
for (; fileIndex < attachments.length; fileIndex++) {
fileName = attachments[fileIndex].getName();
attachment = new RandomAccessFile(
attachments[fileIndex], "r");
sendString("--" + BOUNDARY, out);
sendNewline(out);
sendString(
"Content-Type: "
+ MimeTypeFactory.getMimeType(fileName
.indexOf(".") == -1 ? "*"
: fileName.substring(fileName
.lastIndexOf(".") + 1))
+ "; name=\""
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -