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;
    }

}

至此完工!记录学习!