者:yizhiwazi
www.jianshu.com/p/5eb000544dd7
SMTP全稱為Simple Mail Transfer Protocol(簡單郵件傳輸協議),它是一組用于從源地址到目的地址傳輸郵件的規范,通過它來控制郵件的中轉方式。SMTP認證要求必須提供賬號和密碼才能登陸服務器,其設計目的在于避免用戶受到垃圾郵件的侵擾。
IMAP全稱為Internet Message Access Protocol(互聯網郵件訪問協議),IMAP允許從郵件服務器上獲取郵件的信息、下載郵件等。IMAP與POP類似,都是一種郵件獲取協議。
POP3全稱為Post Office Protocol 3(郵局協議),POP3支持客戶端遠程管理服務器端的郵件。POP3常用于“離線”郵件處理,即允許客戶端下載服務器郵件,然后服務器上的郵件將會被刪除。目前很多POP3的郵件服務器只提供下載郵件功能,服務器本身并不刪除郵件,這種屬于改進版的POP3協議。
兩者最大的區別在于,IMAP允許雙向通信,即在客戶端的操作會反饋到服務器上,例如在客戶端收取郵件、標記已讀等操作,服務器會跟著同步這些操作。而對于POP協議雖然也允許客戶端下載服務器郵件,但是在客戶端的操作并不會同步到服務器上面的,例如在客戶端收取或標記已讀郵件,服務器不會同步這些操作。
JavaMailSender和JavaMailSenderImpl 是Spring官方提供的集成郵件服務的接口和實現類,以簡單高效的設計著稱,目前是Java后端發送郵件和集成郵件服務的主流工具。
非常簡單,直接在業務類注入JavaMailSenderImpl并調用send方法發送郵件。其中簡單郵件可以通過SimpleMailMessage來發送郵件,而復雜的郵件(例如添加附件)可以借助MimeMessageHelper來構建MimeMessage發送郵件。例如:
@Autowired
private JavaMailSenderImpl mailSender;
public void sendMail() throws MessagingException {
//簡單郵件
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
simpleMailMessage.setFrom("admin@163.com");
simpleMailMessage.setTo("socks@qq.com");
simpleMailMessage.setSubject("Happy New Year");
simpleMailMessage.setText("新年快樂!");
mailSender.send(simpleMailMessage);
//復雜郵件
MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage);
messageHelper.setFrom("admin@163.com");
messageHelper.setTo("socks@qq.com");
messageHelper.setSubject("Happy New Year");
messageHelper.setText("新年快樂!");
messageHelper.addInline("doge.gif", new File("xx/xx/doge.gif"));
messageHelper.addAttachment("work.docx", new File("xx/xx/work.docx"));
mailSender.send(mimeMessage);
}
所謂開箱即用其實就是基于官方內置的自動配置,翻看源碼可知曉郵件自動配置類(MailSenderPropertiesConfiguration) 為上下文提供了郵件服務實例(JavaMailSenderImpl)。具體源碼如下:
@Configuration
@ConditionalOnProperty(prefix = "spring.mail", name = "host")
class MailSenderPropertiesConfiguration {
private final MailProperties properties;
MailSenderPropertiesConfiguration(MailProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean
public JavaMailSenderImpl mailSender() {
JavaMailSenderImpl sender = new JavaMailSenderImpl();
applyProperties(sender);
return sender;
}
其中MailProperties是關于郵件服務器的配置信息,具體源碼如下:
@ConfigurationProperties(prefix = "spring.mail")
public class MailProperties {
private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
private String host;
private Integer port;
private String username;
private String password;
private String protocol = "smtp";
private Charset defaultEncoding = DEFAULT_CHARSET;
private Map<String, String> properties = new HashMap<>();
}
登陸網易郵箱163,在設置中打開并勾選POP3/SMTP/IMAP服務,然后會得到一個授權碼,這個郵箱和授權碼將用作登陸認證。
首先咱們通過 Spring Initializr 創建工程springboot-send-mail,如圖所示:
然后在pom.xml 引入web、thymeleaf 和spring-boot-starter-mail等相關依賴。例如:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator-core</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.7</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
根據前面提到的配置項(MailProperties)填寫相關配置信息,其中spring.mail.username 表示連接郵件服務器時認證的登陸賬號,可以是普通的手機號或者登陸賬號,并非一定是郵箱,為了解決這個問題,推薦大家在spring.mail. properties.from填寫郵件發信人即真實郵箱。
然后在application.yml添加如下配置:
spring:
mail:
host: smtp.163.com #SMTP服務器地址
username: socks #登陸賬號
password: 123456 #登陸密碼(或授權碼)
properties:
from: socks@163.com #郵件發信人(即真實郵箱)
thymeleaf:
cache: false
prefix: classpath:/views/
servlet:
multipart:
max-file-size: 10MB #限制單個文件大小
max-request-size: 50MB #限制請求總量
透過前面的進階知識,我們知道在發送郵件前,需要先構建 SimpleMailMessage或 MimeMessage 郵件信息類來填寫郵件標題、郵件內容等信息,最后提交給JavaMailSenderImpl發送郵件,這樣看起來沒什么問題,也能實現既定目標,但在實際使用中會出現大量零散和重復的代碼,還不便于保存郵件到數據庫。
那么優雅的發送郵件應該是如何的呢?應該屏蔽掉這些構建信息和發送郵件的細節,不管是簡單還是復雜郵件,都可以通過統一的API來發送郵件。例如:mailService.send(mailVo) 。
例如通過郵件信息類(MailVo) 來保存發送郵件時的郵件主題、郵件內容等信息 :
package com.hehe.vo;
public class MailVo {
private String id;//郵件id
private String from;//郵件發送人
private String to;//郵件接收人(多個郵箱則用逗號","隔開)
private String subject;//郵件主題
private String text;//郵件內容
private Date sentDate;//發送時間
private String cc;//抄送(多個郵箱則用逗號","隔開)
private String bcc;//密送(多個郵箱則用逗號","隔開)
private String status;//狀態
private String error;//報錯信息
@JsonIgnore
private MultipartFile[] multipartFiles;//郵件附件
//省略GET&SET方法
}
===========接下來正式介紹發送郵件的最核心邏輯 前方高能=============
除了發送郵件之外,還包括檢測郵件和保存郵件等操作,例如:
本案例郵件業務類 MailService 的具體源碼如下:
package com.hehe.service;
/**
* 郵件業務類 MailService
*/
@Service
public class MailService {
private Logger logger = LoggerFactory.getLogger(getClass());//提供日志類
@Autowired
private JavaMailSenderImpl mailSender;//注入郵件工具類
/**
* 發送郵件
*/
public MailVo sendMail(MailVo mailVo) {
try {
checkMail(mailVo); //1.檢測郵件
sendMimeMail(mailVo); //2.發送郵件
return saveMail(mailVo); //3.保存郵件
} catch (Exception e) {
logger.error("發送郵件失敗:", e);//打印錯誤信息
mailVo.setStatus("fail");
mailVo.setError(e.getMessage());
return mailVo;
}
}
//檢測郵件信息類
private void checkMail(MailVo mailVo) {
if (StringUtils.isEmpty(mailVo.getTo())) {
throw new RuntimeException("郵件收信人不能為空");
}
if (StringUtils.isEmpty(mailVo.getSubject())) {
throw new RuntimeException("郵件主題不能為空");
}
if (StringUtils.isEmpty(mailVo.getText())) {
throw new RuntimeException("郵件內容不能為空");
}
}
//構建復雜郵件信息類
private void sendMimeMail(MailVo mailVo) {
try {
MimeMessageHelper messageHelper = new MimeMessageHelper(mailSender.createMimeMessage(), true);//true表示支持復雜類型
mailVo.setFrom(getMailSendFrom());//郵件發信人從配置項讀取
messageHelper.setFrom(mailVo.getFrom());//郵件發信人
messageHelper.setTo(mailVo.getTo().split(","));//郵件收信人
messageHelper.setSubject(mailVo.getSubject());//郵件主題
messageHelper.setText(mailVo.getText());//郵件內容
if (!StringUtils.isEmpty(mailVo.getCc())) {//抄送
messageHelper.setCc(mailVo.getCc().split(","));
}
if (!StringUtils.isEmpty(mailVo.getBcc())) {//密送
messageHelper.setCc(mailVo.getBcc().split(","));
}
if (mailVo.getMultipartFiles() != null) {//添加郵件附件
for (MultipartFile multipartFile : mailVo.getMultipartFiles()) {
messageHelper.addAttachment(multipartFile.getOriginalFilename(), multipartFile);
}
}
if (StringUtils.isEmpty(mailVo.getSentDate())) {//發送時間
mailVo.setSentDate(new Date());
messageHelper.setSentDate(mailVo.getSentDate());
}
mailSender.send(messageHelper.getMimeMessage());//正式發送郵件
mailVo.setStatus("ok");
logger.info("發送郵件成功:{}->{}", mailVo.getFrom(), mailVo.getTo());
} catch (Exception e) {
throw new RuntimeException(e);//發送失敗
}
}
//保存郵件
private MailVo saveMail(MailVo mailVo) {
//將郵件保存到數據庫..
return mailVo;
}
//獲取郵件發信人
public String getMailSendFrom() {
return mailSender.getJavaMailProperties().getProperty("from");
}
}
搞定了發送郵件最核心的業務邏輯,接下來咱們寫一個簡單頁面用來發送郵件。
首先寫好跟頁面交互的控制器 MailController,具體源碼如下:
@RestController
public class MailController {
@Autowired
private MailService mailService;
/**
* 發送郵件的主界面
*/
@GetMapping("/")
public ModelAndView index() {
ModelAndView mv = new ModelAndView("mail/sendMail");//打開發送郵件的頁面
mv.addObject("from", mailService.getMailSendFrom());//郵件發信人
return mv;
}
/**
* 發送郵件
*/
@PostMapping("/mail/send")
public MailVo sendMail(MailVo mailVo, MultipartFile[] files) {
mailVo.setMultipartFiles(files);
return mailService.sendMail(mailVo);//發送郵件和附件
}
}
然后在/resources/views/mail目錄新建sendMail.html,具體源碼如下:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>發送郵件</title>
<link th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" rel="stylesheet" type="text/css"/>
<script th:src="@{/webjars/jquery/jquery.min.js}"></script>
<script th:href="@{/webjars/bootstrap/js/bootstrap.min.js}"></script>
</head>
<body>
<div class="col-md-6" style="margin:20px;padding:20px;border: #E0E0E0 1px solid;">
<marquee behavior="alternate" onfinish="alert(12)" id="mq"
onMouseOut="this.start();$('#egg').text('嗯 真聽話!');"
onMouseOver="this.stop();$('#egg').text('有本事放開我呀!');">
<h5 id="egg">祝大家新年快樂!</h5><img id="doge" src="http://pics.sc.chinaz.com/Files/pic/faces/3709/7.gif" alt="">
</marquee>
<form class="form-horizontal" id="mailForm">
<div class="form-group">
<label class="col-md-2 control-label">郵件發信人:</label>
<div class="col-md-6">
<input class="form-control" id="from" name="from" th:value="${from}" readonly="readonly">
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">郵件收信人:</label>
<div class="col-md-6">
<input class="form-control" id="to" name="to" title="多個郵箱使用,隔開">
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">郵件主題:</label>
<div class="col-md-6">
<input class="form-control" id="subject" name="subject">
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">郵件內容:</label>
<div class="col-md-6">
<textarea class="form-control" id="text" name="text" rows="5"></textarea>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">郵件附件:</label>
<div class="col-md-6">
<input class="form-control" id="files" name="files" type="file" multiple="multiple">
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">郵件操作:</label>
<div class="col-md-3">
<a class="form-control btn btn-primary" onclick="sendMail()">發送郵件</a>
</div>
<div class="col-md-3">
<a class="form-control btn btn-default" onclick="clearForm()">清空</a>
</div>
</div>
</form>
<script th:inline="javascript">
var appCtx = [[${#request.getContextPath()}]];
function sendMail() {
var formData = new FormData($('#mailForm')[0]);
$.ajax({
url: appCtx + '/mail/send',
type: "POST",
data: formData,
contentType: false,
processData: false,
success: function (result) {
alert(result.status === 'ok' ? "發送成功!" : "你被Doge嘲諷了:" + result.error);
},
error: function () {
alert("發送失敗!");
}
});
}
function clearForm() {
$('#mailForm')[0].reset();
}
setInterval(function () {
var total = $('#mq').width();
var width = $('#doge').width();
var left = $('#doge').offset().left;
if (left <= width / 2 + 20) {
$('#doge').css('transform', 'rotateY(180deg)')
}
if (left >= total - width / 2 - 40) {
$('#doge').css('transform', 'rotateY(-360deg)')
}
});
</script>
</div>
</body>
</html>
如果是初學者,建議大家先下載源碼,修改配置后運行工程,成功后再自己重新寫一遍代碼,這樣有助于加深記憶。
啟動工程并訪問:http://localhost:8080 然后可以看到發送郵件的主界面如下:
然后填寫你的小號郵箱,點擊發送郵件,若成功則可以登陸小號郵箱查看郵件和剛才上傳的附件。
至此發送郵件代碼全部完成,歡迎大家下載并關注Github 源碼。
如果企業定制了郵件服務器,自然會記錄郵件日志,根據錯誤編碼存儲日志有利于日常維護。
例如這些由網易郵箱提供的錯誤編碼標識:
質文章,及時送達
鏈接 | www.jianshu.com/p/5eb000544dd7
SMTP全稱為Simple Mail Transfer Protocol(簡單郵件傳輸協議),它是一組用于從源地址到目的地址傳輸郵件的規范,通過它來控制郵件的中轉方式。SMTP認證要求必須提供賬號和密碼才能登陸服務器,其設計目的在于避免用戶受到垃圾郵件的侵擾。
IMAP全稱為Internet Message Access Protocol(互聯網郵件訪問協議),IMAP允許從郵件服務器上獲取郵件的信息、下載郵件等。IMAP與POP類似,都是一種郵件獲取協議。
POP3全稱為Post Office Protocol 3(郵局協議),POP3支持客戶端遠程管理服務器端的郵件。POP3常用于“離線”郵件處理,即允許客戶端下載服務器郵件,然后服務器上的郵件將會被刪除。目前很多POP3的郵件服務器只提供下載郵件功能,服務器本身并不刪除郵件,這種屬于改進版的POP3協議。
兩者最大的區別在于,IMAP允許雙向通信,即在客戶端的操作會反饋到服務器上,例如在客戶端收取郵件、標記已讀等操作,服務器會跟著同步這些操作。而對于POP協議雖然也允許客戶端下載服務器郵件,但是在客戶端的操作并不會同步到服務器上面的,例如在客戶端收取或標記已讀郵件,服務器不會同步這些操作。
什么是JavaMailSender和JavaMailSenderImpl?
JavaMailSender和JavaMailSenderImpl 是Spring官方提供的集成郵件服務的接口和實現類,以簡單高效的設計著稱,目前是Java后端發送郵件和集成郵件服務的主流工具。
非常簡單,直接在業務類注入JavaMailSenderImpl并調用send方法發送郵件。其中簡單郵件可以通過SimpleMailMessage來發送郵件,而復雜的郵件(例如添加附件)可以借助MimeMessageHelper來構建MimeMessage發送郵件。例如:
@Autowired
private JavaMailSenderImpl mailSender;
public void sendMail throws MessagingException {
//簡單郵件
SimpleMailMessage simpleMailMessage=new SimpleMailMessage;
simpleMailMessage.setFrom("admin@163.com");
simpleMailMessage.setTo("socks@qq.com");
simpleMailMessage.setSubject("Happy New Year");
simpleMailMessage.setText("新年快樂!");
mailSender.send(simpleMailMessage);
//復雜郵件
MimeMessage mimeMessage=mailSender.createMimeMessage;
MimeMessageHelper messageHelper=new MimeMessageHelper(mimeMessage);
messageHelper.setFrom("admin@163.com");
messageHelper.setTo("socks@qq.com");
messageHelper.setSubject("Happy New Year");
messageHelper.setText("新年快樂!");
messageHelper.addInline("doge.gif", new File("xx/xx/doge.gif"));
messageHelper.addAttachment("work.docx", new File("xx/xx/work.docx"));
mailSender.send(mimeMessage);
}
所謂開箱即用其實就是基于官方內置的自動配置,翻看源碼可知曉郵件自動配置類(MailSenderPropertiesConfiguration) 為上下文提供了郵件服務實例(JavaMailSenderImpl)。具體源碼如下:
@Configuration
@ConditionalOnProperty(prefix="spring.mail", name="host")
classMailSenderPropertiesConfiguration{
private final MailProperties properties;
MailSenderPropertiesConfiguration(MailProperties properties) {
this.properties=properties;
}
@Bean
@ConditionalOnMissingBean
public JavaMailSenderImpl mailSender {
JavaMailSenderImpl sender=new JavaMailSenderImpl;
applyProperties(sender);
return sender;
}
其中MailProperties是關于郵件服務器的配置信息,具體源碼如下:
@ConfigurationProperties(prefix="spring.mail")
public class MailProperties {
private static final Charset DEFAULT_CHARSET=StandardCharsets.UTF_8;
private String host;
private Integer port;
private String username;
private String password;
private String protocol="smtp";
private Charset defaultEncoding=DEFAULT_CHARSET;
private Map<String, String> properties=new HashMap<>;
}
登陸網易郵箱163,在設置中打開并勾選POP3/SMTP/IMAP服務,然后會得到一個授權碼,這個郵箱和授權碼將用作登陸認證。
首先咱們通過 Spring Initializr 創建工程springboot-send-mail,如圖所示:
然后在pom.xml 引入web、thymeleaf 和spring-boot-starter-mail等相關依賴。例如:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator-core</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.7</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
根據前面提到的配置項(MailProperties)填寫相關配置信息,其中spring.mail.username 表示連接郵件服務器時認證的登陸賬號,可以是普通的手機號或者登陸賬號,并非一定是郵箱,為了解決這個問題,推薦大家在spring.mail. properties.from填寫郵件發信人即真實郵箱。
Tips:關注微信公眾號:Java后端,每日提送技術博文。
然后在application.yml添加如下配置:
spring:
mail:
host: smtp.163.com #SMTP服務器地址
username: socks #登陸賬號
password: 123456 #登陸密碼(或授權碼)
properties:
from: socks@163.com #郵件發信人(即真實郵箱)
thymeleaf:
cache: false
prefix: classpath:/views/
servlet:
multipart:
max-file-size: 10MB #限制單個文件大小
max-request-size: 50MB #限制請求總量
透過前面的進階知識,我們知道在發送郵件前,需要先構建 SimpleMailMessage或 MimeMessage 郵件信息類來填寫郵件標題、郵件內容等信息,最后提交給JavaMailSenderImpl發送郵件,這樣看起來沒什么問題,也能實現既定目標,但在實際使用中會出現大量零散和重復的代碼,還不便于保存郵件到數據庫。
那么優雅的發送郵件應該是如何的呢?應該屏蔽掉這些構建信息和發送郵件的細節,不管是簡單還是復雜郵件,都可以通過統一的API來發送郵件。例如:mailService.send(mailVo) 。
例如通過郵件信息類(MailVo) 來保存發送郵件時的郵件主題、郵件內容等信息 :
package com.hehe.vo;
public class MailVo {
private String id;//郵件id
private String from;//郵件發送人
private String to;//郵件接收人(多個郵箱則用逗號","隔開)
private String subject;//郵件主題
private String text;//郵件內容
private Date sentDate;//發送時間
private String cc;//抄送(多個郵箱則用逗號","隔開)
private String bcc;//密送(多個郵箱則用逗號","隔開)
private String status;//狀態
private String error;//報錯信息
@JsonIgnore
private MultipartFile multipartFiles;//郵件附件
//省略GET&SET方法
}
除了發送郵件之外,還包括檢測郵件和保存郵件等操作,例如:
檢測郵件 checkMail; 首先校驗郵件收信人、郵件主題和郵件內容這些必填項,若為空則拒絕發送。
發送郵件 sendMimeMail; 其次通過MimeMessageHelper來解析MailVo并構建MimeMessage傳輸郵件。
保存郵件 sendMimeMail; 最后將郵件保存到數據庫,便于統計和追查郵件問題。
本案例郵件業務類 MailService 的具體源碼如下:
package com.hehe.service;
/**
* 郵件業務類 MailService
*/
@Service
publicclassMailService{
private Logger logger=LoggerFactory.getLogger(getClass);//提供日志類
@Autowired
private JavaMailSenderImpl mailSender;//注入郵件工具類
/**
* 發送郵件
*/
public MailVo sendMail(MailVo mailVo) {
try {
checkMail(mailVo); //1.檢測郵件
sendMimeMail(mailVo); //2.發送郵件
return saveMail(mailVo); //3.保存郵件
} catch (Exception e) {
logger.error("發送郵件失敗:", e);//打印錯誤信息
mailVo.setStatus("fail");
mailVo.setError(e.getMessage);
return mailVo;
}
}
//檢測郵件信息類
privatevoidcheckMail(MailVo mailVo) {
if (StringUtils.isEmpty(mailVo.getTo)) {
throw new RuntimeException("郵件收信人不能為空");
}
if (StringUtils.isEmpty(mailVo.getSubject)) {
throw new RuntimeException("郵件主題不能為空");
}
if (StringUtils.isEmpty(mailVo.getText)) {
throw new RuntimeException("郵件內容不能為空");
}
}
//構建復雜郵件信息類
privatevoidsendMimeMail(MailVo mailVo) {
try {
MimeMessageHelper messageHelper=new MimeMessageHelper(mailSender.createMimeMessage, true);//true表示支持復雜類型
mailVo.setFrom(getMailSendFrom);//郵件發信人從配置項讀取
messageHelper.setFrom(mailVo.getFrom);//郵件發信人
messageHelper.setTo(mailVo.getTo.split(","));//郵件收信人
messageHelper.setSubject(mailVo.getSubject);//郵件主題
messageHelper.setText(mailVo.getText);//郵件內容
if (!StringUtils.isEmpty(mailVo.getCc)) {//抄送
messageHelper.setCc(mailVo.getCc.split(","));
}
if (!StringUtils.isEmpty(mailVo.getBcc)) {//密送
messageHelper.setCc(mailVo.getBcc.split(","));
}
if (mailVo.getMultipartFiles !=) {//添加郵件附件
for (MultipartFile multipartFile : mailVo.getMultipartFiles) {
messageHelper.addAttachment(multipartFile.getOriginalFilename, multipartFile);
}
}
if (StringUtils.isEmpty(mailVo.getSentDate)) {//發送時間
mailVo.setSentDate(new Date);
messageHelper.setSentDate(mailVo.getSentDate);
}
mailSender.send(messageHelper.getMimeMessage);//正式發送郵件
mailVo.setStatus("ok");
logger.info("發送郵件成功:{}->{}", mailVo.getFrom, mailVo.getTo);
} catch (Exception e) {
throw new RuntimeException(e);//發送失敗
}
}
//保存郵件
private MailVo saveMail(MailVo mailVo) {
//將郵件保存到數據庫..
return mailVo;
}
//獲取郵件發信人
public String getMailSendFrom {
return mailSender.getJavaMailProperties.getProperty("from");
}
}
搞定了發送郵件最核心的業務邏輯,接下來咱們寫一個簡單頁面用來發送郵件。
首先寫好跟頁面交互的控制器 MailController,具體源碼如下:
@RestController
publicclassMailController{
@Autowired
private MailService mailService;
/**
* 發送郵件的主界面
*/
@GetMapping("/")
public ModelAndView index {
ModelAndView mv=new ModelAndView("mail/sendMail");//打開發送郵件的頁面
mv.addObject("from", mailService.getMailSendFrom);//郵件發信人
return mv;
}
/**
* 發送郵件
*/
@PostMapping("/mail/send")
public MailVo sendMail(MailVo mailVo, MultipartFile[] files) {
mailVo.setMultipartFiles(files);
return mailService.sendMail(mailVo);//發送郵件和附件
}
}
然后在/resources/views/mail目錄新建sendMail.html,具體源碼如下:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>發送郵件</title>
<link th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" rel="stylesheet" type="text/css"/>
<script th:src="@{/webjars/jquery/jquery.min.js}"></script>
<script th:href="@{/webjars/bootstrap/js/bootstrap.min.js}"></script>
</head>
<body>
<div class="col-md-6" style="margin:20px;padding:20px;border: #E0E0E0 1px solid;">
<marquee behavior="alternate" onfinish="alert(12)" id="mq"
onMouseOut="this.start;$('#egg').text('嗯 真聽話!');"
onMouseOver="this.stop;$('#egg').text('有本事放開我呀!');">
<h5 id="egg">祝大家新年快樂!</h5><img id="doge" src="http://pics.sc.chinaz.com/Files/pic/faces/3709/7.gif" alt="">
</marquee>
<form class="form-horizontal" id="mailForm">
<div class="form-group">
<label class="col-md-2 control-label">郵件發信人:</label>
<div class="col-md-6">
<input class="form-control" id="from" name="from" th:value="${from}" readonly="readonly">
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">郵件收信人:</label>
<div class="col-md-6">
<input class="form-control" id="to" name="to" title="多個郵箱使用,隔開">
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">郵件主題:</label>
<div class="col-md-6">
<input class="form-control" id="subject" name="subject">
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">郵件內容:</label>
<div class="col-md-6">
<textarea class="form-control" id="text" name="text" rows="5"></textarea>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">郵件附件:</label>
<div class="col-md-6">
<input class="form-control" id="files" name="files" type="file" multiple="multiple">
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">郵件操作:</label>
<div class="col-md-3">
<a class="form-control btn btn-primary" onclick="sendMail">發送郵件</a>
</div>
<div class="col-md-3">
<a class="form-control btn btn-default" onclick="clearForm">清空</a>
</div>
</div>
</form>
<script th:inline="javascript">
var appCtx=[[${#request.getContextPath()}]];
function sendMail {
var formData=new FormData($('#mailForm')[0]);
$.ajax({
url: appCtx + '/mail/send',
type: "POST",
data: formData,
contentType: false,
processData: false,
success: function (result) {
alert(result.status==='ok' ? "發送成功!" : "你被Doge嘲諷了:" + result.error);
},
error: function {
alert("發送失敗!");
}
});
}
function clearForm {
$('#mailForm')[0].reset;
}
setInterval(function {
var total=$('#mq').width;
var width=$('#doge').width;
var left=$('#doge').offset.left;
if (left <=width / 2 + 20) {
$('#doge').css('transform', 'rotateY(180deg)')
}
if (left >=total - width / 2 - 40) {
$('#doge').css('transform', 'rotateY(-360deg)')
}
});
</script>
</div>
</body>
</html>
如果是初學者,建議大家先下載源碼,修改配置后運行工程,成功后再自己重新寫一遍代碼,這樣有助于加深記憶。
啟動工程并訪問:http://localhost:8080 然后可以看到發送郵件的主界面如下:
然后填寫你的小號郵箱,點擊發送郵件,若成功則可以登陸小號郵箱查看郵件和剛才上傳的附件。
至此發送郵件代碼全部完成,歡迎大家下載并關注Github 源碼。
如果企業定制了郵件服務器,自然會記錄郵件日志,根據錯誤編碼存儲日志有利于日常維護。
例如這些由網易郵箱提供的錯誤編碼標識:
421 HL:REP 該IP發送行為異常,存在接收者大量不存在情況,被臨時禁止連接。請檢查是否有用戶發送病毒或者垃圾郵件,并核對發送列表有效性;
421 HL:ICC 該IP同時并發連接數過大,超過了網易的限制,被臨時禁止連接。請檢查是否有用戶發送病毒或者垃圾郵件,并降低IP并發連接數量;
421 HL:IFC 該IP短期內發送了大量信件,超過了網易的限制,被臨時禁止連接。請檢查是否有用戶發送病毒或者垃圾郵件,并降低發送頻率;
421 HL:MEP 該IP發送行為異常,存在大量偽造發送域域名行為,被臨時禁止連接。請檢查是否有用戶發送病毒或者垃圾郵件,并使用真實有效的域名發送;
450 MI:CEL 發送方出現過多的錯誤指令。請檢查發信程序;
450 MI:DMC 當前連接發送的郵件數量超出限制。請減少每次連接中投遞的郵件數量;
450 MI:CCL 發送方發送超出正常的指令數量。請檢查發信程序;
450 RP:DRC 當前連接發送的收件人數量超出限制。請控制每次連接投遞的郵件數量;
450 RP:CCL 發送方發送超出正常的指令數量。請檢查發信程序;
450 DT:RBL 發信IP位于一個或多個RBL里。請參考http://www.rbls.org/關于RBL的相關信息;
450 WM:BLI 該IP不在網易允許的發送地址列表里;
450 WM:BLU 此用戶不在網易允許的發信用戶列表里;
451 DT:SPM ,please try again 郵件正文帶有垃圾郵件特征或發送環境缺乏規范性,被臨時拒收。請保持郵件隊列,兩分鐘后重投郵件。需調整郵件內容或優化發送環境;
451 Requested mail action not taken: too much fail authentication 登錄失敗次數過多,被臨時禁止登錄。請檢查密碼與帳號驗證設置;
451 RP:CEL 發送方出現過多的錯誤指令。請檢查發信程序;
451 MI:DMC 當前連接發送的郵件數量超出限制。請控制每次連接中投遞的郵件數量;
451 MI:SFQ 發信人在15分鐘內的發信數量超過限制,請控制發信頻率;
451 RP:QRC 發信方短期內累計的收件人數量超過限制,該發件人被臨時禁止發信。請降低該用戶發信頻率;
451 Requested action aborted: local error in processing 系統暫時出現故障,請稍后再次嘗試發送;
500 Error: bad syntaxU 發送的smtp命令語法有誤;
550 MI:NHD HELO命令不允許為空;
550 MI:IMF 發信人電子郵件地址不合規范。請參考http://www.rfc-editor.org/關于電子郵件規范的定義;
550 MI:SPF 發信IP未被發送域的SPF許可。請參考http://www.openspf.org/關于SPF規范的定義;
550 MI:DMA 該郵件未被發信域的DMARC許可。請參考http://dmarc.org/關于DMARC規范的定義;
550 MI:STC 發件人當天的連接數量超出了限定數量,當天不再接受該發件人的郵件。請控制連接次數;
550 RP:FRL 網易郵箱不開放匿名轉發(Open relay);
550 RP:RCL 群發收件人數量超過了限額,請減少每封郵件的收件人數量;
550 RP:TRC 發件人當天內累計的收件人數量超過限制,當天不再接受該發件人的郵件。請降低該用戶發信頻率;
550 DT:SPM 郵件正文帶有很多垃圾郵件特征或發送環境缺乏規范性。需調整郵件內容或優化發送環境;
550 Invalid User 請求的用戶不存在;
550 User in blacklist 該用戶不被允許給網易用戶發信;
550 User suspended 請求的用戶處于禁用或者凍結狀態;
550 Requested mail action not taken: too much recipient 群發數量超過了限額;
552 Illegal Attachment 不允許發送該類型的附件,包括以.uu .pif .scr .mim .hqx .bhx .cmd .vbs .bat .com .vbe .vb .js .wsh等結尾的附件;
552 Requested mail action aborted: exceeded mailsize limit 發送的信件大小超過了網易郵箱允許接收的最大限制;
553 Requested action not taken: sender is not allowed 不允許發件人為空,請使用真實發件人發送;
553 Requested action not taken: Local user only SMTP類型的機器只允許發信人是本站用戶;
553 Requested action not taken: no smtp MX only MX類型的機器不允許發信人是本站用戶;
553 authentication is required SMTP需要身份驗證,請檢查客戶端設置;
554 DT:SPM 發送的郵件內容包含了未被許可的信息,或被系統識別為垃圾郵件。請檢查是否有用戶發送病毒或者垃圾郵件;
554 DT:SUM 信封發件人和信頭發件人不匹配;
554 IP is rejected, smtp auth error limit exceed 該IP驗證失敗次數過多,被臨時禁止連接。請檢查驗證信息設置;
554 HL:IHU 發信IP因發送垃圾郵件或存在異常的連接行為,被暫時掛起。請檢測發信IP在歷史上的發信情況和發信程序是否存在異常;
554 HL:IPB 該IP不在網易允許的發送地址列表里;
554 MI:STC 發件人當天內累計郵件數量超過限制,當天不再接受該發件人的投信。請降低發信頻率;
554 MI:SPB 此用戶不在網易允許的發信用戶列表里;
554 IP in blacklist 該IP不在網易允許的發送地址列表里。
【END】
Low了,網頁居然還用這么老掉牙的特效!弄個粗體字,下劃線就是突出重點了?真是一股濃濃的鄉村風撲面而來;弄個圖在頁面飄來飄去就是動畫效果了,你要這么認為的話那我只能保持沉默了。在HTML5占領著整個互聯網之時,想透過網頁抓住所有人的眼球,因循守舊是行不通的。因此,有你不能不知道的HTML常用代碼。只有熟悉掌握了常用的HTML代碼,你才能在編寫網頁的時候做到行云流水,用處處流露著細膩和創意的動效細節打動所有人。
在這里還是要說一下我自己建的前端JavaScript學習交流群:574462090,不管你是小白還是大牛,小編我都挺歡迎,小白嘛,多問點問題也就學好了,不定期分享干貨,包括最新的2017年前端學習資料,歡迎初學和進階中的小伙伴。
html代碼大全:結構性定義
文件類型<HTML></HTML> (放在檔案的開頭與結尾)
文件主題<TITLE></TITLE> (必須放在「文頭」區塊內)
文頭<HEAD></HEAD> (描述性資料,像是「主題」)
文體<BODY></BODY> (文件本體)
標題<H?></H?> (從1到6,有六層選擇)
標題的對齊 <H?ALIGN=LEFT|CENTER|RIGHT></H?>
區分<DIV></DIV>
區分的對齊 <DIVALIGN=LEFT|RIGHT|CENTER|JUSTIFY></DIV>
引文區塊<BLOCKQUOTE></BLOCKQUOTE> (通常會內縮)
強調<EM></EM> (通常會以斜體顯示)
特別強調<STRONG></STRONG> (通常會以加粗顯示)
引文<CITE></CITE> (通常會以斜體顯示)
碼<CODE></CODE> (顯示原始碼之用)
樣本<SAMP></SAMP>
鍵盤輸入<KBD></KBD>
變數<VAR></VAR>
定義<DFN></DFN> (有些瀏覽器不提供)
地址 <ADDRESS></ADDRESS>
大字<BIG></BIG>
小字<SMALL></SMALL>
與外觀相關的標簽(作者自訂的表現方式)
加粗<B></B>
斜體<I></I>
底線<U></U> (尚有些瀏覽器不提供)
刪除線<S></S> (尚有些瀏覽器不提供)
下標<SUB></SUB>
上標<SUP></SUP>
打字機體<TT></TT> (用單空格字型顯示)
預定格式<PRE></PRE> (保留文件中空格的大小)
預定格式的寬度<PRE WIDTH=?></PRE>(以字元計算)
向中看齊<CENTER></CENTER> (文字與圖片都可以)
閃耀<BLINK></BLINK> (有史以來最被嘲弄的標簽)
字體大小 <FONTSIZE=?></FONT>(從1到7)
改變字體大小 <FONTSIZE=+|-?></FONT>
基本字體大小 <BASEFONTSIZE=?> (從1到7; 內定為3)
字體顏色 <FONTCOLOR="#$$"></FONT>($$為顏色代碼)
2
html代碼大全:修改頁面的實用性HTML代碼
1)貼圖:<img src="圖片地址">
2)加入連接:<a href="所要連接的相關地址">寫上你想寫的字</a>
3)在新窗口打開連接:<a href="相關地址" target="_blank">寫上要寫的字</a>
4)移動字體(走馬燈):<marquee>寫上你想寫的字</marquee>
5)字體加粗:<b>寫上你想寫的字</b>
6)字體斜體:<i>寫上你想寫的字</i>
7)字體下劃線: <u>寫上你想寫的字</u>
8)字體刪除線: <s>寫上你想寫的字</s>
9)字體加大: <big>寫上你想寫的字</big>
10)字體控制大小:<h1>寫上你想寫的字</h1> (其中字體大小可從h1-h5,h1最大,h5最小)
11)更改字體顏色:<font color="#value">寫上你想寫的字</font>(其中value值在000000與ffffff(16位進制)之間
12)消除連接的下劃線:<a href="相關地址" style="text-decoration:none">寫上你想寫的字</a>
13)貼音樂:<embed src="音樂地址" width="寬度" height="高度" autostart=false>
14)貼flash: <embed src="flash地址" width="寬度" height="高度">
15)貼影視文件:<img dynsrc="文件地址" width="寬度" height="高度" start=mouseover>
16)換行:<br>
17)段落:<p>段落</p>
18)原始文字樣式:<pre>正文</pre>
19)換帖子背景:<body background="背景圖片地址">
20)固定帖子背景不隨滾動條滾動:<body background="背景圖片地址" body
bgproperties=fixed>
21)定制帖子背景顏色:<body bgcolor="#value">(value值見10)
22)帖子背景音樂:<bgsound="背景音樂地址" loop=infinite>
23)貼網頁:<iframe. src="相關地址" width="寬度" height="高度"></iframe>
3
html代碼大全:常常會遇到的問題
點擊關閉窗口
<a href="javascript.:top.window.close();">點擊關閉窗口</a>!
請問如何去掉主頁右面的滾動條?
<body scroll="no">
<body style="overflow-y:hidden">
如何做到讓一個網頁自動關閉.
<html>
<head>
<OBJECT id=closes type="application/x-oleobject" classid="clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11">
<param name="Command" value="Close">
</object>
</head>
<body >
這個窗口會在10秒過后自動關閉,而且不會出現提示. </body>
如何在不刷新頁面的情況下刷新css?
<style>
button{ color:#000000;}
</style>
<button nclick=document.styleSheets[0].rules[0].style.color=‘‘‘‘red‘‘‘‘>點擊按鈕直接修改style標簽里button選擇符使按鈕改為 紅色</button>
請問如何讓網頁自動刷新?
在head部記入<META. HTTP-EQUIV="Refresh" c>其中20為20秒后自動刷新,你可以更改為任意值。
如何讓頁面自動刷新?
方法一,用refresh
HTML 代碼片段如下:
<head>
<meta. http-equiv="refresh" c>
</head>
5表示刷新時間
[Ctrl+A 全部選擇 提示:你可先修改部分代碼,再按運行]
方法二,使用setTimeout控制
<img src=/logo.gif>
<script>
function rl(){
document.location.reload()
}
setTimeout(rl,2000)
</SCRIPT>
如何讓超鏈接沒有下劃線
在源代碼中的<HEAD>…</HEAD>之間輸入如下代碼:
<style. type="text/css"> <!--
a { text-decoration: none}
--> </style>
請問如何去掉IE的上下滾動條?
<body style=‘‘‘‘overflow:scroll;overflow-y:hidden‘‘‘‘>
</body>
怎樣才能把RealPlayer文件在網頁做一個試聽連接?
<embed height=25 src=51js.rm type=audio/x-pn-realaudio-plugin width=50 autostart="false" c>
如何用html實現瀏覽器上后退按鈕的功能?
<a href="java script.:history.go(-1)">點擊后退</a>
或者
<script> history.back() </SCRIPT>
4
html代碼大全:你不一定知道的技巧
16. ncontextmenu="window.event.returnValue=false" 將徹底屏蔽鼠標右鍵
<table borderncontextmenu=return(false)><td>no</table> 可用于Table
17. <body nselectstart="return false"> 取消選取、防止復制
18.onpaste="return false" 不準粘貼
19.oncopy="return false;" ncut="return false;" 防止復制
20. <link rel="Shortcut Icon"href="favicon.ico"> IE地址欄前換成自己的圖標
21. <link rel="Bookmark"href="favicon.ico"> 可以在收藏夾中顯示出你的圖標
22. <inputstyle="ime-mode:disabled"> 關閉輸入法
23. 永遠都會帶著框架
<script. language="JavaScript"><!--
if (window==top)top.location.href="frames.htm"; //frames.htm為框架網頁
// --></script>
24. 防止被人frame.
<SCRIPT. LANGUAGE=JAVASCRIPT><!--
if (top.location !=self.location)top.location=self.location;
// --></SCRIPT>
25. 網頁將不能被另存為
<noscript><iframe.src=*.html></iframe></noscript>
26. 查看網頁源代碼
<input type=button value=查看網頁源代碼
onclick="window.location="view-source:"+"http://www.pconline.com.cn"">
27.刪除時確認
<a href="javascript:if(confirm("確實要刪除嗎?"))location="boos.asp? &areyou=刪除&page=1"">刪除</a>
28.屏蔽功能鍵Shift,Alt,Ctrl
<script>
function look(){
if(event.shiftKey)
alert("禁止按Shift鍵!");//可以換成ALT CTRL
}
document.onkeydown=look;
</script>
29. 網頁不會被緩存
<META. HTTP-EQUIV="pragma" CONTENT="no-cache">
<META. HTTP-EQUIV="Cache-Control"CONTENT="no-cache, must-revalidate">
<META. HTTP-EQUIV="expires"CONTENT="Wed, 26 Feb 1997 08:21:57 GMT">
或者<META. HTTP-EQUIV="expires"CONTENT="0">
30.怎樣讓表單沒有凹凸感?
<input type=text style="border:1 solid #000000">
或 <input type=text style="border-left:none;border-right:none; border -top:none; border-bottom: 1 solid#000000"></textarea>
31.不要滾動條?
讓豎條沒有:
<body style="overflow:scroll;overflow-y:hidden">
</body>
讓橫條沒有:
<body style="overflow:scroll;overflow-x:hidden">
</body>
兩個都去掉?更簡單了
<body scroll="no">
</body>
32.怎樣去掉圖片鏈接點擊后,圖片周圍的虛線?
<a href="#"nFocus="this.blur()"><img src="logo.jpg"border=0></a>
33.電子郵件處理提交表單
<form. name="form1"method="post" action="mailt****@***.com"enctype="text/plain">
<input type=submit>
</form>
34.在打開的子窗口刷新父窗口的代碼里如何寫?
window.opener.location.reload()
35.如何設定打開頁面的大小
<body nload="top.resizeTo(300,200);">
打開頁面的位置<bodynload="top.moveBy(300,200);">
36.在頁面中如何加入不是滿鋪的背景圖片,拉動頁面時背景圖不動
<STYLE>
body
{background-image:url(logo.gif); background-repeat:no-repeat;
background-position:center;background-attachment: fixed}
</STYLE>
37. 檢查一段字符串是否全由數字組成
<script. language="Javascript"><!--
function checkNum(str){return str.match(//D/)==null}
alert(checkNum("1232142141"))
alert(checkNum("123214214a1"))
// --></script>
38. 獲得一個窗口的大小
document.body.clientWidth; document.body.clientHeight
39. 怎么判斷是否是字符
if (/[^/x00-/xff]/g.test(s)) alert("含有漢字");
else alert("全是字符");
40.TEXTAREA自適應文字行數的多少
<textarea rows=1 name=s1 cols=27npropertychange="this.style.posHeight=this.scrollHeight">
</textarea>
41. 日期減去天數等于第二個日期
<script. language=Javascript>
function cc(dd,dadd)
{
//可以加上錯誤處理
var a=new Date(dd)
a=a.valueOf()
a=a - dadd * 24 * 60 * 60 * 1000
a=new Date(a)
alert(a.getFullYear() + "年" + (a.getMonth() +1) + "月" + a.getDate() + "日")
} cc("12/23/2002",2)
</script>
42. 選擇了哪一個Radio
<HTML><script. language="vbscript">
function checkme()
for each ob in radio1
if ob.checked then window.alert ob.value
next
end function
</script><BODY>
<INPUT name="radio1" type="radio"value="style" checked>Style.
<INPUT name="radio1" type="radio"value="barcode">Barcode
<INPUT type="button" value="check"nclick="checkme()">
</BODY></HTML>
43.腳本永不出錯
<SCRIPT. LANGUAGE="JavaScript">
<!-- Hide function killErrors(){return true;} window.onerror=killErrors;// -->
</SCRIPT>
44.ENTER鍵可以讓光標移到下一個輸入框
<input nkeydown="if(event.keyCode==13)event.keyCode=9">
文末再提兩點建議吧:
剛學前端JavaScript的時候,一定不要追求速成,編程不比其他傳統行業,可以快速的上手,他需要一定的代碼量積累起來,不然你追求速成很可能會車毀人亡。
這個文章到這里就說完了,想要前端學習資料的進我的群自助領取,已經上傳到群文件里了:574462090,歡迎初學和進階中的小伙伴。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。