1.首先引入 mail 依赖,可以使用 freemarker 模板,本教程没有使用,用到了 lombok
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
2. 配置文件
spring:
mail:
host: smtp.geotmt.com
port: 25
username: service_portal@geotmt.com
password: portal_!@#$
default-encoding: UTF-8
properties:
mails:
smtp:
timeout: 10000
connectiontimeout: 10000
writetimeout: 10000
auth: true
starttls:
enable: true
required: true
3.邮件实体类
多附件邮件vo
/**
* @Description 多附件邮件vo
* @Author yanghanwei
* @Mail yanghanwei@geotmt.com
* @Date 14:29 2019-11-26
* @Version v1
**/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class BatchEnclosureMailVo {
/**
* 收件人s
*/
private String toStr;
/**
* 抄送人s
*/
private String cpStr;
/**
* 密送人s
*/
private String bccStr;
/**
* 主题
*/
private String subject;
/**
* 邮件内容
*/
private String content;
/**
* 附件位置
*/
private String[] filePathArr;
}
html 邮件 vo
/**
* @Description html 邮件 vo
* @Author yanghanwei
* @Mail yanghanwei@geotmt.com
* @Date 14:19 2019-11-26
* @Version v1
**/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class HtmlMailVo {
/**
* 收件人s
*/
private String toStr;
/**
* 抄送人s
*/
private String cpStr;
/**
* 密送人s
*/
private String bccStr;
/**
* 主题
*/
private String subject;
/**
* 邮件内容
*/
private String content;
}
嵌入静态资源的邮件 vo
/**
* @Description 嵌入静态资源的邮件 vo
* @Author yanghanwei
* @Mail yanghanwei@geotmt.com
* @Date 14:23 2019-11-26
* @Version v1
**/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class InlineResourceMailVo {
/**
* 收件人s
*/
private String toStr;
/**
* 抄送人s
*/
private String cpStr;
/**
* 密送人s
*/
private String bccStr;
/**
* 主题
*/
private String subject;
/**
* 邮件内容,需要包括一个静态资源的id,比如:<img src=\"cid:rscId01\" >
*/
private String content;
/**
* 静态资源路径和文件名
*/
private String filePath;
/**
* 静态资源id
*/
private String fileId;
}
带单附件的邮件 vo
/**
* @Description 带附件的邮件
* @Author yanghanwei
* @Mail yanghanwei@geotmt.com
* @Date 14:20 2019-11-26
* @Version v1
**/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class LocalEnclosureMailVo {
/**
* 收件人s
*/
private String toStr;
/**
* 抄送人s
*/
private String cpStr;
/**
* 密送人s
*/
private String bccStr;
/**
* 主题
*/
private String subject;
/**
* 邮件内容
*/
private String content;
/**
* 附件位置
*/
private String filePath;
}
简单邮件的 vo
/**
* @Description 发送简单邮件的 vo
* @Author yanghanwei
* @Mail yanghanwei@geotmt.com
* @Date 14:15 2019-11-26
* @Version v1
**/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class SimpleMailVo {
/**
* 收件人s
*/
private String toStr;
/**
* 抄送人s
*/
private String cpStr;
/**
* 密送人s
*/
private String bccStr;
/**
* 主题
*/
private String subject;
/**
* 邮件内容
*/
private String content;
}
批量的流附件邮件 Vo
/**
* @Description 批量的流附件邮件 Vo
* @Author yanghanwei
* @Mail yanghanwei@geotmt.com
* @Date 11:10 2019-11-28
* @Version v1
**/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class StreamBatchEnclosureMailVo {
/**
* 收件人s
*/
private String toStr;
/**
* 抄送人s
*/
private String cpStr;
/**
* 密送人s
*/
private String bccStr;
/**
* 主题
*/
private String subject;
/**
* 邮件内容
*/
private String content;
/**
* 附件byte Map, key: 文件名, value: 文件字节数组
*/
private Map<String, byte[]> enclosureMap;
}
流附件 mail vo
/**
* @Description 流附件 mail vo
* @Author yanghanwei
* @Mail yanghanwei@geotmt.com
* @Date 14:59 2019-11-27
* @Version v1
**/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class StreamEnclosureMailVo implements Serializable {
/**
* 收件人s
*/
private String toStr;
/**
* 抄送人s
*/
private String cpStr;
/**
* 密送人s
*/
private String bccStr;
/**
* 主题
*/
private String subject;
/**
* 邮件内容
*/
private String content;
/**
* 附件流
*/
private byte[] inputByte;
/**
* 附件名
*/
private String fileName;
}
静态资源的邮件 vo
/**
* @Description 静态资源的邮件 vo
* @Author yanghanwei
* @Mail yanghanwei@geotmt.com
* @Date 16:03 2019-11-28
* @Version v1
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class StreamInlineResourceMailVo {
/**
* 收件人s
*/
private String toStr;
/**
* 抄送人s
*/
private String cpStr;
/**
* 密送人s
*/
private String bccStr;
/**
* 主题
*/
private String subject;
/**
* 邮件内容,需要包括一个静态资源的id,比如:<img src=\"cid:rscId01\" >
*/
private String content;
/**
* 静态资源路径和文件名
*/
private String filePath;
/**
* 静态资源id
*/
private String fileId;
}
4.有了实体类,接下来就可以编写 邮件的工具类了
/**
* @Description 邮件模板类
* @Author yanghanwei
* @Mail yanghanwei@geotmt.com
* @Date 17:03 2019-11-25
* @Version v1
**/
@Component
public class SendMailUtils {
private final Logger logger = LoggerFactory.getLogger(SendMailUtils.class);
@Value("${spring.mail.username}")
private String from;
@Resource
private JavaMailSender sender;
/**
* 发送纯文本的简单邮件
*
* @param simpleMailVo
*/
public void sendSimpleMail(SimpleMailVo simpleMailVo) throws Exception {
logger.info("send simple mail starting ...");
MimeMessage message = sender.createMimeMessage();
this.setPublicMsg(message, simpleMailVo.getToStr(), simpleMailVo.getCpStr(), simpleMailVo.getBccStr(), simpleMailVo.getSubject());
message.setText(simpleMailVo.getContent());
sender.send(message);
logger.info("send simple mail,send to:{},copy for:{}", simpleMailVo.getToStr(), simpleMailVo.getCpStr());
}
/**
* 发送html格式的邮件
*
* @param htmlMailVo
*/
public void sendHtmlMail(HtmlMailVo htmlMailVo) throws Exception {
logger.info("send html mail start ...");
MimeMessage message = sender.createMimeMessage();
this.setPublicMsg(message, htmlMailVo.getToStr(), htmlMailVo.getCpStr(), htmlMailVo.getBccStr(), htmlMailVo.getSubject());
//true表示需要创建一个multipart message
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setText(htmlMailVo.getContent(), true);
sender.send(message);
logger.info("send html mail, send to:{}, copy to:{}", htmlMailVo.getToStr(), htmlMailVo.getCpStr());
}
/**
* 发送本地带附件的邮件
*
* @param vo
*/
public void sendAttachmentsMail(LocalEnclosureMailVo vo) throws Exception {
logger.info("send local enclosure mail starting ...");
String filePath = vo.getFilePath();
MimeMessage message = sender.createMimeMessage();
this.setPublicMsg(message, vo.getToStr(), vo.getCpStr(), vo.getBccStr(), vo.getSubject());
//true表示需要创建一个multipart message
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setText(vo.getContent(), true);
FileSystemResource file = new FileSystemResource(new File(filePath));
String fileName = filePath.substring(filePath.lastIndexOf(File.separator));
helper.addAttachment(fileName, file);
sender.send(message);
logger.info("send local enclosure mail send to:{}, copy to:{}", vo.getToStr(), vo.getCpStr());
}
/**
* 发送流附件的邮件
*
* @param vo
*/
public void sendStreamAttachmentsMail(StreamEnclosureMailVo vo) throws Exception {
logger.info("send stream enclosure mail starting ...");
MimeMessage message = sender.createMimeMessage();
this.setPublicMsg(message, vo.getToStr(), vo.getCpStr(), vo.getBccStr(), vo.getSubject());
//true表示需要创建一个multipart message
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setText(vo.getContent(), true);
String file = IoUtils.getFile(vo.getInputByte(), null, vo.getFileName());
FileSystemResource fileSource = new FileSystemResource(new File(file));
helper.addAttachment(vo.getFileName(), fileSource);
sender.send(message);
logger.info("send stream enclosure mail, send to:{}, copy to:{}", vo.getToStr(), vo.getCpStr());
}
/**
* 发送嵌入静态资源(一般是图片)的邮件
*
* @param vo
*/
public void sendInlineResourceMail(InlineResourceMailVo vo) throws Exception {
logger.info("send local inline resource mail starting ...");
MimeMessage message = sender.createMimeMessage();
this.setPublicMsg(message, vo.getToStr(), vo.getCpStr(), vo.getBccStr(), vo.getSubject());
//true表示需要创建一个multipart message
MimeMessageHelper helper = new MimeMessageHelper(message, true);
this.setPublicMsg(message, vo.getToStr(), vo.getCpStr(), vo.getBccStr(), vo.getSubject());
helper.setText(vo.getContent(), true);
FileSystemResource res = new FileSystemResource(new File(vo.getFilePath()));
helper.addInline(vo.getFileId(), res);
sender.send(message);
logger.info("send local inline resource mail starting send to:{}, copy:{}", vo.getToStr(), vo.getCpStr());
}
/**
* 发送多附件 方法
*
* @param vo
* @throws Exception
*/
public void sendBatchMailWithFile(BatchEnclosureMailVo vo) throws Exception {
logger.info("send local batch enclosure mail starting ...");
MimeMessage message = sender.createMimeMessage();
this.setPublicMsg(message, vo.getToStr(), vo.getCpStr(), vo.getBccStr(), vo.getSubject());
MimeMessageHelper messageHelper = new MimeMessageHelper(message, true, "UTF-8");
// 构建多附件
this.buildBodyPart(message, messageHelper, vo.getContent(), vo.getFilePathArr());
message = messageHelper.getMimeMessage();
sender.send(message);
logger.info("send local inline resource mail starting send to:{}, copy:{}", vo.getToStr(), vo.getCpStr());
}
/**
* 发送批量流附件
*
* @param vo
*/
public void sendStreamBatchEnclosureMailWithFile(StreamBatchEnclosureMailVo vo) throws Exception {
logger.info("send stream batch enclosure mail starting ...");
MimeMessage message = sender.createMimeMessage();
this.setPublicMsg(message, vo.getToStr(), vo.getCpStr(), vo.getBccStr(), vo.getSubject());
MimeMessageHelper messageHelper = new MimeMessageHelper(message, true, "UTF-8");
// 构建多附件
this.buildBodyPart(message, messageHelper, vo.getContent(), IoUtils.getFiles(null, vo.getEnclosureMap()));
message = messageHelper.getMimeMessage();
sender.send(message);
logger.info("send local inline resource mail starting send to:{}, copy:{}", vo.getToStr(), vo.getCpStr());
}
/**
* 设置邮箱服务器公共信息
*
* @param toArrStr 收件人
* @param cpArrStr 抄送人
* @param bccArrStr 密送人
* @param subject 主题
* @return
* @throws MessagingException
* @throws GeneralSecurityException
*/
private void setPublicMsg(MimeMessage message, String toArrStr, String cpArrStr, String bccArrStr, String subject) throws Exception {
// subject
subject = StringUtils.isEmpty(subject) ? "Default Theme" : subject;
String to = dealToStr(toArrStr);
String cp = dealToStr(cpArrStr);
String bcc = dealToStr(bccArrStr);
if (StringUtils.isEmpty(to)) {
throw new PreconditionFailedException("Addressee cannot be empty");
}
// Set senders
message.setFrom(new InternetAddress(from));
// Set receives
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));
// Set copy person
if (!StringUtils.isEmpty(cp)) {
message.setRecipients(Message.RecipientType.CC, InternetAddress.parse(cp));
}
// Set bcc
if (!StringUtils.isEmpty(bcc)) {
message.setRecipients(Message.RecipientType.BCC, InternetAddress.parse(bcc));
}
// Set Subject
message.setSubject(subject);
}
/**
* 处理邮件列表
*
* @param to
* @return
*/
private String dealToStr(String to) {
return StringUtils.isEmpty(to) ? null : ",".equals(to.substring(to.length() - 1)) ? to.substring(0, to.length() - 1) : to;
}
/**
* 组装多附件
*
* @param message MimeMessage 对象
* @param content 邮件内容
* @param files 附件 Filepath
* @throws Exception
*/
private void buildBodyPart(MimeMessage message, MimeMessageHelper messageHelper, String content, String[] files) throws Exception {
if (files != null) {
// 新建一个存放信件内容的BodyPart对象
BodyPart mdp = new MimeBodyPart();
// 给BodyPart对象设置内容和格式/编码方式
mdp.setContent(content, "text/html;charset=UTF-8");
// 新建一个MimeMultipart对象用来存放BodyPart对象
Multipart mm = new MimeMultipart();
// 将BodyPart加入到MimeMultipart对象中(可以加入多个BodyPart)
mm.addBodyPart(mdp);
// 把mm作为消息对象的内容
MimeBodyPart filePart;
FileDataSource filedatasource;
// 逐个加入附件
for (int j = 0; j < files.length; j++) {
filePart = new MimeBodyPart();
filedatasource = new FileDataSource(files[j]);
filePart.setDataHandler(new DataHandler(filedatasource));
try {
filePart.setFileName(filedatasource.getName());
} catch (Exception e) {
e.printStackTrace();
}
mm.addBodyPart(filePart);
}
message.setContent(mm);
} else {
messageHelper.setText(content, true);
}
}
}
5.上述的 工具方法也可以改为 多线程的方式
线程类
/**
* @Description 发邮件的线程类
* @Author yanghanwei
* @Mail yanghanwei@geotmt.com
* @Date 17:18 2019-11-25
* @Version v1
**/
public class EmailThread extends Thread {
private JavaMailSender sender;
private final MimeMessage mimeMessage;
public EmailThread(MimeMessage mimeMessage, JavaMailSender sender) {
this.mimeMessage = mimeMessage;
this.sender = sender;
}
@Override
public void run() {
sender.send(mimeMessage);
}
}
工具方法改造
/**
* @Description 邮件模板类
* @Author yanghanwei
* @Mail yanghanwei@geotmt.com
* @Date 17:03 2019-11-25
* @Version v1
**/
@Component
public class SendMailUtils {
private final Logger logger = LoggerFactory.getLogger(SendMailUtils.class);
@Value("${spring.mail.username}")
private String from;
@Resource
private JavaMailSender sender;
/**
* 发送纯文本的简单邮件
*
* @param simpleMailVo
*/
public void sendSimpleMail(SimpleMailVo simpleMailVo) throws Exception {
logger.info("send simple mail starting ...");
MimeMessage message = sender.createMimeMessage();
this.setPublicMsg(message, simpleMailVo.getToStr(), simpleMailVo.getCpStr(), simpleMailVo.getBccStr(), simpleMailVo.getSubject());
message.setText(simpleMailVo.getContent());
// 多线程发送邮件
new EmailThread(message,sender).start();
logger.info("send simple mail,send to:{},copy for:{}", simpleMailVo.getToStr(), simpleMailVo.getCpStr());
}
}
6.编写测试类
@GetMapping("/simple_mail")
public String test4() throws Exception {
SimpleMailVo simpleMailVo = new SimpleMailVo("xxx@163.com", "xxx@163.com", "xxx@163.com", "测试","ceshi 123123");
sendMailUtils.sendSimpleMail(simpleMailVo));
return "success";
}
7.补充 IoUtils
public class IoUtils {
private static final Logger logger = LoggerFactory.getLogger(IoUtils.class);
/**
* 文件转换为字节数组
*
* @param file
* @return
*/
public static byte[] fileToByteArray(File file) {
byte[] fileBytes = null;
try {
// 读取输入的文件====文件输入流
FileInputStream fis = new FileInputStream(file);
// 字节数组输出流 在内存中创建一个字节数组缓冲区,所有输出流的数据都会保存在字符数组缓冲区中
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int len;
byte[] buffer = new byte[1024];
// 将文件读入到字节数组中
while ((len = fis.read(buffer)) != -1) {
// 将指定字节数组中从偏移量 off 开始的 len 个字节写入此字节数组输出流。
baos.write(buffer, 0, len);
}
// 当前输出流的拷贝 拷贝到指定的字节数组中
fileBytes = baos.toByteArray();
fis.close();
baos.close();
} catch (Exception e) {
logger.error("failure in file to array running, case: " + e.getMessage(), e);
}
return fileBytes;
}
/**
* 多个文件转化为字节数组Map
*
* @param fileList
* @return
*/
public static Map<String, byte[]> filesToByteArrays(List<File> fileList) {
Map<String, byte[]> map = new HashMap<>(8);
if (null == fileList || fileList.isEmpty()) {
return map;
}
for (File file : fileList) {
String name = file.getName();
byte[] bytes = fileToByteArray(file);
map.put(name, bytes);
}
return map;
}
/**
* 根据byte[] byte[] 保存为本地文件
*
* @param bfile 字节数组
* @param filePath 文件路径(如果为空默认使用 临时目录)
* @param fileName 文件名
*/
public static String getFile(byte[] bfile, String filePath, String fileName) {
String fileFullPath = "";
filePath = StringUtils.isEmpty(filePath) ? SystemArgUtils.tmpdir() + System.currentTimeMillis() : filePath;
// 带缓冲得文件输出流
BufferedOutputStream bos = null;
// 文件输出流
FileOutputStream fos = null;
File file = null;
try {
File dir = new File(filePath);
// 判断文件目录是否存在
if (!dir.exists() && !dir.isDirectory()) {
dir.mkdirs();
}
//文件路径+文件名
fileFullPath = filePath + SystemArgUtils.separator() + fileName;
file = new File(fileFullPath);
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
bos.write(bfile);
} catch (Exception e) {
logger.error("failure in array to file running, case: " + e.getMessage(), e);
} finally {
if (bos != null) {
try {
bos.close();
} catch (IOException e1) {
logger.error("failure close BufferedOutputStream, case: " + e1.getMessage(), e1);
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e1) {
logger.error("failure close FileOutputStream, case: " + e1.getMessage(), e1);
}
}
}
return fileFullPath;
}
/**
* 多个 byte[] 保存为本地文件()
*
* @param filePath
* @param fileByteArr
* @return
*/
public static String[] getFiles(String filePath, Map<String, byte[]> fileByteArr) {
if (null == fileByteArr || fileByteArr.size() == 0) {
return null;
}
String[] filePathArr = new String[fileByteArr.size()];
int i = 0;
for (Map.Entry<String, byte[]> entry : fileByteArr.entrySet()) {
filePathArr[i] = getFile(entry.getValue(), filePath, entry.getKey());
i++;
}
return filePathArr;
}
}
至此完工!记录学习!