如何用java发送包含表格形式的邮件
问题:
如何用java发送包含表格形式的邮件?
方法:
发用freemaker工具来替换html的表格变量,从而动态生成了html。然后再发送这个html格式(不能用纯文本)文本即可。
优化流程:
1、准备模板(这里是以Excel转html为模板)
2、处理模板及对模板填充内容的工具类
3、修改发送邮件的代码
所需依赖
pom.xml依赖
<!-- freemarker -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.4</version>
</dependency>
具体实现
1、准备模板
先用excel画一个模板,如下图
然后在网上找一个excel转换html的在线转换工具
我用的是:零代码 - Table在线布局工具(Excel转HTML)
2. 转换成功之后下载,之后会得到这样一个html文件。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>www.lingdaima.com(零代码excel转HTML)</title><style>
table{border-top:1px solid #333;border-left:1px solid #333;border-spacing:0;background-color:#fff;width:100%}
table td{border-bottom:1px solid #333;border-right:1px solid #333;font-size:13px;padding:5px}
.font5{color:windowtext;}
.xl65{text-align:left ;}
</style>
</head>
<body><table style="width:500pt"> <!--StartFragment--> <colgroup><col width="111" style="mso-width-source:userset;mso-width-alt:3552;width:83pt"> <col width="131" style="mso-width-source:userset;mso-width-alt:4192;width:98pt"> <col width="290" style="mso-width-source:userset;mso-width-alt:9280;width:218pt"> <col width="135" style="mso-width-source:userset;mso-width-alt:4320;width:101pt"> </colgroup><tbody><tr height="19"> <td class="xl65">号码</td> <td class="xl65">所属运营商</td> <td class="xl65">所属号码组</td> <td class="xl65">报警时间</td> </tr> <tr height="19"> <td class="xl65">15528474394</td> <td class="xl65">万事融通</td> <td class="xl65">智慧外呼_万事融通_日产专用_品牌认证</td> <td class="xl65">2024/10/24 9:35</td> </tr> <!--EndFragment--> </tbody>
</table>
</body>
</html>
3.把这个文件修改为 alert-email-template.ftl, 放到 java 项目的 resouce/ftl/目录下
该文件的内容为
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><style>
table{border-top:1px solid #333;border-left:1px solid #333;border-spacing:0;background-color:#fff;width:100%}
table td{border-bottom:1px solid #333;border-right:1px solid #333;font-size:13px;padding:5px}
.font5{color:windowtext;}
.xl65{text-align:left ;}
</style>
</head>
<body>
<div style="font-size: 12px; margin: 10px;">您好:<br/>      本次共有${totalAmounts}个未接通号码,详情请参见附件。<br/>下面列出的是前${topAmounts}个未接通号码详细信息。<br/>
</div><table style="width:500pt"> <!--StartFragment--><colgroup><col width="111" style="mso-width-source:userset;mso-width-alt:3552;width:83pt"><col width="131" style="mso-width-source:userset;mso-width-alt:4192;width:98pt"><col width="290" style="mso-width-source:userset;mso-width-alt:9280;width:218pt"><col width="135" style="mso-width-source:userset;mso-width-alt:4320;width:101pt"></colgroup><tbody><tr height="19"><td class="xl65">号码</td><td class="xl65">所属运营商</td><td class="xl65">所属号码组</td><td class="xl65">报警时间</td></tr><#if items?? && (items? size >0)><#list items as item><tr height="18"><td class="xl66">${(item.phone)!}</td><td class="xl66">${(item.supplier)!}</td><td class="xl66">${(item.group)!}</td><td class="xl66">${(item.time)!}</td></tr></#list></#if></tbody>
</table>
</body>
</html>
这里带$的就是需要freemaker来替换的变量。
注:这里items是一个list变量。
- 处理模板及对模板填充内容的工具类
直接上java代码
@Data
public class FailedCallInfo {private String phone;private String supplier;private String group;private String time;
}
List<FailedCallInfo> items = new ArrayList<>();FailedCallInfo record = new FailedCallInfo();record.setPhone("123456");record.setSupplier("abc");record.setGroup("group1");record.setTime("2024-10-10 12:12:00");items.add(record);Map<String, Object> callInfoMap = new HashMap<>(32);//注意变量名字要和ftl模板里面的一致!callInfoMap.put("items", items);callInfoMap.put("totalAmounts", 1);callInfoMap.put("topAmounts", 1);String generatedEmailBodyText = null;try {generatedEmailBodyText = FreemarkerUtil.generate("alert-email-template.ftl", callInfoMap);} catch (IOException e) {log.warn("email template generation failed, IOException exception:{}", e.getMessage());csvfile.delete();return;} catch (TemplateException e) {log.warn("email template generation failed, TemplateException exception:{}", e.getMessage());csvfile.delete();return;}boolean send_result = EmailHelper.send(receivers, null, null, "未接通号码告警邮件", generatedEmailBodyText, attachment, "xc-jsb-sjjg-unit@autohome.com.cn");if (send_result) {log.info("发送告警邮件成功!收件人:{}", JSON.toJSONString(receivers));} else {log.warn("发送告警邮件失败,收件人:{}", JSON.toJSONString(receivers));}
FreemarkerUtil.java
package com.xxx.util;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;import java.io.BufferedWriter;
import java.io.IOException;
import java.io.StringWriter;/*** freemarker 工具类*/
public class FreemarkerUtil {private static Configuration config = null;/*** Static initialization.** Initialize the configuration of Freemarker.*/static {config = new Configuration();config.setClassForTemplateLoading(FreemarkerUtil.class, "/ftl/");config.setTemplateUpdateDelay(0);}public static Configuration getConfiguration() {return config;}/*** @param template* @return* @throws Exception*/public static String generate(String template, Object obj) throws IOException, TemplateException {Configuration config = getConfiguration();config.setDefaultEncoding("UTF-8");Template tp = config.getTemplate(template, "utf-8");StringWriter stringWriter = new StringWriter();BufferedWriter writer = new BufferedWriter(stringWriter);tp.setEncoding("UTF-8");tp.process(obj, writer);String htmlStr = stringWriter.toString();writer.flush();writer.close();return htmlStr;}}
EmailHelper.java
package com.xxx.util;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;@Slf4j
public class EmailHelper {private static final String DEFAULT_SMTP_HOST = "12.12.12.12";private static final Integer DEFAULT_SMTP_PORT = 25;private static final String DEFAULT_MAIL_SENDER = "test@aby.com.cn";/*** 邮件发送** @param to 收件人邮件地址列表* @param subject 邮件标题* @param content 邮件内容* @return 发送结果*/public static boolean send(List<String> to, String subject, String content) {return send(to, null, null, subject, content, null);}/*** 邮件发送** @param to 收件人邮件地址列表* @param subject 邮件标题* @param content 邮件内容* @param attachments 附件* @return 发送结果*/public static boolean send(List<String> to, String subject, String content, List<File> attachments) {return send(to, null, null, subject, content, attachments);}/*** 邮件发送** @param to 收件人邮件地址列表* @param cc 抄送人邮件地址列表* @param subject 邮件标题* @param content 邮件内容* @param attachments 附件* @return 发送结果*/public static boolean send(List<String> to, List<String> cc, String subject, String content, List<File> attachments) {return send(to, cc, null, subject, content, attachments);}/*** 邮件发送** @param to 收件人邮件地址列表* @param cc 抄送人邮件地址列表* @param bcc 密送人邮件地址列表* @param subject 邮件标题* @param content 邮件内容* @param attachments 附件* @param sender 发件人* @return 发送结果*/public static boolean send(List<String> to, List<String> cc, List<String> bcc,String subject, String content, List<File> attachments,String sender) {return send(DEFAULT_SMTP_HOST, DEFAULT_SMTP_PORT, sender, null, to, cc, bcc, subject, content, attachments);}/*** 邮件发送** @param to 收件人邮件地址列表* @param cc 抄送人邮件地址列表* @param bcc 密送人邮件地址列表* @param subject 邮件标题* @param content 邮件内容* @param attachments 附件* @return 发送结果*/public static boolean send(List<String> to, List<String> cc, List<String> bcc,String subject, String content, List<File> attachments) {return send(DEFAULT_SMTP_HOST, DEFAULT_SMTP_PORT, DEFAULT_MAIL_SENDER, null, to, cc, bcc, subject, content, attachments);}/*** 邮件发送** @param smtpHost smtp服务地址* @param smtpPort smtp服务端口* @param sender 发送人邮箱地址* @param senderPassword 发送人邮箱密码* @param to 收件人邮件地址列表* @param cc 抄送人邮件地址列表* @param bcc 密送人邮件地址列表* @param subject 邮件标题* @param content 邮件内容* @param attachments 附件* @return 发送结果*/public static boolean send(String smtpHost, Integer smtpPort, String sender, String senderPassword,List<String> to, List<String> cc, List<String> bcc,String subject, String content, List<File> attachments) {MimeMessage message = getMimeMessage(smtpHost, smtpPort, sender, senderPassword);try {// 收件人List<InternetAddress> toInternetAddressList = convertToInternetAddress(to);if (CollectionUtils.isEmpty(toInternetAddressList)) {log.error("邮件收件人不能为空");return false;}message.addRecipients(Message.RecipientType.TO, toInternetAddressList.toArray(new InternetAddress[0]));// 抄送人List<InternetAddress> ccInternetAddressList = convertToInternetAddress(cc);if (!CollectionUtils.isEmpty(ccInternetAddressList)) {message.addRecipients(Message.RecipientType.CC, ccInternetAddressList.toArray(new InternetAddress[0]));}// 密送人List<InternetAddress> bccInternetAddressList = convertToInternetAddress(bcc);if (!CollectionUtils.isEmpty(bccInternetAddressList)) {message.addRecipients(Message.RecipientType.BCC, bccInternetAddressList.toArray(new InternetAddress[0]));}// 内容Multipart multipart = new MimeMultipart();MimeBodyPart contentPart = new MimeBodyPart();contentPart.setContent(content, "text/html;charset=utf-8");multipart.addBodyPart(contentPart);// 附件if (!CollectionUtils.isEmpty(attachments)) {attachments.forEach(file -> {try {MimeBodyPart filePartBody = new MimeBodyPart();DataSource fileDataSource = new FileDataSource(file);DataHandler dataHandler = new DataHandler(fileDataSource);filePartBody.setDataHandler(dataHandler);filePartBody.setFileName(MimeUtility.encodeText(file.getName()));multipart.addBodyPart(filePartBody);} catch (Exception e) {log.error("邮件中的附件失败,已经丢弃该附件:{}", file.getName());}});}message.setContent(multipart);message.setFrom(new InternetAddress(sender));String mailSubject = StringUtils.isNotBlank(subject) ? subject : "无主题";String activeProfile = System.getProperty("spring.profiles.active");if (StringUtils.isNotBlank(activeProfile) && !activeProfile.contains("online")) {mailSubject += String.format(" 【环境: %s】", activeProfile);}message.setSubject(mailSubject, "UTF-8");message.saveChanges();Transport.send(message);return true;} catch (MessagingException ex) {log.error("error when send email.", ex);return false;}}private static MimeMessage getMimeMessage(String smtpHost, Integer smtpPort, String sender, String senderPassword) {Properties properties = new Properties();properties.put("mail.transport.protocol", "smtp");properties.put("mail.smtp.host", smtpHost);properties.put("mail.smtp.port", smtpPort);Authenticator authenticator = null;if (StringUtils.isNotBlank(sender) && StringUtils.isNotBlank(senderPassword)) {authenticator = new Authenticator() {@Overrideprotected PasswordAuthentication getPasswordAuthentication() {return new PasswordAuthentication(sender, senderPassword);}};}properties.put("mail.smtp.auth", authenticator != null ? "true" : "false");Session session = Session.getDefaultInstance(properties, authenticator);return new MimeMessage(session);}private static List<InternetAddress> convertToInternetAddress(List<String> mailAddressList) {if (CollectionUtils.isEmpty(mailAddressList)) {return new ArrayList<>();}List<InternetAddress> mailInternetAddress = new ArrayList<>();mailAddressList.forEach(item -> {try {mailInternetAddress.add(new InternetAddress(item));} catch (AddressException e) {log.error("邮件地址错误,此邮件地址不参与邮件的接收:{}", item);}});return mailInternetAddress;}
}
相关文章:

如何用java发送包含表格形式的邮件
问题: 如何用java发送包含表格形式的邮件? 方法: 发用freemaker工具来替换html的表格变量,从而动态生成了html。然后再发送这个html格式(不能用纯文本)文本即可。 优化流程: 1、准备模板&#x…...
讲个故事:关于一次接口性能优化的心里路程
这是一个程序猿写的第一个故事,请各位懂行的客官静下心来,慢慢品读。就知道我为什么要单独写一个文章来记录这次过程了,因为实在是太坎坷了...... 背景介绍 近期项目投产时遇到一个问题,投产后在验证时发现大部分用户系统登…...
Centos7升级到openssh9.9
openssh9.9 是2024.9.20出的最新版ssh。因为客户扫描出一大堆centos7的漏洞,全是这个openssh的,好多补丁,所以索性升级到最新版。 需要自己制作rpm包,这个我是不懂,照这个来: Linux服务器升级openssh9.9最…...

使用 STM32F407 串口实现 485 通信
准备工作 了解485通信基本概念与原理:RS485通信详解_485通讯de接什么口-CSDN博客 安装编译软件:keil uVision 5.6 软件资料:STM32CubeF4 固件包,正点原子RS485通信例程 参考视频:第26讲 基础篇-新建H…...

基于NERF技术重建学习笔记
NeRF(Neural Radiance Fields)是一种用于3D场景重建的神经网络模型,能够从2D图像生成逼真的3D渲染效果。它将场景表征为一个连续的5D函数,利用了体积渲染和神经网络的结合,通过学习光线穿过空间时的颜色和密度来重建场…...
webView 支持全屏播放
webView 支持全屏播放 直接上代码 public class CustomFullScreenWebViewClient extends WebChromeClient {WebView webView;Context context;/*** 视频全屏参数*/protected static final FrameLayout.LayoutParams COVER_SCREEN_PARAMS new FrameLayout.LayoutParams(ViewG…...

QGIS之三十二DEM地形导出三维模型gltf
效果 1、准备数据 (1)dem.tif (2)dom.tif 2、qgis加载dem和dom数据 3、安装插件 插件步骤可以参考这篇文章 QGIS之二十四安装插件 安装了Qgis2threejs插件,结果...

【python爬虫】携程旅行景点游客数据分析与可视化
一.选题背景 随着旅游业的快速发展,越来越多的人选择通过互联网平台预订旅行产品,其中携程网作为国内领先的在线旅行服务提供商,拥有大量的旅游产品和用户数据。利用爬虫技术可以获取携程网上各个景点的游客数据,包括游客数量、游…...

python实现onvif协议下控制摄像头变焦,以及融合人形识别与跟踪控制
#1024程序员节 | 征文# 这两天才因为项目需要,对网络摄像头的视频采集以及实现人形识别与跟踪技术。对于onvif协议自然起先也没有任何的了解。但是购买的摄像头是SONY网络头是用在其他地方的。因为前期支持探究项目解决方案,就直接拿来做demo测试使用。 …...

【Vue】Vue3.0(十四)接口,泛型和自定义类型的概念及使用
上篇文章: 【Vue】Vue3.0(十三)中标签属性ref(加在普通标签上、加在组件标签上)、局部样式 🏡作者主页:点击! 🤖Vue专栏:点击! ⏰️创作时间&…...

【C++】红黑树万字详解(一文彻底搞懂红黑树的底层逻辑)
目录 00.引入 01.红黑树的性质 02.红黑树的定义 03.红黑树的插入 1.按照二叉搜索树的规则插入新节点 2.检测新节点插入后,是否满足红黑树的性质 1.uncle节点存在且为红色 2.uncle节点不存在 3.uncle节点存在且为黑色 04.验证红黑树 00.引入 和AVL树一样&am…...
开源FluentFTP实操,操控FTP文件
概述:通过FluentFTP库,轻松在.NET中实现FTP功能。支持判断、创建、删除文件夹,判断文件是否存在,实现上传、下载和删除文件。简便而强大的FTP操作,提升文件传输效率。 在.NET中,使用FluentFTP库可以方便地…...

论文解读 | ECCV2024 AutoEval-Video:一个用于评估大型视觉-语言模型在开放式视频问答中的自动基准测试...
点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入! 点击 阅读原文 观看作者讲解回放! 作者简介 陈修元,上海交通大学清源研究院硕士生 概述 总结来说,我们提出了一个新颖且具有挑战性的基准测试AutoEvalVideo,用于全…...

postgresql14主从同步流复制搭建
1. 如果使用docker搭建请移步 Docker 启动 PostgreSQL 主从架构:实现数据同步的高效部署指南_docker安装postgresql主从同步-CSDN博客 2. 背景 pgsql版本:PostgreSQL 14.13 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4…...

企业信息化管理中的数据集成方案:销售出库单对接
企业信息化管理中的数据集成方案:销售出库单对接 销售出库单旺店通→金蝶:高效数据集成案例分享 在企业信息化管理中,数据的高效流动和准确对接是实现业务流程自动化的关键。本文将聚焦于一个具体的系统对接集成案例:如何将旺店通…...

3.cpp基本数据类型
cpp基本数据类型 1.cpp基本数据类型 1.cpp基本数据类型 C基本数据类型和C语言的基本数据类型差不多 注意bool类型:存储真值 true 或假值 false,C语言编译器C99以上支持。 C语言的bool类型:要添加 #include <stdbool.h>头文件 #includ…...

MCK主机加固与防漏扫的深度解析
在当今这个信息化飞速发展的时代,网络安全成为了企业不可忽视的重要议题。漏洞扫描,简称漏扫,是一种旨在发现计算机系统、网络或应用程序中潜在安全漏洞的技术手段。通过自动化工具,漏扫能够识别出系统中存在的已知漏洞࿰…...

《软件估算之原始功能点:精准度量软件规模的关键》
《软件估算之原始功能点:精准度量软件规模的关键》 一、软件估算的重要性与方法概述二、原始功能点的构成要素(一)数据功能(二)事务功能 三、原始功能点的估算方法(一)功能点分类估算࿰…...
序列化与反序列化
序列化和反序列化是数据处理中的两个重要概念,它们在多种场景下都非常有用,尤其是在分布式系统、网络通信、持久化存储等方面。下面是对这两个概念的详细解释: 序列化(Serialization) 定义:序列化是将对象…...

安装nginx实现多ip访问多网站
[rootlocalhost ~]# systemctl stop firewalld 关防火墙 [rootlocalhost ~]# setenforce 0 关selinux [rootlocalhost ~]# mount /dev/sr0 /mnt 挂载点 [rootlocalhost ~]# dnf install nginx -y 安装nginx [rootlocalhost ~]# nmtui 当前主机添加多地址 [rootlocal…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...

多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...

20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...

Ubuntu Cursor升级成v1.0
0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开,快捷键也不好用,当看到 Cursor 升级后,还是蛮高兴的 1. 下载 Cursor 下载地址:https://www.cursor.com/cn/downloads 点击下载 Linux (x64) ,…...

android RelativeLayout布局
<?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:gravity&…...

破解路内监管盲区:免布线低位视频桩重塑停车管理新标准
城市路内停车管理常因行道树遮挡、高位设备盲区等问题,导致车牌识别率低、逃费率高,传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法,正成为破局关键。该设备安装于车位侧方0.5-0.7米高度,直接规避树枝遮…...