Spring Boot基于FreeMarker发送模板邮件(带附件)

一、背景

  邮件在工作中中经常被用到,场景非常的广泛,比如:注册账户,接收验证码,或者是公司发的公告,又或者是异常消息推送等等。本文中的Spring Boot 的版本是2.6.0

二、maven依赖

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.alian</groupId>
    <artifactId>springEmail</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>SpringEmail</name>
    <description>SpringEmail</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.68</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.14</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

三、编码实现

3.1、邮件对象

Email.java

@Data
public class Email {

    /**
     * 邮件发送方
     */
    private String sender;

    /**
     * 邮件接收方
     */
    private List<String> receiverList = new ArrayList<>();

    /**
     * 邮件抄送方
     */
    private List<String> ccList = new ArrayList<>();

    /**
     * 邮件主题
     */
    private String subject;

    /**
     * 邮件内容
     */
    private String content;

    /**
     * 嵌入式资源(key:contentId,value:filePath)
     */
    private Map<String, String> inlineMap=new HashMap<>();

    /**
     * 附件列表(value:filePath)
     */
    private List<String> attachmentList = new ArrayList<>();

    public String[] getReceiverArray() {
        return this.receiverList.toArray(new String[0]);
    }

    public String[] getCcArray() {
        return this.ccList.toArray(new String[0]);
    }

    public String toJsonString() {
        return JSON.toJSONString(this);
    }
}

邮件对象主要包含:

  • 邮件发送方(sender)
  • 邮件接收方(receiverList),支持发送多人
  • 邮件抄送方(ccList),支持抄送多人
  • 邮件主题(subject)
  • 邮件内容(content),可以使用freeMark模板
  • 嵌入式资源(inlineMap),支持嵌入多个资源
  • 附件列表(attachmentList),支持多个附件发送

3.2、服务层

EmailService.java

@Slf4j
@Service
public class EmailService {

    @Autowired
    private JavaMailSenderImpl mailSender;

    @Autowired
    private FreeMarkerConfigurer configurer;

    public boolean sendSimpleEmail(Email email) {
        log.info("发送简单邮件信息:{}", email.toJsonString());
        // 实例化SimpleMailMessage
        SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
        try {
            // 设置邮件发件人
            simpleMailMessage.setFrom(email.getSender());
            // 设置邮件接收人(可以多人)
            simpleMailMessage.setTo(email.getReceiverArray());
            // 设置邮件抄送
            String[] ccArray = email.getCcArray();
            if (ccArray.length > 0) {
                simpleMailMessage.setCc(ccArray); // 普通抄送[邮件接收人可以看到抄送给谁了]
//                simpleMailMessage.setBcc(); // 盲抄送[邮件接收人可以看不到抄送给谁了]
            }
            // 设置邮件主题
            simpleMailMessage.setSubject(email.getSubject());
            // 设置邮件内容
            simpleMailMessage.setText(email.getContent());
            // 发送简单邮件
            mailSender.send(simpleMailMessage);
            return true;
        } catch (Exception e) {
            log.error(null, e);
        }
        return false;
    }

    public boolean sendComplexEmail(Email email, String templateName, Map<String, Object> data) {
        log.info("发送复杂邮件信息:{}", email.toJsonString());
        MimeMessage mimeMessage = mailSender.createMimeMessage();
        try {
            MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage,true,"UTF-8");
            // 设置邮件发件人
            messageHelper.setFrom(email.getSender());
            // 设置邮件接收人(可以多人)
            messageHelper.setTo(email.getReceiverArray());
            // 设置邮件抄送
            String[] ccArray = email.getCcArray();
            if (ccArray.length > 0) {
                messageHelper.setCc(ccArray); // 普通抄送[邮件接收人可以看到抄送给谁了]
//                messageHelper.setBcc(ccArray); // 盲抄送[邮件接收人可以看不到抄送给谁了]
            }
            // 设置邮件主题
            messageHelper.setSubject(email.getSubject());
            // 通过模板后去内容
            String content = getContent(templateName, data);
            // 设置邮件内容,第二个参数为true表示将发送内容设置为"text/html"
            messageHelper.setText(content, true);
            // 批量处理嵌入式资源
            Map<String, String> inlineMap = email.getInlineMap();
            for (Map.Entry<String, String> entry : inlineMap.entrySet()) {
                String contentId = entry.getKey();
                String filePath = entry.getValue();
                // 设置嵌入式资源
                messageHelper.addInline(contentId, new File(filePath));
            }
            // 批量处理附件
            List<String> attachmentList = email.getAttachmentList();
            for (int i = 0; i < attachmentList.size(); i++) {
                String filePath = attachmentList.get(i);
                String fileName = i + "-" + filePath.substring(filePath.lastIndexOf(File.separator));
                // 设置附件
                messageHelper.addAttachment(fileName, new File(filePath));
            }
            // 发送简单邮件
            mailSender.send(mimeMessage);
            return true;
        } catch (Exception e) {
            log.error(null, e);
        }
        return false;
    }

    public String getContent(String templateName, Map<String, Object> data) throws TemplateException, IOException {
        // 获取模板
        Template template = configurer.getConfiguration().getTemplate(templateName, "UTF-8");
        // 填充数据并把模板转为字符串
        return FreeMarkerTemplateUtils.processTemplateIntoString(template, data);
    }

}

其中:

  • SimpleMailMessage:用来发送简单的文本邮件
  • MimeMessage:用来发送html邮件、带附件的邮件、有静态资源(图片)的邮件

  如果你的代码里有多个附件或者多嵌入式资源,就需要如下:

MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage,true,"UTF-8");

  第二个参数说明你有多个资源文件(嵌入式资源或者附件)

3.2.1、抄送人
  • 普通抄送 setCc():邮件接收人可以看到抄送给谁了
  • 盲抄送 setBcc():邮件接收人可以看不到抄送给谁了
3.2.2、嵌入式资源

  通过MimeMessageHelperaddInline方法可以添加嵌入式资源,比如在邮件里显示图片。

// 设置嵌入式资源
messageHelper.addInline(contentId, new File(filePath));

  需要注意的是

  • 如果模板是<img src=‘cid:CSDN1234’/>,那么contentId就是CSDN1234不要把cid:一起写入了
  • 有多个资源时,contentId不要重复了
3.2.3、附件

  通过MimeMessageHelperaddAttachment方法可以添加附件。

// 设置附件
messageHelper.addAttachment(fileName, new File(filePath));

  需要注意的是:这里的文件名fileName尽量不要相同,如果有相同的,最好加以区分

3.3、邮件模板

complex-email.ftl

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>${title}</title>
</head>
<body>
    <p>
        <img width="100" height="50" src='cid:CSDN1234'/>
        <img width="100" height="100"src='cid:Alian1223'/>
        </br>
        <span>欢迎使用CSDN博客系统</span>
    </p>
    <table>
        <tr>
            <td>平台地址:</td>
            <td><a href="${website}">${website}</a></td>
        </tr>
        <tr>
            <td>登录账号:</td>
            <td>${account}</td>
        </tr>
        <tr>
            <td>登录密码:</td>
            <td>${password}</td>
        </tr>
    </table>
    <p>
        温馨提示:密码有效期为90天,密码必须包含大小写字母数字及特殊符号且长度不小于8位,请妥善保管好密码,祝您工作顺利!
    </p>
</body>
</html>

  需要注意的是:我这里的模板文件complex-email.ftl放到resources目录下了

3.3.1、模板引擎

  FreeMarker是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。因为我们发送邮件可能会有很多种,频繁改动代码不方便,所以我们采用模板,增加一个模板,然后把参数传入即可。

@Autowired
private FreeMarkerConfigurer configurer;

public String getContent(String templateName, Map<String, Object> data) throws TemplateException, IOException {
    // 获取模板
    Template template = configurer.getConfiguration().getTemplate(templateName, "UTF-8");
    // 填充数据并把模板转为字符串
    return FreeMarkerTemplateUtils.processTemplateIntoString(template, data);
}

  我们通过FreeMarkerConfigurer来获取到我们的模板,然后通过Spring提供的方法FreeMarkerTemplateUtils.processTemplateIntoString(template, data)完成数据的封装。

  当我们使用MimeMessage发送html邮件时,设置邮件内容时,第二个参数为true表示将发送内容设置为"text/html",不然就会把内容当成文本发送了。

// 设置邮件内容,第二个参数为true表示将发送内容设置为"text/html"
messageHelper.setText(content, true);

3.4、配置文件

application.properties

#服务端口
server.port=8080
#上下文路径
server.servlet.context-path=/email

#163邮箱
#配置邮件发送的服务器
spring.mail.host=smtp.163.com
#邮件的发送者
spring.mail.username=你的163邮箱
#邮箱申请的授权码
spring.mail.password=你的授权码

#邮件的编码格式
spring.mail.default-encoding=UTF-8
#邮件服务器连接超时
spring.mail.properties.mail.smtp.connectiontimeout=10000
#邮件接收超时
spring.mail.properties.mail.smtp.timeout=20000
#邮件发送超时
spring.mail.properties.mail.smtp.writetimeout=10000

我上面是使用163邮箱发送,如果你想用QQ邮箱或者企业微信邮箱则类似如下配置:

QQ邮箱

##qq邮箱配置(开启POP3,设置客户端授权码)
##配置邮件发送的服务器
#spring.mail.host=smtp.qq.com
##邮件的发送者
#spring.mail.username=你的QQ@qq.com
##邮箱申请的授权码
#spring.mail.password=你的授权码

企业微信邮箱

##企业微信邮箱
##配置邮件发送的服务器
#spring.mail.host=smtp.exmail.qq.com
##邮件的发送者
#spring.mail.username=你的企业微信邮箱
##邮箱申请的授权码
#spring.mail.password=你的授权码
  • 上面的password不是邮箱登录密码,是开启POP3之后设置的客户端授权码
  • 默认端口2525,当我们使用465端口时,新增如下配置:
spring.mail.port=465
spring.mail.properties.mail.smtp.ssl.enable=true

四、测试

4.1、发送简单邮件

@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class SimpleEmailTest {

    @Autowired
    private EmailService emailService;

    @Test
    public void sendSimpleEmail() {
        List<String> receiverList = new ArrayList<>();
        receiverList.add("zhanglian@wtsd.cn");
        Email email = new Email();
        email.setSender("wtsd_ywdl@163.com");
        email.setReceiverList(receiverList);
        email.setSubject("发送简单邮件测试(纯文本)");
        email.setContent("文章你都没有看完,好意思说有5年Java功力???");
        boolean b = emailService.sendSimpleEmail(email);
        log.info("发送结果:{}", b);
    }

}

4.2、发送复杂邮件

@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class ComplexEmailTest {

    @Autowired
    private EmailService emailService;

    @Test
    public void sendComplexEmail() {
        List<String> receiverList = new ArrayList<>();
        receiverList.add("zhanglian@wtsd.cn");

        // 邮件对象
        Email email = new Email();
        email.setSender("wtsd_ywdl@163.com");
        email.setReceiverList(receiverList);
        email.setSubject("发送复杂邮件测试(带附件)");

        // 嵌入式资源
        Map<String, String> inlineMap = email.getInlineMap();
        inlineMap.put("CSDN1234", "C:\\myFile\\CSDN\\csdn.png");
        inlineMap.put("Alian1223", "C:\\myFile\\CSDN\\alian.png");
        email.setInlineMap(inlineMap);

        // 附件
        List<String> attachmentList = email.getAttachmentList();
        attachmentList.add("C:\\myFile\\CSDN\\csdn.png");
        attachmentList.add("C:\\myFile\\CSDN\\alian.png");
        email.setAttachmentList(attachmentList);

        // 设置模板数据
        Map<String, Object> data = new HashMap<>();
        data.put("title", "注册成功");
        data.put("website", "https://blog.csdn.net/Alian_1223");
        data.put("account", "alian");
        data.put("password", "Alian1234");
        boolean b = emailService.sendComplexEmail(email, "complex-email.ftl", data);
        log.info("发送结果:{}", b);
    }

}

4.3、效果图

在这里插入图片描述

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Boot 中集成 Freemarker 导出 Excel,可以通过以下步骤实现: 1. 首先,需要在 Spring Boot 中添加 Freemarker 和 Apache POI 依赖: ```xml <dependencies> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>${freemarker.version}</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>${apache.poi.version}</version> </dependency> </dependencies> ``` 2. 创建 Excel 模板文件,可以使用 Freemarker模板语法来定义表头和数据内容,同时在模板中可以使用 `img` 标签来引用图片,例如: ```html <html> <head> <title>Excel Template</title> </head> <body> <table> <thead> <tr> <th>Name</th> <th>Age</th> <th>Photo</th> </tr> </thead> <tbody> <#list users as user> <tr> <td>${user.name}</td> <td>${user.age}</td> <td><img src="${user.photo}" /></td> </tr> </#list> </tbody> </table> </body> </html> ``` 其中,`users` 是一个包含数据的列表,每个元素是一个包含 `name`、`age` 和 `photo` 属性的对象,`photo` 属性是图片的 URL。 3. 在 Spring Boot 中定义一个控制器,用于处理导出 Excel 的请求: ```java @Controller public class ExcelController { @Autowired private Configuration freemarkerConfig; @GetMapping("/export") public void exportExcel(HttpServletResponse response) throws Exception { // 读取 Excel 模板文件 Template template = freemarkerConfig.getTemplate("excel-template.ftl"); // 准备数据 List<User> users = prepareData(); // 创建 Excel 工作簿 Workbook workbook = new XSSFWorkbook(); // 渲染模板,生成 Excel 文件 Map<String, Object> model = new HashMap<>(); model.put("users", users); StringWriter out = new StringWriter(); template.process(model, out); InputStream is = new ByteArrayInputStream(out.toString().getBytes("UTF-8")); workbook = WorkbookFactory.create(is); // 设置响应头,告诉浏览器文件类型是 Excel response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setHeader("Content-disposition", "attachment; filename=users.xlsx"); // 将 Excel 文件写入响应流中 OutputStream os = response.getOutputStream(); workbook.write(os); os.flush(); os.close(); } private List<User> prepareData() { // TODO: 从数据库或其他来源读取数据 return Arrays.asList( new User("Alice", 25, "https://example.com/alice.jpg"), new User("Bob", 30, "https://example.com/bob.jpg"), new User("Charlie", 20, "https://example.com/charlie.jpg") ); } private static class User { private String name; private int age; private String photo; public User(String name, int age, String photo) { this.name = name; this.age = age; this.photo = photo; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getPhoto() { return photo; } public void setPhoto(String photo) { this.photo = photo; } } } ``` 在该控制器中,我们注入了 `Configuration` 类,它是 Freemarker 的配置类,用于加载 Excel 模板文件。在 `exportExcel` 方法中,我们先准备数据,然后通过 `template.process` 方法渲染模板,生成 Excel 文件。最后将 Excel 文件写入响应流中,浏览器会自动下载该文件。 4. 如果 Excel 模板中包含图片,那么需要在渲染模板之前,将图片下载到本地,然后将图片的本地路径传递给模板。例如: ```java private String downloadImage(String imageUrl) throws Exception { URL url = new URL(imageUrl); String fileName = url.getFile(); String filePath = "images/" + fileName.substring(fileName.lastIndexOf("/") + 1); FileUtils.copyURLToFile(url, new File(filePath)); return filePath; } @GetMapping("/export") public void exportExcel(HttpServletResponse response) throws Exception { // 读取 Excel 模板文件 Template template = freemarkerConfig.getTemplate("excel-template.ftl"); // 准备数据 List<User> users = prepareData(); // 下载图片并将本地路径传递给模板 for (User user : users) { String photoPath = downloadImage(user.getPhoto()); user.setPhoto(photoPath); } // 创建 Excel 工作簿 Workbook workbook = new XSSFWorkbook(); // 渲染模板,生成 Excel 文件 Map<String, Object> model = new HashMap<>(); model.put("users", users); StringWriter out = new StringWriter(); template.process(model, out); InputStream is = new ByteArrayInputStream(out.toString().getBytes("UTF-8")); workbook = WorkbookFactory.create(is); // 设置响应头,告诉浏览器文件类型是 Excel response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setHeader("Content-disposition", "attachment; filename=users.xlsx"); // 将 Excel 文件写入响应流中 OutputStream os = response.getOutputStream(); workbook.write(os); os.flush(); os.close(); } ``` 在该示例中,我们定义了一个 `downloadImage` 方法,用于下载图片,并将图片保存到 `images` 目录下。然后在 `exportExcel` 方法中,遍历用户列表,调用 `downloadImage` 方法下载每个用户的图片,并将本地路径传递给模板。注意,模板中使用的图片路径应该和下载到本地的路径一致。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值