需求:发送带附件的邮件,并需要将邮件正文中的图片链接替换。
1、发送邮件的主方法
/**
* 发送邮件主方法
* @param outbox为发送邮件封装的对象,主要包括发送协议、收发人地址、密码等基本信息
* @return
*/
public MailOutboxMsg sendEmail(MailOutboxMsg outbox) throws Exception{
JavaMailSenderImpl javaMailSender = this.createMailSender(outbox);
Properties props = this.initMailProp();
javaMailSender.setJavaMailProperties(props);
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
mimeMessage.addHeader("uuid", "uuid");
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true,"UTF-8");
helper.setSubject(outbox.getSubject());
helper.setFrom(outbox.getSentEmail());
//设置邮件接收人、抄送人、密送人,均为邮件地址数组
helper.setTo(outbox.getToEmail().split(";"));
helper.setCc(outbox.getCcEmail().split(";"));
helper.setBcc(outbox.getBccEmail().split(";"));
helper.setSentDate(new Date());
//设置邮件内容,替换图片链接
this.parseContent(helper, outbox.getContent());
helper.setText(outbox.getContent(), true);
//添加附件,此处文件服务器为fastdfs,可自行替换成其他获取文件流方法
if(CollectionUtils.isNotEmpty(outbox.getAttaches())){
for(MailAttachMsg attach : outbox.getAttaches()){
String attachtUrl = attach.getAttachUrl();
String relativeUrl = attachtUrl.replace(hdfsUrlPrex, "");
String group = relativeUrl.substring(0, relativeUrl.indexOf("/"));
String fileName = relativeUrl.substring(relativeUrl.indexOf("/") + 1);
// 获取hdfs上传文件流
byte[] bytes = FastDFSClient.down(group, fileName);
File file = this.byte2File(bytes, "./" + attach.getAttachName());
helper.addAttachment(attach.getAttachName(), file);
files.add(file);
}
}
//发送邮件
javaMailSender.send(mimeMessage);
//发送成功获取邮件头,后续业务操作中会使用到
String msgId = mimeMessage.getMessageID().replace("[", "").replace("]", "");
outbox.setMsgId(msgId);
return outbox;
}
2、发送前邮件属性初始化
/**
* 初始化邮件发送sender
* @param outbox
* @return
*/
private JavaMailSenderImpl createMailSender(MailOutboxMsg outbox){
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
javaMailSender.setProtocol("smtp");
javaMailSender.setHost(outbox.getSentAddress());
javaMailSender.setPort(outbox.getSentPort());
javaMailSender.setPassword(outbox.getPassword());
javaMailSender.setUsername(outbox.getSentEmail());
javaMailSender.setDefaultEncoding("utf-8");
return javaMailSender;
}
/**
* 初始化邮件发送properties
* @return
*/
private Properties initMailProp(){
Properties props = new Properties();
props.setProperty("spring.mail.properties.mail.smtp.auth","true");
props.setProperty("spring.mail.properties.mail.smtp.ssl.enable","true");
props.setProperty("spring.mail.test-connection","true");
//防止中文乱码
props.setProperty("spring.mail.default-encoding", "GB2312");
props.setProperty("mail.smtp.socketFactory.fallback", "true");
props.setProperty("mail.smtp.starttls.enable", "true");
props.setProperty("mail.mime.splitlongparameters", "false");
return props;
}
3、解析邮件中的图片,进行替换。这个操作是避免将自身服务器链接直接暴露给其他人,也可以避免日后因为文件服务器访问权限变更,造成邮件中图片无法查看的情况
/**
* 解析发送邮件内容中的图片
* @param emailContent
*/
private String parseContent(MimeMessageHelper helper, String emailContent) throws Exception {
// 获取正文图片地址
Set<String> pics = getImgUrl(emailContent);
// 处理每张图片,必须要先设置settext,再设置addinline,否则图片无法显示
HashMap<String, File> imgMap = new HashMap<>();
for (String imageUrl : pics) {
// 正文图片
String contentId = getRandomFileName(imageUrl);
String relativeUrl = imageUrl.replace(hdfsUrlPrex, "");
String group = relativeUrl.substring(0, relativeUrl.indexOf("/"));
String fileName = relativeUrl.substring(relativeUrl.indexOf("/") + 1);
// 获取hdfs上传文件流
byte[] bytes = FastDFSClient.down(group, fileName);
emailContent = emailContent.replace(imageUrl, "cid:" + contentId);
File file = this.byte2File(bytes, "./" + relativeUrl.substring(relativeUrl.lastIndexOf("/")+1));
files.add(file);
imgMap.put(contentId, file);
}
helper.setText(emailContent,true);
for(String contentId : imgMap.keySet()){
helper.addInline(contentId, imgMap.get(contentId));
}
return emailContent;
}
/**
* 获取图片链接
* @param content
* @return
*/
private Set<String> getImgUrl(String content) {
Set<String> pics = new HashSet<>();
String img = "";
Pattern p_image;
Matcher m_image;
// String regEx_img = "<img.*src=(.*?)[^>]*?>"; //图片链接地址
String regEx_img = "<img.*src\\s*=\\s*(.*?)[^>]*?>";
p_image = Pattern.compile(regEx_img, Pattern.CASE_INSENSITIVE);
m_image = p_image.matcher(content);
while (m_image.find()) {
// 得到<img />数据
img = m_image.group();
// 匹配<img>中的src数据
Matcher m = Pattern.compile("src\\s*=\\s*\"?(.*?)(\"|>|\\s+)").matcher(img);
while (m.find()) {
// 地址以hdfs路径开头
if (m.group(1).startsWith(hdfsUrlPrex)) {
pics.add(m.group(1));
}
}
}
return pics;
}
/**
* 设置图片id,不能重复
* @param hdfsUrl
* @return
*/
private String getRandomFileName(String hdfsUrl) {
// 获取后缀名
String suffix = hdfsUrl.substring(hdfsUrl.lastIndexOf("."));
// 当前系统时间+5位随机数+文件后缀名生成
return String.valueOf(new Date().getTime()) + (int)((Math.random()*9+1)*10000) + suffix;
}
4、最后一个小方法,将字节流转换为file。我这个是从fastdfs服务器获取文件,所以需要这一步,实际情况应该比这个简单,可以直接new File即可
public File byte2File(byte[] byteArray, String targetPath) {
InputStream in = new ByteArrayInputStream(byteArray);
File file = new File(targetPath);
String path = targetPath.substring(0, targetPath.lastIndexOf("/"));
if (!file.exists()) {
new File(path).mkdir();
}
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
int len = 0;
byte[] buf = new byte[1024];
while ((len = in.read(buf)) != -1) {
fos.write(buf, 0, len);
}
fos.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != fos) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return file;
}