定時發送報表統計是公司前段時間的一個小需求,謹此記錄一下。
一、hutool郵件工具-MailUtil
hutool就不過多解釋了,感謝這個開源項目的貢獻者,讓平時開發省了不少力,用了才發現之前自己封裝的小工具類挺雞肋...
官方介紹:
Hutool是一個小而全的Java工具類庫,通過靜態方法封裝,降低相關API的學習成本,提高工作效率,使Java擁有函數式語言般的優雅,讓Java語言也可以“甜甜的”。
沒用過的可以有時間看看,很nice。
這裏把hutool的郵件工具-MailUtil的使用拿過來說明一下。
1.引入依賴
Hutool對所有第三方都是可選依賴,因此在使用MailUtil時需要自行引入第三方依賴。
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.7</version>
</dependency>
2.郵件服務器配置
在classpath(在標準Maven項目中爲src/main/resources)的config目錄下新建mail.setting文件,最小配置內容如下,在此配置下,smtp服務器和用戶名都將通過from參數識別:
# 發件人(必須正確,否則發送失敗)
from = [email protected]
# 密碼(注意,某些郵箱需要爲SMTP服務單獨設置密碼,詳情查看相關幫助)
pass = q1w2e3
but,有時候一些非標準郵箱服務器(例如企業郵箱服務器)的smtp地址等信息並不與發件人後綴一致,端口也可能不同,此時Hutool可以提供完整的配置文件:
完整配置
# 郵件服務器的SMTP地址,可選,默認爲smtp.<發件人郵箱後綴>
host = smtp.yeah.net
# 郵件服務器的SMTP端口,可選,默認25
port = 25
# 發件人(必須正確,否則發送失敗)
from = [email protected]
# 用戶名,默認爲發件人郵箱前綴
user = hutool
# 密碼(注意,某些郵箱需要爲SMTP服務單獨設置授權碼,詳情查看相關幫助)
pass = q1w2e3
注意:郵件服務器必須支持並打開SMTP協議,詳細請查看相關幫助說明 配置文件的樣例中提供的是我專門爲測試郵件功能註冊的yeah.net郵箱,帳號密碼公開,供Hutool用戶測試使用。
3.發送郵件
- 發送普通文本郵件,最後一個參數可選是否添加多個附件:
MailUtil.send("[email protected]", "測試", "郵件來自Hutool測試", false);
- 發送HTML格式的郵件並附帶附件,最後一個參數可選是否添加多個附件:
MailUtil.send("[email protected]", "測試", "<h1>郵件來自Hutool測試</h1>", true, FileUtil.file("d:/aaa.xml"));
- 羣發郵件,可選HTML或普通文本,可選多個附件:
ArrayList<String> tos = CollUtil.newArrayList(
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]");
MailUtil.send(tos, "測試", "郵件來自Hutool羣發測試", false);
ps:羣發的話可以將目標郵件地址寫到yml文件裏,用“,”隔開,hutool會自動截取。
send方法源碼:
//send()發送方法
public static void send(String to, String subject, String content, boolean isHtml, File... files) {
send((Collection)splitAddress(to), subject, content, isHtml, files);
}
//splitAddress(),判斷是否是字符串並截取
private static List<String> splitAddress(String addresses) {
if(StrUtil.isBlank(addresses)) {
return null;
} else {
Object result;
if(StrUtil.contains(addresses, ',')) {
result = StrUtil.splitTrim(addresses, ',');
} else if(StrUtil.contains(addresses, ';')) {
result = StrUtil.splitTrim(addresses, ';');
} else {
result = CollUtil.newArrayList(new String[]{addresses});
}
return (List)result;
}
}
發送郵件非常簡單,只需一個方法即可搞定其中按照參數順序說明如下:
- tos: 對方的郵箱地址,可以是單個,也可以是多個(Collection表示)
- subject:標題
- content:郵件正文,可以是文本,也可以是HTML內容
- isHtml: 是否爲HTML,如果是,那參數3識別爲HTML內容
- files: 可選:附件,可以爲多個或沒有,將File對象加在最後一個可變參數中即可
其他說明
- 自定義郵件服務器
除了使用配置文件定義全局賬號以外,MailUtil.send
方法同時提供重載方法可以傳入一個MailAccount
對象,這個對象爲一個普通Bean,記錄了郵件服務器信息。
MailAccount account = new MailAccount();
account.setHost("smtp.yeah.net");
account.setPort("25");
account.setAuth(true);
account.setFrom("[email protected]");
account.setUser("hutool");
account.setPass("q1w2e3");
MailUtil.send(account, CollUtil.newArrayList("[email protected]"), "測試", "郵件來自Hutool測試", false);
- 使用SSL加密方式發送郵件 在使用QQ或Gmail郵箱時,需要強制開啓SSL支持,此時我們只需修改配置文件即可:
# 發件人(必須正確,否則發送失敗),“小磊”可以任意變更,<>內的地址必須唯一,以下方式也對
# from = [email protected]
from = 小磊<[email protected]>
# 密碼(注意,某些郵箱需要爲SMTP服務單獨設置密碼,詳情查看相關幫助)
pass = q1w2e3
# 使用SSL安全連接
sslEnable = true
在原先極簡配置下只需加入sslEnable
即可完成SSL連接,當然,這是最簡單的配置,很多參數根據已有參數已設置爲默認。
完整的配置文件如下:
# 郵件服務器的SMTP地址
host = smtp.yeah.net
# 郵件服務器的SMTP端口
port = 465
# 發件人(必須正確,否則發送失敗)
from = [email protected]
# 用戶名(注意:如果使用foxmail郵箱,此處user爲qq號)
user = hutool
# 密碼(注意,某些郵箱需要爲SMTP服務單獨設置密碼,詳情查看相關幫助)
pass = q1w2e3
#使用 STARTTLS安全連接,STARTTLS是對純文本通信協議的擴展。
starttlsEnable = true
# 使用SSL安全連接
sslEnable = true
# 指定實現javax.net.SocketFactory接口的類的名稱,這個類將被用於創建SMTP的套接字
socketFactoryClass = javax.net.ssl.SSLSocketFactory
# 如果設置爲true,未能創建一個套接字使用指定的套接字工廠類將導致使用java.net.Socket創建的套接字類, 默認值爲true
socketFactoryFallback = true
# 指定的端口連接到在使用指定的套接字工廠。如果沒有設置,將使用默認端口456
socketFactoryPort = 465
# SMTP超時時長,單位毫秒,缺省值不超時
timeout = 0
# Socket連接超時值,單位毫秒,缺省值不超時
connectionTimeout = 0
- 針對QQ郵箱和Foxmail郵箱的說明
(1) QQ郵箱中SMTP密碼是單獨生成的授權碼,而非你的QQ密碼,至於怎麼生成,見騰訊的幫助說明:http://service.mail.qq.com/cgi-bin/help?subtype=1&&id=28&&no=1001256
使用幫助引導生成授權碼後,配置如下即可:
pass = 你生成的授權碼
(2) Foxmail郵箱本質上也是QQ郵箱的一種別名,你可以在你的QQ郵箱中設置一個foxmail郵箱,不過配置上有所區別。在Hutool中user
屬性默認提取你郵箱@前面的部分,但是foxmail郵箱是無效的,需要單獨配置爲與之綁定的qq號碼或者[email protected]
的XXXX
。即:
host = smtp.qq.com
from = [email protected]
user = foxmail郵箱對應的QQ號碼或者qq郵箱@前面部分
...
二、Scheduled定時任務
定時任務使用的是Spring自帶的Scheduled,使用起來非常方便,在定義的定時方法上加上@Scheduled註解即可,也不過多闡述。
1.新建mail.setting配置文件,並按hutool文檔說明加上自己的配置
2.引入依賴
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.6.3</version>
</dependency>
3.yml配置文件加上目標郵件地址,以及用於環境控制配置的參數配置
yml文件追加:
report:
env: dev
mailAddress: 地址1,地址2,地址3...
4.新建定時任務:
@Component
@Slf4j
public class ReportSendMailScheduled {
//定義一個郵件發送接口方便定時任務調用
@Autowired
private SendReportMailServiceImpl sendReportMailService;
//開發環境
@Value("${report.env}")
private String env;
//每天一點鐘執行一次
@Scheduled(cron = "0 0 1 * * ?")
public void sendMail(){
//生產環境下才發送
if (env.equals("prod")){
sendReportMailService.sendReportMail();
}
}
}
5.郵件發送服務接口
public interface ISendReportMailService {
public void sendReportMail(ReportSendMailDto reportSendMailDto);
}
6.郵件發送服務實現類
public class SendReportMailServiceImpl implements ISendReportMailService {
//注入yml配置的目標郵件地址
@Value("${report.mailAddress}")
private String mailAddress;
@Autowired
private ReportUserInfoMapper reportUserInfoMapper;
public void sendReportMail(ReportSendMailDto reportSendMailDto) {
log.info("====報表郵件開始=====");
File file = new File(StrUtil.format("截止{}報表統計.xlsx", DateUtil.getDay()));
try {
//用戶統計(這是案例,具體看自己業務需要哪些數據,總之查詢出來寫進excel)
List<ReportUserStatisticsVO> reportUserStatisticsVOS = reportUserInfoMapper.selectReportUserStatistics(reportSendMailDto);
//默認是第一個sheet
ExcelWriter writer = ExcelUtil.getBigWriter(file, "用戶數");
writer.addHeaderAlias("statisticsDate", "日期");
writer.addHeaderAlias("registeredUsers", "註冊用戶");
writer.addHeaderAlias("passengerAuthenticationUser", "乘客認證用戶");
writer.addHeaderAlias("driverAuthenticationUser", "車主認證用戶");
writer.write(reportUserStatisticsVOS, true);
//乘客需求匹配量(爲了演示寫入多個sheet)
List<ReportMatchingOfPassengerDemandVO> reportMatchingOfPassengerDemandVOS = reportMasterRouteManageMapper.searchMatchingOfPassengerDemand(reportSendMailDto);
//寫完一個sheet可以再次追加sheet
writer.setSheet("乘客需求匹配量");
writer.addHeaderAlias("statisticsDate", "日期");
writer.addHeaderAlias("passengerMatchingTotal", "乘客出行需求發佈總量");
writer.addHeaderAlias("passengerMatchingSuccess", "乘客出行需求發佈匹配成功量");
writer.addHeaderAlias("passengerMatchingFail", "乘客出行需求發佈未匹配成功量");
writer.write(reportMatchingOfPassengerDemandVOS, true);
//關閉寫入流
writer.close();
//這裏示範的是無內容僅有附件的郵件發送
MailUtil.send(mailAddress, "報表統計", "", false, file);
log.info("====報表郵件結束===");
} catch (Exception e) {
log.error(e.getMessage());
} finally {
//發送完就刪除生產的excel文件,否則文件會一直追加在項目目錄裏
file.delete();
}
}
}
代碼有詳細註釋解釋。
至此,記錄完成~