前言#
今天做了一個自動打卡工具,準備搭建郵件服務,反饋打卡結果。使用的郵箱是 yandex mail。出現 Got bad greeting from SMTP host: smtp.yandex.com, port: 465, response: [EOF],記錄一下。
郵箱準備#
使用郵箱沒有要求,支持 smtp 就行
註冊郵箱#
登錄郵箱#
登錄郵箱後可能會提示開啟 smtp。
這裡是官方提供打開 smtp 的文檔,還記錄了端口號。
啟用 smtp#
郵箱首頁右上角的齒輪 > security> Email clients>
這裡我使用 outlook 測試了連接,使用的端口是 465,附連接成功圖。
郵件協議#
SMTP 協議#
SMTP 是一個相對簡單的基於文本的協議。在其之上指定了一條消息的一個或多個接收者(在大多數情況下被確認是存在的),然後消息文本會被傳輸。可以很簡單地通過 telnet 程序來測試一個 SMTP 服務器。提供了 SSL 加密的 SMTP 協議被稱為 SMTPS,SMTP 使用 TCP 端口25
,SMTPS 使用 TCP 端口465
POP3 協議#
POP3,全名為 “Post Office Protocol - Version 3”,即 “郵局協議版本 3”。是 TCP/IP 協議族中的一員,由 RFC1939 定義。本協議主要用於支持使用客戶端遠程管理在服務器上的電子郵件。提供了 SSL 加密的 POP3 協議被稱為 POP3S,POP3 默認端口110
,POP3S 默認端口995
。
IMAP 協議#
IMAP(Internet Mail Access Protocol)以前稱作交互郵件訪問協議(Interactive Mail Access Protocol),是一個應用層協議。IMAP 是斯坦福大學在 1986 年開發的一種郵件獲取協議。它的主要作用是郵件客戶端可以通過這種協議從郵件服務器上獲取郵件的信息,下載郵件等。當前的權威定義是 RFC3501。IMAP 協議運行在 TCP/IP 協議之上,使用的端口是143
。它與 POP3 協議的主要區別是用戶可以不用把所有的郵件全部下載,可以通過客戶端直接對服務器上的郵件進行操作,提供了 SSL 加密的 IMAP 協議被稱為 IMAP S,POP3 默認端口143
,POP3S 默認端口993
。
搭建步驟及簡單使用#
mail 依賴#
build.gradl 文件
compile group: 'org.springframework.boot', name: 'spring-boot-starter-mail', version: '2.3.2.RELEASE'
yml 配置文件#
server:
port: 80
logging:
level:
web: debug
spring:
mail:
default-encoding: UTF-8
host: smtp.yandex.com
username: [email protected]
password: 123456
port: 25 #smtp協議使用25端口
# port: 465 #smtps使用465端口,不然報錯。
protocol: smtp #指定協議
test-connection: true
properties:
mail:
smtp:
auth: true # 使用
starttls: # 使用 SSL 安全協議,須如下配置
enable: true
required: true
java 代碼#
MailService 接口文件#
public interface MailService {
/**
* 發送純文本郵件
* @param toAddr 收件人
* @param title 標題
* @param content 內容
*/
void sendTextMail(String toAddr, String title, String content);
/**
* 發送 html 郵件
* @param toAddr 收件人
* @param title 標題
* @param content 內容(HTML)
*/
void sendHtmlMail(String toAddr, String title, String content);
/**
* 發送待附件的郵件
* @param toAddr 收件人
* @param title 標題
* @param content 內容
* @param filePath 附件地址
*/
void sendAttachmentsMail(String toAddr, String title, String content, String filePath);
/**
* 發送文本中有靜態資源(圖片)的郵件
* @param toAddr 收件人
* @param title 標題
* @param content 內容
* @param rscPath 資源路徑
* @param rscId 資源id (可能有多個圖片)
*/
void sendInlineResourceMail(String toAddr, String title, String content, String rscPath, String rscId);
}
MailServiceImpl 文件#
@Component
public class MailServiceImpl implements MailService {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final JavaMailSender mailSender;
/**
* 注入常量
*/
@Value("${spring.mail.username}")
private String from;
public MailServiceImpl(JavaMailSender mailSender) {
this.mailSender = mailSender;
}
/**
* 發送文本郵件
*
* @param toAddr 收件人
* @param title 標題
* @param content 內容
*/
@Override
public void sendTextMail(String toAddr, String title, String content) {
// 純文本郵件對象
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(from);
message.setTo(toAddr);
message.setSubject(title);
message.setText(content);
try {
mailSender.send(message);
if (logger.isInfoEnabled()) {
logger.info("Text郵件已經發送。");
}
} catch (Exception e) {
logger.error("發送Text郵件時發生異常!", e);
}
}
/**
* 發送 html 郵件
*
* @param toAddr 收件人
* @param title 標題
* @param content 內容(HTML)
*/
@Override
public void sendHtmlMail(String toAddr, String title, String content) {
// html 郵件對象
MimeMessage message = mailSender.createMimeMessage();
try {
//true表示需要創建一個multipart message
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(toAddr);
helper.setSubject(title);
helper.setText(content, true);
mailSender.send(message);
if (logger.isInfoEnabled()) {
logger.info("html郵件發送成功");
}
} catch (MessagingException e) {
logger.error("發送html郵件時發生異常!", e);
}
}
/**
* 發送待附件的郵件
*
* @param toAddr 收件人
* @param title 標題
* @param content 內容
* @param filePath 附件地址
*/
@Override
public void sendAttachmentsMail(String toAddr, String title, String content, String filePath) {
MimeMessage message = mailSender.createMimeMessage();
try {
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(toAddr);
helper.setSubject(title);
helper.setText(content, true);
FileSystemResource file = new FileSystemResource(new File(filePath));
String fileName = filePath.substring(filePath.lastIndexOf(File.separator));
helper.addAttachment(fileName, file);
mailSender.send(message);
if (logger.isInfoEnabled()) {
logger.info("帶附件的郵件已經發送。");
}
} catch (MessagingException e) {
logger.error("發送帶附件的郵件時發生異常!", e);
}
}
/**
* 發送文本中有靜態資源(圖片)的郵件
*
* @param toAddr 收件人
* @param title 標題
* @param content 內容
* @param rscPath 資源路徑
* @param rscId 資源id (可能有多個圖片)
*/
@Override
public void sendInlineResourceMail(String toAddr, String title, String content, String rscPath, String rscId) {
MimeMessage message = mailSender.createMimeMessage();
try {
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(toAddr);
helper.setSubject(title);
helper.setText(content, true);
FileSystemResource res = new FileSystemResource(new File(rscPath));
helper.addInline(rscId, res);
mailSender.send(message);
if (logger.isInfoEnabled()) {
logger.info("嵌入靜態資源的郵件已經發送。");
}
} catch (MessagingException e) {
logger.error("發送嵌入靜態資源的郵件時發生異常!", e);
}
}
}
測試類#
@SpringBootTest
class ClockInApplicationTests {
@Autowired
MailService mailService;
@Test
void sendTextMail(){
mailService.sendTextMail("[email protected]","單元測試","測試郵件發送");
}
}
運行結果#
如標題所述,出現了異常
使用協議對應端口後發送成功,附圖。
結語#
通過這個實踐,我覺得要寫代碼前還是要做很多準備,或者說我了解的東西不夠全面。出錯後百度異常信息怎麼都找不到,想着哪裡出錯了,之前使用 outlook 連接測試通過,應該就是代碼問題。後來看到很多人使用 25 端口發送,然後去百度端口才發現協議與端口不一致。所以我覺得寫代碼前的準備不充分或者知識儲備不足,這個重要信息都不了解。