需求描述
系統需要監控阿里雲企業郵箱的收件信息,發現Dev發送過來的郵件,且附件名稱滿足一定格式要求的,我們需要將附件保存下來,並自動上傳管理系統。已到達郵件及時有效處理且降低人力成本的目的。
技術儲備
在實現這個功能之前我們需要了解一下與郵件有關的協議,smtp、pop3和imap協議,(關於smtp,pop3以及imap協議可以點擊鏈接瞭解)在這裏我們選擇的是imap協議。因爲我們需要更新郵件的狀態,如果我們系統處理過郵件,那麼我們要將它設計爲已讀狀態。防止重複處理。
實現代碼
獲取郵箱IMAPStore的鏈接
private IMAPStore getConnect() {
//代理配置
Properties props = System.getProperties();
if (enableProxy) {
props.setProperty("proxySet", "true");
//使用測試環境專有的郵件代理IP
props.setProperty("ProxyHost", proxyIpEmail);
//使用測試環境專有的郵件代理端口
props.setProperty("ProxyPort", proxyPortEmail);
}
IMAPStore store = null;
props.setProperty("mail.store.protocol", protocol);
props.setProperty("mail.imap.host", imapHost);
props.setProperty("mail.imap.port", imapPort);
props.setProperty("mail.imap.auth.login.disable", "true");
authentication = new PasswordAuthentication(username, password);
int tryTimes = 0;
do {
try {
session = Session.getInstance(props, this);
// 2、通過session得到Store對象
store = (IMAPStore) session.getStore("imap");
// 3、連上郵件服務器
store.connect(username, password);
} catch (Exception e) {
Logs.error("第{}次嘗試連接失敗:{}", tryTimes, e.getMessage());
}
tryTimes++;
//嘗試10就可以了
if (tryTimes >= EmailServiceImpl.TRY_TIMES) {
break;
}
try {
Thread.sleep(15000);
} catch (InterruptedException e) {
//ignore this exception
}
} while (true);
return store;
}
參數配置參考
mail.store.protocol=imap
mail.imap.host=imap.mxhichina.com
mail.imap.port=143
email.username=********
email.password=******
- 獲取收件夾中的未讀郵件
@Override
public boolean receiveEmail() throws Exception {
/**
* 1. 取得鏈接
*/
IMAPStore imapStore = getConnect();
if (null == imapStore) {
Logs.error("獲取郵箱imap連接失敗。方法執行失敗。退出當前方法!!!!!!!");
return false;
}
Folder folder = imapStore.getFolder("INBOX");
folder.open(Folder.READ_WRITE);
Message[] messages = folder.getMessages(folder.getMessageCount() - folder.getUnreadMessageCount() + 1, folder.getMessageCount());
Logs.info("未讀郵件數量: {}", messages.length);
for (Message msg : messages) {
try {
//郵件發送日期超過兩天 退出遍歷方法
if (outDays(msg)) {
Logs.info("郵件{}已過期,退出循環", msg.getSubject());
continue;
}
//不是最新郵件,跳過
if (isNew(msg)) {
Logs.info("郵件不是新郵件,跳過");
continue;
}
//是否屬於需要處理的郵件
if (!needProcessEmail(msg)) {
Logs.info("郵件不需要處理,跳過");
continue;
}
/**
* 處理郵件信息
*/
processEmailData(msg);
} catch (Exception e) {
Logs.error(e.getMessage(), e);
}
}
//關閉資源
folder.close(true);
imapStore.close();
return false;
}
/**
* @param
* @param msg
* @return boolean
* @author FeianLing
* @date 2019/8/20
* @desc 檢查當前郵件是否已超過1天, 接收時間大於1天以前 返回true
*/
private boolean outDays(Message msg) throws MessagingException {
Date date = DateUtils.offsetDate(new Date(), emailOutDays);
Date receiveDate = msg.getReceivedDate();
Logs.info("郵件接收時間:{}", DateUtils.formatYmdHms(receiveDate));
return receiveDate.getTime() - date.getTime() < 0;
}
/**
* @param
* @param msg
* @return boolean
* @author FeianLing
* @date 2019/8/20
* @desc 判斷郵件是否是新的郵件
*/
private boolean isNew(Message msg) throws MessagingException {
boolean isNewFlag = false;
Flags flags = msg.getFlags();
Flags.Flag[] flagsArr = flags.getSystemFlags();
Logs.info("郵件狀態:" + JSONObject.toJSONString(flagsArr));
for (Flags.Flag flag : flagsArr) {
if (flag == Flags.Flag.SEEN) {
isNewFlag = true;
Logs.info("當前郵件爲未讀狀態!");
break;
}
}
return isNewFlag;
}
/**
* @param
* @param msg
* @return boolean
* @author FeianLing
* @date 2019/8/20
* @desc 檢查郵件內容是否需要我們處理
* 1. 檢查發件人是否滿足要求
* 2. 檢查是否包含附件
* 3. 檢查是否有滿足條件的附件
*/
private boolean needProcessEmail(Message msg) throws Exception {
Logs.info("needProcessEmail > 當前郵件的標題:{}", msg.getSubject());
// 1. 檢查發件人郵箱是否包含在我們監控的郵箱列表裏面
String from = getFrom(msg);
if (!monitoringEmail.contains(from)) {
Logs.info("當前郵件的發件人[{}]不是我們要監控的對象", from);
return false;
}
if (!isContainAttach((Part) msg)) {
Logs.info("發件人滿足要求但是附件爲空,不滿足我們監控的需求!");
return false;
}
Map<String, InputStream> fileMap = new HashMap<>();
getFileInputStream(msg, fileMap);
if (fileMap.isEmpty()) {
Logs.info("儘管郵件中有附件但是郵件中的附件卻無一個滿足要求!");
return false;
}
return true;
}
/**
* @param
* @param part
* @return java.io.InputStream
* @author FeianLing
* @date 2019/8/20
* @desc 獲取文件輸入流
*/
private void getFileInputStream(Part part, Map<String, InputStream> inputStreamMap) throws Exception {
String fileName;
if (part.isMimeType("multipart/*")) {
Multipart mp = (Multipart) part.getContent();
for (int i = 0; i < mp.getCount(); i++) {
BodyPart mPart = mp.getBodyPart(i);
String disposition = mPart.getDisposition();
if ((disposition != null)
&& ((disposition.equals(Part.ATTACHMENT)) || (disposition
.equals(Part.INLINE)))) {
fileName = mPart.getFileName();
if (fileName.toLowerCase().indexOf("gb2312") != -1) {
fileName = MimeUtility.decodeText(fileName);
}
if (checkFileName(fileName)) {
inputStreamMap.put(fileName, mPart.getInputStream());
}
} else if (mPart.isMimeType("multipart/*")) {
Logs.info("子郵件裏面的附件");
getFileInputStream(mPart, inputStreamMap);
} else {
fileName = mPart.getFileName();
if ((fileName != null)
&& (fileName.toLowerCase().indexOf("GB2312") != -1)) {
fileName = MimeUtility.decodeText(fileName);
if (checkFileName(fileName)) {
inputStreamMap.put(fileName, mPart.getInputStream());
}
}
}
}
} else if (part.isMimeType("message/rfc822")) {
getFileInputStream((Part) part.getContent(), inputStreamMap);
}
}
/**
* @param
* @param fileName
* @return boolean
* @author FeianLing
* @date 2019/8/20
* @desc 檢查文件名稱是否符合要求 FTK-88584316 FTK-申請號.pdf
*/
private boolean checkFileName(String fileName) {
return EmailServiceImpl.FILENAME_REGEX.matcher(fileName).find();
}
/**
* @param
* @param part
* @return boolean
* @author FeianLing
* @date 2019/8/20
* @desc 判斷郵件是否包含附件,如果沒有包含附件,返回false 反之返回true
*/
private boolean isContainAttach(Part part) throws Exception {
boolean attachFlag = false;
// String contentType = part.getContentType();
if (part.isMimeType("multipart/*")) {
Multipart mp = (Multipart) part.getContent();
for (int i = 0; i < mp.getCount(); i++) {
BodyPart mPart = mp.getBodyPart(i);
String disposition = mPart.getDisposition();
if ((disposition != null)
&& ((disposition.equals(Part.ATTACHMENT)) || (disposition
.equals(Part.INLINE)))) {
attachFlag = true;
} else if (mPart.isMimeType("multipart/*")) {
attachFlag = isContainAttach((Part) mPart);
} else {
String conType = mPart.getContentType();
if (conType.toLowerCase().indexOf("application") != -1) {
attachFlag = true;
}
if (conType.toLowerCase().indexOf("name") != -1) {
attachFlag = true;
}
}
}
} else if (part.isMimeType("message/rfc822")) {
attachFlag = isContainAttach((Part) part.getContent());
}
return attachFlag;
}
/**
* @param
* @param msg
* @return java.lang.String
* @author FeianLing
* @date 2019/8/20
* @desc 獲取發送地址
*/
private String getFrom(Message msg) throws MessagingException {
String from = "";
InternetAddress[] addresses = (InternetAddress[]) msg.getFrom();
if (null == addresses || addresses.length == 0) {
Logs.error("無法獲取發送人地址信息!");
return from;
}
Address address = addresses[0];
Logs.info("發件人地址json:" + JSONObject.toJSONString(address));
String form = ((InternetAddress) address).getAddress();
return form;
}