一、创建Word模板
java
{{company}}{{Date}}服务器运行情况报告
一、服务器:总告警次数:{{ServerTotal}}
服务器IP:{{IPA}},总共告警次数:{{ServerATotal}}
服务器IP:{{IPB}},总共告警次数:{{ServerBTotal}}
服务器IP:{{IPC}},总共告警次数:{{ServerCTotal}}
二、应用程序:总告警次数{{ApplicationTotal}}
服务器IP:{{IPA}},包含{{ApplicationAName}}应用程序服务,总共告警次数:{{ApplicationATotal}}
服务器IP:{{IPB}},包含{{ApplicationBName}}应用程序服务,总共告警次数:{{ApplicationBTotal}}
服务器IP:{{IPC}},包含{{ApplicationCName}}应用程序服务,总共告警次数:{{ApplicationCTotal}}
三、数据库:总告警次数{{DBTotal}}
服务器IP:{{IPA}},包含{{DBAName}}数据库,总共告警次数:{{DBATotal}}
服务器IP:{{IPB}},包含{{DBBName}}数据库,总共告警次数:{{DBBTotal}}
服务器IP:{{IPC}},包含{{DBCName}}数据库,总共告警次数:{{DBCTotal}}
四、中间件:总告警次数{{MiddleWareTotal}}
服务器IP:{{IPA}},包含{{MiddleWareAName}}中间件,总共告警次数:{{MiddleWareATotal}}
服务器IP:{{IPB}},包含{{MiddleWareBName}}中间件,总共告警次数:{{MiddleWareBTotal}}
服务器IP:{{IPC}},包含{{MiddleWareCName}}中间件,总共告警次数:{{MiddleWareCTotal}}
五、接口:总告警次数{{InterfaceTotal}}
服务器IP:{{IPA}},包含{{InterfaceAName}}接口服务,总共告警次数:{{InterfaceATotal}}
服务器IP:{{IPB}},包含{{InterfaceBName}}接口服务,总共告警次数:{{InterfaceBTotal}}
服务器IP:{{IPC}},包含{{InterfaceCName}}接口服务,总共告警次数:{{InterfaceCTotal}}
将上述内容存入一个docx文件中,放到resources文件夹下
二、编写代码
java
package com.gitee.pifeng.monitoring.server.business.server.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.gitee.pifeng.monitoring.server.business.server.dao.*;
import com.gitee.pifeng.monitoring.server.business.server.entity.*;
import com.gitee.pifeng.monitoring.server.business.server.service.IAlarmService;
import com.gitee.pifeng.monitoring.server.config.WordTemplateConfig;
import com.gitee.pifeng.monitoring.server.util.db.SelectEmailsService;
import org.apache.poi.util.StringUtil;
import org.apache.poi.xwpf.usermodel.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.thymeleaf.util.StringUtils;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
/**
* description 导出word版本的报表
*
* @author yhj
* @date 2025-01-06 17:08:15
*/
@RequestMapping("/ExportWord")
@RestController
public class ExportWordReportController {
@Autowired
private WordTemplateConfig wordTemplateConfig;
/**
* 告警服务接口
*/
@Autowired
private IAlarmService alarmService;
@Autowired
private IMonitorInstanceDao monitorInstanceDao;
@Autowired
private IMonitorAlarmRecordDao monitorAlarmRecordDao;
@Autowired
private IMonitorDbDao monitorDbDao;
@Autowired
private IMonitorTcpDao monitorTcpDao;
@Autowired
private IMonitorHttpDao monitorHttpDao;
@Autowired
private SelectEmailsService emailsService;
public void exportWordTemplate(HttpServletResponse response, Map<String, String> data, String filename) {
String companyName = emailsService.selectCompanyName();
data.put("company", companyName);
try {
String filePath = URLDecoder.decode(getClass().getClassLoader().getResource("templates/static/WordReportTemplete.docx").getPath(), "UTF-8");
// 读取Word模板
FileInputStream fis = new FileInputStream(filePath);
XWPFDocument document = new XWPFDocument(fis);
// 将填充后的文档写入响应输出流
wordTemplateConfig.replaceTextInDocument(document, data);
// 设置响应头,指定下载文件类型和名称
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + filename + ".docx");
document.write(response.getOutputStream());
// 关闭资源
document.close();
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private void exportEmptyWordTemplate(HttpServletResponse response, Map<String, String> data) {
String companyName = emailsService.selectCompanyName();
String filename = URLEncoder.encode(String.valueOf(new StringBuilder(companyName).append("现场暂无数据"))).replaceAll("\\+", "%20");
data.put("company", companyName);
try {
String filePath = URLDecoder.decode(getClass().getClassLoader().getResource("templates/static/EmptyWordReportTemplete.docx").getPath(), "UTF-8");
// 读取Word模板
FileInputStream fis = new FileInputStream(filePath);
XWPFDocument document = new XWPFDocument(fis);
// 将填充后的文档写入响应输出流
wordTemplateConfig.replaceTextInDocument(document, data);
// 设置响应头,指定下载文件类型和名称
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + filename + ".docx");
document.write(response.getOutputStream());
// 关闭资源
document.close();
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 本日服务器报错信息word导出
*
* @param response
*/
@GetMapping("/getThisDayWordReport")
public void getDayWordReport(HttpServletResponse response) {
String companyName = emailsService.selectCompanyName();
Map<String, String> data = new HashMap<>();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date date = new Date();
String TodayDay = simpleDateFormat.format(date);
data.put("Date", TodayDay);
Integer flag = 0; // 0代表今日
//文件名,以下方法用于防止中文编码错误
String name = String.valueOf(new StringBuilder(companyName).append(TodayDay).append("服务器报错信息"));
String filename = URLEncoder.encode(name).replaceAll("\\+", "%20");
thisWordInfo(response, flag, TodayDay, data, filename);
}
// 将导出的信息填充给word
private void thisWordInfo(HttpServletResponse response, Integer flag, String Date, Map<String, String> data, String filename) {
// 查询所有IP
List<String> IpList = getIpList();
if (IpList.size() == 0) {
exportEmptyWordTemplate(response, data);
} else {
List<ExportWordReport> listA = new ArrayList<>();
if (!IpList.get(0).isEmpty()) {
String IPA = IpList.get(0);
if (flag == 0) {
// 当日报错个数
listA = alarmService.selectWordDayAlarmTotal(IPA, Date);
} else if (flag == 1) {
// 本周报错个数
listA = alarmService.selectWordWeekAlarmTotal(IPA, Date);
} else if (flag == 2) {
// 本月报错个数
listA = alarmService.selectWordMonthAlarmTotal(IPA, Date);
} else if (flag == 3) {
// 本年报错个数
listA = alarmService.selectWordYearAlarmTotal(IPA, Date);
}
// 汇总各项报错个数
Map<String, Integer> mapA = listA.stream().collect(Collectors.toMap(ExportWordReport::getName, ExportWordReport::getTotal));
// 查询该IP下所有应用程序的名称
String applicationName = getApplicationNameList(IPA);
// 查询该IP下所有数据库名字
String DBName = getDBNameList(IPA);
// 查询该IP下所有中间件名字
String MiddleWareName = getMiddleNameList(IPA);
// 查询该IP下所有接口名称
String InterfaceName = getInterfaceNameList(IPA);
// 填充word里面的字段
data.put("IPA", IPA);
data.put("ApplicationAName", !applicationName.isEmpty() ? applicationName : "0个");
data.put("DBAName", !DBName.isEmpty() ? DBName : "0个");
data.put("MiddleWareAName", !MiddleWareName.isEmpty() ? MiddleWareName : "0个");
data.put("InterfaceAName", !InterfaceName.isEmpty() ? InterfaceName : "0个");
// 服务器
data.put("ServerATotal", mapA.get("SERVER") != null ? String.valueOf(mapA.get("SERVER")) : "0");
// 应用
data.put("ApplicationATotal", mapA.get("INSTANCE") != null ? String.valueOf(mapA.get("INSTANCE")) : "0");
// 数据库
data.put("DBATotal", mapA.get("DATABASE") != null ? String.valueOf(mapA.get("DATABASE")) : "0");
// 中间件
data.put("MiddleWareATotal", mapA.get("TCP4SERVICE") != null ? String.valueOf(mapA.get("TCP4SERVICE")) : "0");
// 接口
data.put("InterfaceATotal", mapA.get("HTTP4SERVICE") != null ? String.valueOf(mapA.get("HTTP4SERVICE")) : "0");
}
if (!IpList.get(1).isEmpty()) {
List<ExportWordReport> listB = new ArrayList<>();
String IPB = IpList.get(1);
if (flag == 0) {
listB = alarmService.selectWordDayAlarmTotal(IPB, Date);
} else if (flag == 1) {
// 本周报错信息
listB = alarmService.selectWordWeekAlarmTotal(IPB, Date);
} else if (flag == 2) {
// 本月报错个数
listB = alarmService.selectWordMonthAlarmTotal(IPB, Date);
} else if (flag == 3) {
// 本年报错个数
listB = alarmService.selectWordYearAlarmTotal(IPB, Date);
}
// 汇总各项报错个数
Map<String, Integer> mapB = listB.stream().collect(Collectors.toMap(ExportWordReport::getName, ExportWordReport::getTotal));
// 查询该IP下所有应用程序的名称
String applicationName = getApplicationNameList(IPB);
// 查询该IP下所有数据库名字
String DBName = getDBNameList(IPB);
// 查询该IP下所有中间件名字
String MiddleWareName = getMiddleNameList(IPB);
// 查询该IP下所有接口名称
String InterfaceName = getInterfaceNameList(IPB);
data.put("IPB", IPB);
data.put("ApplicationBName", !applicationName.isEmpty() ? applicationName : "0个");
data.put("DBBName", !DBName.isEmpty() ? DBName : "0个");
data.put("MiddleWareBName", !MiddleWareName.isEmpty() ? MiddleWareName : "0个");
data.put("InterfaceBName", !InterfaceName.isEmpty() ? InterfaceName : "0个");
// 服务器
data.put("ServerBTotal", mapB.get("SERVER") != null ? String.valueOf(mapB.get("SERVER")) : "0");
// 应用
data.put("ApplicationBTotal", mapB.get("INSTANCE") != null ? String.valueOf(mapB.get("INSTANCE")) : "0");
// 数据库
data.put("DBBTotal", mapB.get("DATABASE") != null ? String.valueOf(mapB.get("DATABASE")) : "0");
// 中间件
data.put("MiddleWareBTotal", mapB.get("TCP4SERVICE") != null ? String.valueOf(mapB.get("TCP4SERVICE")) : "0");
// 接口
data.put("InterfaceBTotal", mapB.get("HTTP4SERVICE") != null ? String.valueOf(mapB.get("HTTP4SERVICE")) : "0");
}
if (!IpList.get(2).isEmpty()) {
List<ExportWordReport> listC = new ArrayList<>();
String IPC = IpList.get(2);
if (flag == 0) {
listC = alarmService.selectWordDayAlarmTotal(IPC, Date);
} else if (flag == 1) {
// 本周报错个数
listC = alarmService.selectWordWeekAlarmTotal(IPC, Date);
} else if (flag == 2) {
// 本月报错个数
listC = alarmService.selectWordMonthAlarmTotal(IPC, Date);
} else if (flag == 3) {
// 本年报错个数
listC = alarmService.selectWordYearAlarmTotal(IPC, Date);
}
// 汇总各项报错个数
Map<String, Integer> mapC = listC.stream().collect(Collectors.toMap(ExportWordReport::getName, ExportWordReport::getTotal));
// 查询该IP下所有应用程序的名称
String applicationName = getApplicationNameList(IPC);
// 查询该IP下所有数据库名字
String DBName = getDBNameList(IPC);
// 查询该IP下所有中间件名字
String MiddleWareName = getMiddleNameList(IPC);
// 查询该IP下所有接口名称
String InterfaceName = getInterfaceNameList(IPC);
data.put("IPC", IPC);
data.put("ApplicationCName", !applicationName.isEmpty() ? applicationName : "0个");
data.put("DBCName", DBName);
data.put("MiddleWareCName", !MiddleWareName.isEmpty() ? MiddleWareName : "0个");
data.put("InterfaceCName", !InterfaceName.isEmpty() ? InterfaceName : "0个");
// 服务器
data.put("ServerCTotal", mapC.get("SERVER") != null ? String.valueOf(mapC.get("SERVER")) : "0");
// 应用
data.put("ApplicationCTotal", mapC.get("INSTANCE") != null ? String.valueOf(mapC.get("INSTANCE")) : "0");
// 数据库
data.put("DBCTotal", mapC.get("DATABASE") != null ? String.valueOf(mapC.get("DATABASE")) : "0");
// 中间件
data.put("MiddleWareCTotal", mapC.get("TCP4SERVICE") != null ? String.valueOf(mapC.get("TCP4SERVICE")) : "0");
// 接口
data.put("InterfaceCTotal", mapC.get("HTTP4SERVICE") != null ? String.valueOf(mapC.get("HTTP4SERVICE")) : "0");
}
// 所有服务器报错信息条数总和
int ServerTotal = Integer.parseInt(data.get("ServerATotal")) + Integer.parseInt(data.get("ServerBTotal")) + Integer.parseInt(data.get("ServerCTotal"));
data.put("ServerTotal", String.valueOf(ServerTotal));
// 所有应用程序报错信息总和
int ApplicationTotal = Integer.parseInt(data.get("ApplicationATotal")) + Integer.parseInt(data.get("ApplicationBTotal")) + Integer.parseInt(data.get("ApplicationCTotal"));
data.put("ApplicationTotal", String.valueOf(ApplicationTotal));
//所有数据库报错信息总和
int DBTotal = Integer.parseInt(data.get("DBATotal")) + Integer.parseInt(data.get("DBBTotal")) + Integer.parseInt(data.get("DBCTotal"));
data.put("DBTotal", String.valueOf(DBTotal));
//所有中间件报错信息总和
int MiddleWareTotal = Integer.parseInt(data.get("MiddleWareATotal")) + Integer.parseInt(data.get("MiddleWareBTotal")) + Integer.parseInt(data.get("MiddleWareCTotal"));
data.put("MiddleWareTotal", String.valueOf(MiddleWareTotal));
//所有接口报错信息总和
int InterfaceTotal = Integer.parseInt(data.get("InterfaceATotal")) + Integer.parseInt(data.get("InterfaceBTotal")) + Integer.parseInt(data.get("InterfaceCTotal"));
data.put("InterfaceTotal", String.valueOf(InterfaceTotal));
exportWordTemplate(response, data, filename);
}
}
// 查询所有接口名称
private String getInterfaceNameList(String IP) {
LambdaQueryWrapper<MonitorHttp> queryInterfaceNameWrapper = new LambdaQueryWrapper<>();
queryInterfaceNameWrapper.select(MonitorHttp::getDescr)
.ne(MonitorHttp::getDescr,null)
.ne(MonitorHttp::getDescr,"")
.eq(MonitorHttp::getHostnameSource, IP)
.groupBy(MonitorHttp::getDescr);
List<String> InterfaceList = monitorHttpDao.selectList(queryInterfaceNameWrapper)
.stream()
.filter(item -> item!=null)
.map(MonitorHttp::getDescr)
.collect(Collectors.toList());
String InterfaceName = InterfaceList.stream().map(String::toString).collect(Collectors.joining("、"));
return InterfaceName;
}
// 查询所有IP
private List<String> getIpList() {
LambdaQueryWrapper<MonitorAlarmRecord> queryIPWrapper = new LambdaQueryWrapper<>();
queryIPWrapper.select(MonitorAlarmRecord::getIp).groupBy(MonitorAlarmRecord::getIp);
List<String> IpList = monitorAlarmRecordDao.selectList(queryIPWrapper)
.stream()
.filter(item -> item != null)
.map(MonitorAlarmRecord::getIp)
.collect(Collectors.toList());
return IpList;
}
// 查询该IP下所有中间件的名称
private String getMiddleNameList(String IP) {
LambdaQueryWrapper<MonitorTcp> queryMiddleNameWrapper = new LambdaQueryWrapper<>();
queryMiddleNameWrapper.select(MonitorTcp::getDescr)
.ne(MonitorTcp::getDescr,"")
.ne(MonitorTcp::getDescr,null)
.eq(MonitorTcp::getHostnameSource, IP)
.groupBy(MonitorTcp::getDescr);
List<String> MiddleWareList = monitorTcpDao.selectList(queryMiddleNameWrapper)
.stream()
.filter(item -> item != null)
.map(MonitorTcp::getDescr)
.collect(Collectors.toList());
String MiddleWareName = MiddleWareList.stream().map(String::toString).collect(Collectors.joining("、"));
return MiddleWareName;
}
// 查询该IP下所有应用程序的名称
private String getApplicationNameList(String IP) {
// 查询该IP下所有应用程序的名称
LambdaQueryWrapper<MonitorInstance> queryInstanceNameWrapper = new LambdaQueryWrapper<>();
queryInstanceNameWrapper.select(MonitorInstance::getInstanceName) // 指定查询字段
.ne(MonitorInstance::getInstanceName,"")
.ne(MonitorInstance::getInstanceName,null)
.eq(MonitorInstance::getIp, IP) // 添加 IP 条件
.groupBy(MonitorInstance::getInstanceName); // 去重(相当于 DISTINCT)
List<String> ApplicationNameList = monitorInstanceDao.selectList(queryInstanceNameWrapper)
.stream()
.map(MonitorInstance::getInstanceName) // 提取字段值
.distinct().collect(Collectors.toList());// 确保去重
String applicationName = ApplicationNameList.stream().map(String::toString).collect(Collectors.joining("、"));
return applicationName;
}
// 查询所有数据库名字公共方法
private String getDBNameList(String IP) {
LambdaQueryWrapper<MonitorDb> queryDBListWrapper = new LambdaQueryWrapper<>();
queryDBListWrapper.select(MonitorDb::getDbType)
.ne(MonitorDb::getDbType,"")
.ne(MonitorDb::getDbType,null)
.eq(MonitorDb::getIp, IP)
.groupBy(MonitorDb::getDbType);
List<String> DBList = monitorDbDao.selectList(queryDBListWrapper).stream().filter(item -> item != null).map(MonitorDb::getDbType).collect(Collectors.toList());
String DBName = DBList.stream().map(String::toString).collect(Collectors.joining("、"));
return DBName;
}
}
工具类,主要读取信息并写入word模板内
java
package com.gitee.pifeng.monitoring.server.config;
import org.apache.poi.xwpf.usermodel.*;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
@Component
public class WordTemplateConfig {
public void replaceTextInDocument(XWPFDocument document, Map<String, String> data) {
// 替换段落中的占位符
for (XWPFParagraph paragraph : document.getParagraphs()) {
StringBuilder paragraphText = new StringBuilder();
List<XWPFRun> runs = paragraph.getRuns();
if (runs != null) {
// 收集段落中的所有文本
for (XWPFRun run : runs) {
paragraphText.append(run.getText(0) != null ? run.getText(0) : "");
}
// 替换占位符
String replacedText = paragraphText.toString();
for (Map.Entry<String, String> entry : data.entrySet()) {
replacedText = replacedText.replace("{{" + entry.getKey() + "}}", entry.getValue() != null ? entry.getValue() : "");
}
// 清空原有 runs
for (int i = runs.size() - 1; i >= 0; i--) {
paragraph.removeRun(i);
}
// 重新设置替换后的文本
XWPFRun newRun = paragraph.createRun();
newRun.setText(replacedText);
}
}
// 替换表格中的占位符
for (XWPFTable table : document.getTables()) {
for (XWPFTableRow row : table.getRows()) {
for (XWPFTableCell cell : row.getTableCells()) {
for (XWPFParagraph paragraph : cell.getParagraphs()) {
for (XWPFRun run : paragraph.getRuns()) {
String text = run.getText(0);
if (text != null) {
for (Map.Entry<String, String> entry : data.entrySet()) {
if (text.contains("{{" + entry.getKey() + "}}")) {
text = text.replace("{{" + entry.getKey() + "}}", entry.getValue());
run.setText(text, 0);
}
}
}
}
}
}
}
}
}
}
三、发送邮箱
java
@RequestMapping("/SendWord")
@RestController
public class SendWordReportController {
@Autowired
private EmailSender emailSender;
@Autowired
private SelectEmailsService emailsService;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String date = simpleDateFormat.format(new Date());
/**
* 发送日报表
*/
@PostMapping("/sendDayWordReport")
public void sendDayWordReport() {
List<String> emails = emailsService.findEmail();
String companyName = emailsService.selectCompanyName();
String name = companyName+new StringBuilder(date).append("日服务器报错信息汇总");
try {
// 调用接口下载 Excel 文件
String apiUrl = "http://localhost:16000/phoenix-server/ExportWord/getThisDayWordReport";
File wordFile = FileDownloader.downloadWordFile(apiUrl);
// 接收邮件的邮箱
for (String email: emails) {
emailSender.sendEmailWithAttachment(email,wordFile,name);
}
// emailSender.sendEmailWithAttachment(toEmail,wordFile,name);
} catch (IOException | MessagingException e) {
e.printStackTrace();
}
}
}
}
java
package com.gitee.pifeng.monitoring.server.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import java.io.File;
import java.io.IOException;
import java.util.Properties;
/**
* description 发送报表数据
*
* @author yhj
* @date 2025-01-02 15:39:08
*/
@Component
public class EmailSender {
@Value("${spring.mail.username}")
private String username;
@Value("${spring.mail.password}")
private String password;
@Autowired
private JavaMailSender sender;
public void sendEmailWithAttachment(String toEmail,File attachmentFile,String name) throws MessagingException, IOException {
// 设置邮件属性
Properties properties = new Properties();
properties.put("mail.smtp.host","smtp.exmail.qq.com");
properties.put("mail.smtp.port",465);
properties.put("mail.smtp.auth","true");
properties.put("mail.smtp.starttls.enable","true");
// 获取邮件会话
// Session session = Session.getInstance(properties, new Authenticator() {
// @Override
// protected PasswordAuthentication getPasswordAuthentication() {
// // 填写发送者邮箱和授权码
// return new PasswordAuthentication(username, password);
// }
// });
//创建邮件内容
// 创建MimeMessage对象
MimeMessage message = sender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true); // true表示支持附件
helper.setFrom("249@qq.com");
helper.setTo(toEmail);
// String subject = String.valueOf(new StringBuilder(companyName).append("服务器运行状况报表"));
message.setSubject(name);
// 创建一个Multipart对象,包含邮件正文和附件
Multipart multipart = new MimeMultipart();
// 邮件正文部分
// MimeBodyPart messageBodyPart = new MimeBodyPart();
// messageBodyPart.setText(name);
// multipart.addBodyPart(messageBodyPart);
// 邮件附件部分
MimeBodyPart attachmentPart = new MimeBodyPart();
attachmentPart.attachFile(attachmentFile);
attachmentPart.setFileName(name+".docx");
multipart.addBodyPart(attachmentPart);// 附加附件文件
message.setContent(multipart);
// 发送邮件
sender.send(message);
System.out.println("邮件发送成功!");
}
}