整合SpringMail连接qq邮箱
一、邮箱协议
连接邮箱有smtp、pop3、imap三种协议。
- smtp协议:主要用于发信。全称“Simple Mail Transfer Protocol”,即简单邮件传输协议。相当于中转站,将邮件发送到客户端。
- pop3协议:连接邮箱接收信件。称“Post Office Protocol 3”,POP3允许用户将服务器上的邮件下载到本地计算机上,同时删除邮件服务器上的邮件,pop3协议是接受邮件的协议。
- imap:连接邮箱接收信件全称“Internet Mail Access Protocol”,即交互式邮件存取协议,跟pop协议类似。但是可以与邮箱服务器及时交互,IMAP协议比POP3协议复杂的多,也是按照C/S的工作方式,现在较新的版本是IMAP4。
二、配置属性
连接邮箱需要配置相应的属性:
- 协议
- host
- username
- password
- port
- auth
在yml中配置如下设置:
spring:
mail:
host: smtp.qq.com #个人这样就行了,企业邮箱smtp.exmail.qq.com
username: xxxxxxxxx@qq.com
password: xxxxxxxxx
properties:
mail:
smtp:
port: 587
starttls:
enable: true
required: true
ssl: true
pop3:
protocol: pop3
host: pop.qq.com #个人这样就行了,企业邮箱pop.exmail.qq.com
port: 110 #qq官方写的是995,但是实测只能110
auth: true
即发件使用smtp、收件使用pop3协议;使用imap会在最后补充。
三、pom.xml的配置
<!-- mail -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!-- thymeleaf模板引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 模板引擎 代码生成 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.1</version>
</dependency>
<!--避坑包-->
<dependency>
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
<version>1.9.22</version>
</dependency>
mail的pom是必须的。
如果要使用模板发送就需要下面三个pom。
- thymeleaf模板引擎,可以再项目里增加html文件,并动态的往里塞数据,最后通过邮件发出去。
- org.apache.velocity是StringUtils.join(x,y);方法的由来,不明确是否需要。
- net.sourceforge.nekohtml论坛老哥的闭坑包,不确定是否需要,但是项目中确实有这两个pom↓
<!--读取csv-->
<dependency>
<groupId>net.sourceforge.javacsv</groupId>
<artifactId>javacsv</artifactId>
<version>2.0</version>
</dependency>
<!--读取文件编码-->
<dependency>
<groupId>net.sourceforge.jchardet</groupId>
<artifactId>jchardet</artifactId>
<version>1.0</version>
</dependency>
四、发邮件(使用javaMail实现)
@Test
public void sendmail() {
MailModel mailModel = new MailModel();
// 设置收件人
mailModel.setToMailList(Lists.newArrayList("1053886691@qq.com"));
// 设置标题
mailModel.setTitle("测试邮箱发送=w=!");
// 设置标题
mailModel.setContent("这里是内容!");
String messageId = JSON.toJSONString("神秘的messageId,>w<");
// 设置messageId
mailModel.setMessageId(messageId);
mailUtil.sendEmail(mailModel, false);
System.out.println("邮件成功发送");
}
五、监听退信收信
package com.kcidea.erms.service.task.impl;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.kcidea.erms.common.constant.Constant;
import com.kcidea.erms.common.constant.Vm;
import com.kcidea.erms.common.exception.CustomException;
import com.kcidea.erms.common.util.*;
import com.kcidea.erms.dao.database.DatabaseContactMessageRelDao;
import com.kcidea.erms.dao.task.ErmsTaskDao;
import com.kcidea.erms.dao.task.ErmsTaskRecordDao;
import com.kcidea.erms.domain.task.ErmsTask;
import com.kcidea.erms.domain.task.ErmsTaskRecord;
import com.kcidea.erms.enums.common.EnumTrueFalse;
import com.kcidea.erms.enums.message.EnumMessageType;
import com.kcidea.erms.enums.task.EnumTaskState;
import com.kcidea.erms.enums.task.EnumTaskType;
import com.kcidea.erms.model.database.notify.MessageIdInfoModel;
import com.kcidea.erms.model.task.*;
import com.kcidea.erms.service.common.BaseService;
import com.kcidea.erms.service.message.MessageService;
import com.kcidea.erms.service.task.BackMailService;
import com.kcidea.erms.service.task.TaskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.internet.MimeMessage;
import javax.mail.search.AndTerm;
import javax.mail.search.ComparisonTerm;
import javax.mail.search.SearchTerm;
import javax.mail.search.SentDateTerm;
import java.io.File;
import java.text.MessageFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.stream.Collectors;
/**
* @author majuehao
* @version 1.0
* @date 2022/03/07
**/
@Slf4j
@Service
public class BackMailServiceImpl extends BaseService implements BackMailService {
@Value("${my-config.temp-path}")
private String TEMP_PATH;
@Value("${spring.mail.username}")
private String username;
@Value("${spring.mail.password}")
private String password;
@Value("${spring.mail.properties.mail.pop3.protocol}")
private String protocol;
@Value("${spring.mail.properties.mail.pop3.host}")
private String host;
@Value("${spring.mail.properties.mail.pop3.port}")
private String port;
@Value("${spring.mail.properties.mail.pop3.auth}")
private String auth;
@Resource
private MessageService messageService;
@Resource
private TaskService taskService;
@Resource
private ErmsTaskRecordDao taskRecordDao;
@Resource
private ErmsTaskDao taskDao;
@Resource
private DatabaseContactMessageRelDao databaseContactMessageRelDao;
/**
* 执行定时提醒退信任务
*
* @author majuehao
* @date 2022/3/7 16:58
**/
@Override
public void executePayRemindTask() {
// 连接邮箱
Store store = this.connect();
if (store == null) {
throw new CustomException("连接邮箱失败!");
}
// 获取符合时间的邮件
Message[] messages = getMessagesByDate(store);
if (messages == null) {
log.info("暂无邮件!");
return;
}
// 根据主题筛选qq的退信邮件
messages = getMessagesBySubject(messages);
// 获取附件中的信件信息
List<BackMailModel> list = EmailUtil.getMessageId(messages);
if (CollectionUtils.isEmpty(list)) {
log.info("未能找到相关退信!");
return;
}
// 过滤掉不属于ERMS、退信任务类型以及信息不全的信件
list = filterMail(list);
// 根据任务id成组
Map<Long, List<BackMailModel>> listMap = list.stream()
.collect(Collectors.groupingBy(x -> x.getMessageIdInfo().getTaskId(),
Collectors.mapping(x -> x, Collectors.toList())));
String title = "退信提醒";
String content = "尊敬的ERMS用户,您好!检测到通知库商邮件被退信,点击此处查看详情";
for (Map.Entry<Long, List<BackMailModel>> entry : listMap.entrySet()) {
List<BackMailModel> mailList = entry.getValue();
Long sid = mailList.get(0).getMessageIdInfo().getSid();
Long userId = mailList.get(0).getMessageIdInfo().getUserId();
// 创建任务
createTask(entry.getKey(), sid, userId, entry.getValue());
// 发送站内信
messageService.addMessageList(sid, Sets.newHashSet(userId), title, content,
Constant.Message.TASK_BACK_MAIL_URL, EnumMessageType.退信提醒.getValue());
}
}
/**
* 过滤掉不属于ERMS和退信任务类型的信件
*
* @param list 信件列表
* @return 过滤后的列表
* @author majuehao
* @date 2022/3/10 16:28
**/
private List<BackMailModel> filterMail(List<BackMailModel> list) {
List<BackMailModel> result = Lists.newArrayList();
for (BackMailModel backMailModel : list) {
MessageIdInfoModel infoModel = backMailModel.getMessageIdInfo();
if (infoModel == null) {
continue;
}
if (!Objects.equals(infoModel.getSystem(), Constant.Mail.MESSAGE_ID_SYSTEM)) {
continue;
}
if (!Objects.equals(infoModel.getTaskType(), EnumTaskType.退信通知.getValue())) {
continue;
}
if (null == infoModel.getTaskId() || null == infoModel.getSid() || null == infoModel.getUserId() ||
null == infoModel.getDid() || null == infoModel.getMessageId()) {
continue;
}
result.add(backMailModel);
}
return result;
}
/**
* 创建任务
*
* @param sid 学校id
* @param userId 用户id
* @author majuehao
* @date 2022/3/10 9:40
**/
private void createTask(Long taskId, Long sid, Long userId, List<BackMailModel> list) {
List<BackMailExcelModel> excelList = Lists.newArrayList();
log.info("创建任务...");
// 遍历邮件列表
for (BackMailModel mailModel : list) {
MessageIdInfoModel info = mailModel.getMessageIdInfo();
Long did = info.getDid();
Long messageId = info.getMessageId();
// 数据库名称
String databaseName = super.findDatabaseName(sid, did);
List<BackMailReasonModel> backMailReasonList = mailModel.getBackMailReasonList();
for (BackMailReasonModel backMailReasonModel : backMailReasonList) {
// 发信时间
String sendTim = getSendTime(sid, did, messageId);
excelList.add(new BackMailExcelModel().create(databaseName, backMailReasonModel.getTo(), sendTim,
backMailReasonModel.getReason()));
}
}
// 根据退信中的任务id找到之前执行的任务
TaskInfoModel mailTask = taskService.findOneTask(taskId, sid);
if (mailTask == null) {
throw new CustomException(Vm.ERROR_PARAMS);
}
// 任务名称(之前的任务时间+名称+退信通知)
String taskName = DateTimeUtil.localDateTimeToString(mailTask.getStartTime()).concat(mailTask.getTaskName())
.concat(Constant.SplitChar.LINE_CHAR).concat(EnumTaskType.退信通知.getName());
ErmsTask task = taskService.addTask(sid, taskName, EnumTaskType.退信通知.getValue(),
EnumTaskState.正在执行.getValue(), JSON.toJSONString(taskName), null, null, userId,
Boolean.TRUE);
ErmsTaskRecord taskRecord = new ErmsTaskRecord().setTaskId(task.getId()).setStartTime(LocalDateTime.now());
// 生成文件名
String resultFileName = System.currentTimeMillis() + "-result" + Constant.Suffix.XLSX_WITH_POINT;
// 总数/成功数
int totalCount = excelList.size();
try {
String resultFilePath = saveFile(excelList, resultFileName);
String taskLog = MessageFormat.format("共{0}条数据,成功:{1},失败:{2},警告:{3},您可以下载结果文件查看详细结果。",
Integer.toString(totalCount), Integer.toString(totalCount),
Integer.toString(0), Integer.toString(0));
TaskResultModel taskResultModel = new TaskResultModel();
//实际导入数量是 成功+警告
taskResultModel.create(EnumTaskState.执行完成.getValue(), totalCount,
totalCount, 0, resultFileName, resultFilePath, taskLog);
//更新任务状态
task.updateByTaskResult(taskResultModel);
//更新任务结果日志
taskRecord.setTaskLog(taskLog);
log.info("任务执行成功");
} catch (Exception ex) {
log.error("执行数据库信息导入任务失败,原因:" + ex.getMessage());
//更新任务状态
task.setTaskState(EnumTaskState.执行失败.getValue());
//更新任务结果
taskRecord.setTaskLog(Vm.TASK_ERROR.concat(ex.getMessage()));
} finally {
//任务结果保存
taskRecord.setEndTime(LocalDateTime.now());
taskRecord.setCreatedBy(userId);
taskRecord.setCreatedTime(LocalDateTime.now());
taskRecordDao.insert(taskRecord);
//更新任务
taskDao.updateById(task);
}
}
/**
* 获取发送时间
*
* @param sid 学校id
* @param did 数据库id
* @param messageId 信息id
* @return 发送时间
* @author majuehao
* @date 2022/3/10 21:08
**/
private String getSendTime(Long sid, Long did, Long messageId) {
LocalDateTime date = databaseContactMessageRelDao.findSendTimeBySidDidMessageId(sid, did, messageId);
if (date != null) {
return DateTimeUtil.localDateTimeToString(date);
}
return "";
}
/**
* 保存文件
*
* @param exportList 导出文件数据
* @param fileName 文件名
* @return java.lang.String
* @author majuehao
* @date 2022/3/10 9:40
**/
private String saveFile(List<BackMailExcelModel> exportList, String fileName) {
//当前月
String nowMonth = DateTimeUtil.localDateToString(LocalDate.now(), Constant.Pattern.MONTH);
//保存的路径加上月份
String filePath = TEMP_PATH.concat(nowMonth).concat("/");
ExcelUtil.saveExcel(exportList, "", "退信信息详情", BackMailExcelModel.class, filePath, fileName);
//读取本地文件
File file = new File(filePath.concat(fileName));
//上传到文件服务器
return MinioFileUtil.uploadFile(Constant.MinIoBucketName.ERMS, file, EnumTrueFalse.是.getValue());
}
/**
* 根据邮件主题筛选邮件列表
*
* @param messages 邮件列表
* @return 邮件列表
* @author majuehao
* @date 2022/3/8 9:33
**/
private Message[] getMessagesBySubject(Message[] messages) {
Message[] messagesBySubject = new Message[messages.length];
int index = 0;
for (Message message : messages) {
try {
MimeMessage mimeMessage = (MimeMessage) message;
String subject = ConvertUtil.getSubChinese(mimeMessage.getSubject(),
Constant.Encode.ISO_8859_1);
if (Objects.equals(Constant.Mail.SUBJECT_KEY_WORD, subject)) {
messagesBySubject[index++] = message;
}
} catch (Exception e) {
log.error("获取邮件主题失败");
}
}
return messagesBySubject;
}
/**
* 获取符合条件的邮件
*
* @param store 邮箱
* @return javax.mail.Message[]
* @author majuehao
* @date 2022/3/7 17:29
**/
private Message[] getMessagesByDate(Store store) {
Message[] messages = null;
try {
// 获得收件箱
Folder folder = store.getFolder(Constant.Mail.INBOX);
// 以读写模式READ_WRITE打开收件箱 只读READ_ONLY
folder.open(Folder.READ_ONLY);
// 获取 从昨天上午9.00开始,到今天上午9.00结束 的邮件
SearchTerm[] st = new SearchTerm[]{
// 从昨天上午9.00开始
new SentDateTerm(ComparisonTerm.GE, DateTimeUtil.getNineAmOfYesterday()),
// 到今天上午9.00结束
new SentDateTerm(ComparisonTerm.LE, DateTimeUtil.getNineAmOfToday()),
};
// 设置搜索条件
SearchTerm search = new AndTerm(st);
// 按条件搜索邮件
messages = folder.search(search);
// 找到的邮件数量
if (messages.length == 0) {
return null;
}
} catch (Exception e) {
log.error(e.getMessage());
}
return messages;
}
/**
* 连接邮箱
*
* @return void
* @author majuehao
* @date 2022/3/7 17:23
**/
private Store connect() {
Store store = null;
try {
// 准备连接服务器的会话信息
Properties props = new Properties();
props.setProperty(Constant.Mail.POP3_PROTOCOL, protocol);
props.setProperty(Constant.Mail.POP3_PORT, port);
props.setProperty(Constant.Mail.POP3_HOST, host);
props.setProperty(Constant.Mail.POP3L_AUTH, auth);
// 创建Session实例对象
Session session = Session.getInstance(props);
// 创建IMAP协议的Store对象
// store = session.getStore(protocol);
// 连接邮件服务器
// store.connect(username, password);
} catch (Exception e) {
log.error(e.getMessage());
}
return store;
}
}
六、邮件工具类MailUtil
可以使用模板或者直接发送。
- 使用模板时,请配置好相应的模板已经模板参数。
- 直接发送时,如果发送内容中有图片,也会进行单独处理
- 对附件进行处理
package com.kcidea.erms.common.util;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.kcidea.erms.common.constant.Constant;
import com.kcidea.erms.common.exception.CustomException;
import com.kcidea.erms.model.common.MailModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import javax.annotation.Resource;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author yeweiwei
* @version 1.0
* @date 2021/11/17
**/
@Slf4j
@Component
public class MailUtil {
@Resource
private JavaMailSender javaMailSender;
@Value("${spring.mail.username}")
private String fromEmail;
@Resource
private TemplateEngine templateEngine;
@Value("${my-config.temp-path}")
private String TEMP_PATH;
/**
* 群发邮件
*
* @param mailModel 邮件相关信息
* @author yeweiwei
* @date 2021/11/17 14:18
*/
public void sendEmail(MailModel mailModel, boolean useTemplateFlag) {
List<String> mailList = mailModel.getToMailList();
if (CollectionUtils.isEmpty(mailList)) {
throw new CustomException("邮件发送失败,请检查收件人是否正确!");
}
try {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
// 发信设置messageId
mimeMessage.setHeader(Constant.Mail.MESSAGE_ID, mailModel.getMessageId());
// 邮件帮助类
MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true,
Constant.Encode.UTF_8);
//发送人
messageHelper.setFrom(fromEmail);
//发送给谁
messageHelper.setTo(mailList.toArray(new String[0]));
//标题
messageHelper.setSubject(mailModel.getTitle());
// 是否使用模板
if (useTemplateFlag) {
// 使用模板
this.useTemplate(mailModel.getParamsMap(), mailModel.getTemplate(), messageHelper);
} else {
// 不使用模板
this.notUseTemplate(mailModel.getContent(), messageHelper);
}
// 是否包含附件
if (!Strings.isNullOrEmpty(mailModel.getFilePath())) {
FileSystemResource fileSystemResource = new FileSystemResource(mailModel.getFilePath());
messageHelper.addAttachment(mailModel.getFileName(), fileSystemResource);
}
//发送邮件
javaMailSender.send(mimeMessage);
} catch (Exception e) {
log.error("邮件发送失败,原因:" + e.getMessage(), e);
throw new CustomException("邮件发送失败,请检查邮箱填写是否正确!");
}
}
/**
* 不使用模板
*
* @param content 邮件内容
* @param messageHelper 邮件帮助类
* @author majuehao
* @date 2022/3/8 10:19
**/
private void notUseTemplate(String content, MimeMessageHelper messageHelper) throws Exception {
Map<Integer, FileSystemResource> map = Maps.newHashMap();
// 解析文本内容
List<String> imgPathList = Lists.newArrayList(this.getImgPathSet(content));
if (!CollectionUtils.isEmpty(imgPathList)) {
for (int i = 0; i < imgPathList.size(); i++) {
String imgPath = imgPathList.get(i);
if (Strings.isNullOrEmpty(imgPath)) {
continue;
}
content = content.replace(imgPath, Constant.NotifyContact.CID + i);
String filePath = writeToLocal(imgPath);
FileSystemResource res = new FileSystemResource(filePath);
map.put(i, res);
}
}
if (!Strings.isNullOrEmpty(content)) {
messageHelper.setText(content, true);
for (Map.Entry<Integer, FileSystemResource> entry : map.entrySet()) {
FileSystemResource value = entry.getValue();
Integer key = entry.getKey();
messageHelper.addInline(String.valueOf(key), value);
}
}
}
/**
* 使用模板
*
* @param paramsMap 模板参数
* @param template 模板
* @param messageHelper 邮件帮助类
* @author majuehao
* @date 2022/3/8 10:16
**/
private void useTemplate(Map<String, Object> paramsMap, String template, MimeMessageHelper messageHelper)
throws MessagingException {
//使用模板thymeleaf
Context context = new Context();
//定义模板数据
context.setVariables(paramsMap);
//获取thymeleaf的html模板,指定模板路径
String emailContent = templateEngine.process(template, context);
messageHelper.setText(emailContent, true);
}
/**
* 获取IMG标签里的src路径
*
* @param htmlStr HTML文本
* @return java.util.Set<java.lang.String>
* @author majuehao
* @date 2022/3/4 18:05
**/
private Set<String> getImgPathSet(String htmlStr) {
if (Strings.isNullOrEmpty(htmlStr)) {
return Sets.newHashSet();
}
Set<String> pics = Sets.newHashSet();
String regExImg = "<img.*src\\s*=\\s*(.*?)[^>]*?>";
Pattern pImage = Pattern.compile(regExImg, Pattern.CASE_INSENSITIVE);
Matcher mImage = pImage.matcher(htmlStr);
while (mImage.find()) {
// 得到<img />数据
String img = mImage.group();
// 匹配<img>中的src数据
Matcher matcher = Pattern.compile("src\\s*=\\s*\"?(.*?)(\"|>|\\s+)").matcher(img);
while (matcher.find()) {
pics.add(matcher.group(1));
}
}
return pics;
}
/**
* 把文件流写到本地文件
*
* @param imgPath 路径
* @author majuehao
* @date 2022/3/4 18:05
**/
private String writeToLocal(String imgPath) throws IOException {
// 文件后缀名
String suffix = imgPath.substring(imgPath.lastIndexOf(Constant.SplitChar.POINT_CHAR));
InputStream fileInputStream = MinioFileUtil.getFileInputStream(Constant.MinIoBucketName.ERMS,
imgPath.substring(imgPath.lastIndexOf("=") + 1));
// 将文件流写入本地文件
String filePath = TEMP_PATH + DateTimeUtil.localDateToString(LocalDate.now());
File file = new File(filePath);
if (!file.exists()) {
file.mkdir();
}
String fileName = System.currentTimeMillis() + suffix;
filePath = filePath.concat("/").concat(fileName);
int index;
byte[] bytes = new byte[1024];
FileOutputStream downloadFile = new FileOutputStream(filePath);
while ((index = fileInputStream.read(bytes)) != -1) {
downloadFile.write(bytes, 0, index);
downloadFile.flush();
}
downloadFile.close();
fileInputStream.close();
return filePath;
}
}
七、邮件工具类
package com.kcidea.erms.common.util;
import com.alibaba.fastjson.JSON;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.kcidea.erms.common.constant.Constant;
import com.kcidea.erms.model.database.notify.MessageIdInfoModel;
import com.kcidea.erms.model.task.BackMailModel;
import com.kcidea.erms.model.task.BackMailReasonModel;
import lombok.extern.slf4j.Slf4j;
import javax.mail.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author majuehao
* @version 1.0
* @date 2022/03/07
**/
@Slf4j
public class EmailUtil {
private static final String multipart = "multipart/*";
/**
* 内容解析
*
* @param part part
* @return X-OQ-MSGID
* @author majuehao
* @date 2022/3/7 13:18
**/
public static BackMailModel analysisMessage(Part part) throws Exception {
BackMailModel backMail = new BackMailModel();
if (!part.isMimeType(multipart)) {
return backMail;
}
// messageIdInfo
MessageIdInfoModel info = null;
// 收件人
String to = "";
// 原因
String reason = "";
List<BackMailReasonModel> backMailReasonModelList = Lists.newArrayList();
Multipart multipart = (Multipart) part.getContent();
for (int i = 0; i < multipart.getCount(); i++) {
BodyPart bodyPart = multipart.getBodyPart(i);
if (part.isMimeType("message/rfc822")) {
return analysisMessage((Part) part.getContent());
}
InputStream inputStream = bodyPart.getInputStream();
try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) {
String strLine;
boolean nextLine = false;
while ((strLine = br.readLine()) != null) {
// 虽然发送是设置的是MessageId,但是在退信时,qq会把原邮件的messageId放在X-OQ-MSGID中
if (strLine.startsWith(Constant.Mail.X_OQ_MSGID)) {
String[] split = strLine.split(Constant.Mail.X_OQ_MSGID);
String messageId = (split.length > 1 ? split[1].trim() : null);
// 解析messageId
info = analysisMessageId(messageId);
}
if (strLine.contains(Constant.Mail.NOT_GET_MAIL_PEOPLE)) {
to = getNotGetMailPeople(strLine);
}
// 如果上一行检测到 退信原因 四个字,那么这一行就把具体原因截取出来
if (nextLine) {
nextLine = false;
reason = getReason(strLine);
backMailReasonModelList.add(new BackMailReasonModel().create(to, reason));
}
if (strLine.contains(Constant.Mail.REASON)) {
// 检测到 退信原因 四个字,下一行将是具体原因
nextLine = true;
}
}
}
}
backMail.create(backMailReasonModelList, info);
return backMail;
}
/**
* 获取附件中的MessageId
*
* @param messages 信息列表
* @return void
* @author majuehao
* @date 2022/3/7 13:23
**/
public static List<BackMailModel> getMessageId(Message[] messages) {
List<BackMailModel> list = Lists.newArrayList();
Arrays.stream(messages).filter(EmailUtil::isContainAttachment).forEach((message -> {
BackMailModel backMail = null;
try {
backMail = EmailUtil.analysisMessage(message);
} catch (Exception e) {
log.error("解析MessageId失败,原因:{}", e.getMessage());
}
if (backMail != null) {
list.add(backMail);
}
}));
return list;
}
/**
* 是否包含附件
*
* @param part part
* @return 是否包含附件
* @author majuehao
* @date 2022/3/7 13:33
**/
public static boolean isContainAttachment(Part part) {
boolean attachFlag = false;
if (part == null) {
return attachFlag;
}
try {
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 = isContainAttachment((Part) mpart);
} else {
String contype = mpart.getContentType();
if (contype.toLowerCase().contains("application")) {
attachFlag = true;
}
if (contype.toLowerCase().contains("name")) {
attachFlag = true;
}
}
}
} else if (part.isMimeType("message/rfc822")) {
attachFlag = isContainAttachment((Part) part.getContent());
}
} catch (MessagingException | IOException e) {
e.printStackTrace();
}
return attachFlag;
}
/**
* 解析messageId
*
* @param messageId messageId
* @author majuehao
* @date 2022/3/8 16:16
**/
public static MessageIdInfoModel analysisMessageId(String messageId) {
if (Strings.isNullOrEmpty(messageId)) {
return null;
}
try {
return JSON.parseObject(messageId, MessageIdInfoModel.class);
} catch (Exception e) {
log.error("messageId解析失败");
return null;
}
}
/**
* 获取无法收到邮件的邮箱的集合
*
* @param strLine 行数据
* @return 无法收到邮件的邮箱的集合
* @author majuehao
* @date 2022/3/9 17:04
**/
public static String getNotGetMailPeople(String strLine) {
String notGetMail = "";
if (Strings.isNullOrEmpty(strLine)) {
return notGetMail;
}
String regex = "[\\w!#$%&'*+/=?^_`{|}~-]+(?:\\.[\\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\\w](?:[\\w-]*[\\w])?\\.)+[\\w](?:[\\w-]*[\\w])?";
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(strLine);
while (matcher.find()) {
// 得到<img />数据
notGetMail = matcher.group();
}
return notGetMail;
}
/**
* 获取退信原因
*
* @param strLine 行数据
* @return 退信原因
* @author majuehao
* @date 2022/3/9 17:04
**/
public static String getReason(String strLine) {
String reason = "";
if (Strings.isNullOrEmpty(strLine)) {
return reason;
}
String regex = "<[a-zA-Z]+.*?>([\\s\\S]*?)<br";
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(strLine);
while (matcher.find()) {
// 得到<td>...<br中的数据
reason = matcher.group(1);
}
return reason;
}
}
八、imap收信
@Value("${spring.mail.username}")
private String username;
@Value("${spring.mail.password}")
private String password;
@Value("${spring.mail.properties.mail.pop3.protocol}")
private String protocol;
@Value("${spring.mail.properties.mail.pop3.host}")
private String host;
@Value("${spring.mail.properties.mail.pop3.port}")
private String port;
@Value("${spring.mail.properties.mail.pop3.auth}")
private String auth;
@Test
public void getSendBox() {
IMAPFolder folder = null;
IMAPStore store = null;
try {
String host = "imap.qq.com";
int port = 993;
final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
/* Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
这里有一个错我是这么解决的(Windows -> Preferences,Java/Compiler/Errors/Warnings->
Deprecated and restricted API, Forbidden reference (access rules),原始设定为Error修改为Warning)*/
Properties props = System.getProperties();
props.setProperty("mail.imap.socketFactory.class", SSL_FACTORY);
props.setProperty("mail.imap.socketFactory.port", "993");
props.setProperty("mail.store.protocol", "imap");
props.setProperty("mail.imap.host", host);
props.setProperty("mail.imap.port", "993");
props.setProperty("mail.imap.auth.login.disable", "true");
Session session = Session.getDefaultInstance(props, null);
session.setDebug(false);
store = (IMAPStore) session.getStore("imap"); // 使用imap会话机制,连接服务器
store.connect(host, port, username, password);
folder = (IMAPFolder) store.getFolder("Sent Messages"); //收件箱
Folder defaultFolder = store.getDefaultFolder();
Folder[] allFolder = defaultFolder.list();
for (int i = 0; i < allFolder.length; i++) {
System.out.println("这个是服务器中的文件夹=" + allFolder[i].getFullName());
}
// 使用只读方式打开收件箱
folder.open(Folder.READ_WRITE);
int size = folder.getMessageCount();
System.out.println("这里是打印的条数==" + size);
Message[] mess = folder.getMessages();
// Message message = folder.getMessage(size);
for (int i = 0; i < 5; i++) {
String from = mess[i].getFrom()[0].toString();
String subject = mess[i].getSubject();
Date date = mess[i].getSentDate();
System.out.println("From: " + from);
System.out.println("Subject: " + subject);
System.out.println("Date: " + date);
System.out.println("====================");
}
/* String from = message.getFrom()[0].toString();
String subject = message.getSubject();
Date date = message.getSentDate();*/
/* BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); */
} catch (MessagingException e) {
e.printStackTrace();
} finally {
try {
if (folder != null) {
folder.close(false);
}
if (store != null) {
store.close();
}
} catch (MessagingException e) {
e.printStackTrace();
}
}
System.out.println("接收完毕!");
}
try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) {
String strLine;
boolean nextLine = false;
while ((strLine = br.readLine()) != null) {
// 虽然发送是设置的是MessageId,但是在退信时,qq会把原邮件的messageId放在X-OQ-MSGID中
if (strLine.startsWith(Constant.Mail.X_OQ_MSGID)) {
String[] split = strLine.split(Constant.Mail.X_OQ_MSGID);
String messageId = (split.length > 1 ? split[1].trim() : null);
// 解析messageId
info = analysisMessageId(messageId);
}
if (strLine.contains(Constant.Mail.NOT_GET_MAIL_PEOPLE))
// 如果上一行检测到 退信原因 四个字,那么这一行就把具体原因截取出来
if (nextLine) {
nextLine = false;
reason = getReason(strLine);
toReasonsMap.put(to, reason);
}
if (strLine.contains(Constant.Mail.REASON)) {
// 检测到 退信原因 四个字,下一行将是具体原因
nextLine = true;
}
}
}
/**
* 获取退信原因
*
* @param strLine 行数据
* @return 退信原因
* @author majuehao
* @date 2022/3/9 17:04
**/
public static String getReason(String strLine) {
String reason = "";
if (Strings.isNullOrEmpty(strLine)) {
return reason;
}
String regex = "<[a-zA-Z]+.?>([\s\S]?)<br";
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(strLine);
while (matcher.find()) {
// 得到
reason = matcher.group(1);
}
return reason;
}
log.info("未能找到相关退信!");
log.info("创建任务...");
log.info("任务执行成功");
工具书:参考工具;
标准/法规:标准;
事实数据:事实;数据;
;:;
,:;
文摘目录:文摘引文
科技报告:其他;
Q.E.D.