我正在参与掘金创作者训练营第4期,点击了解活动详情,一起学习吧!
前言
在前面的文章里有介绍过hutool工具的邮件发送,但是在实际的业务中并不是调用其sendMail方法就能实现业务的,有时候我们得对其进行改造或者自己写方法实现,那今天来看看我是如何实现发送邮件及多个附件功能的。
一.实现思路
先实现动态Excel实现再实现邮件发送多人及多附件,动态生成Excel的是因为业务需要的Excel表格数据可能会根据后期的需要增加或减少字段,所以不能固定,可以通过SQL查询出来然后根据字段创建动态表头和对应的数据列,其次是发送邮件时需要发送多个附件,如果是先生成到本地再从本地取到再作为附件发送有点不太好,可以用数据流的方式发送生成的Excel文件,这个点也是行得通的,那下面来看看具体的实现吧。
二.具体实现
1.生成动态Excel
1)动态表头生成
/***
* 动态创建表头
* @param map
* @return
*/
public static String[] createTitle(Map<String, Object> map) {
int count = 0;
String strings[] = new String[map.size()];
for( Map.Entry<String, Object> entry : map.entrySet() ) {
strings[count] = entry.getKey();
count++;
}
return strings;
}
复制代码
2)绘制Excel,包括表头以及数量类型的一些简单处理
/**
* 创建Excel
* @param sheetName sheet名称
* @param title 标题
* @param values 内容
* @param wb HSSFWorkbook对象
* @return
*/
public static HSSFWorkbook createHSSFWorkbook(String sheetName, String[] title, String[][] values, HSSFWorkbook wb) {
// 第一步,创建一个HSSFWorkbook,对应一个Excel文件
if (wb == null) {
wb = new HSSFWorkbook();
}
// 第二步,在workbook中添加一个sheet,对应Excel文件中的sheet
HSSFSheet sheet = wb.createSheet(sheetName);
// 第三步,在sheet中添加表头第0行,注意老版本poi对Excel的行数列数有限制
HSSFRow row = sheet.createRow(0);
// 第四步,创建单元格,并设置值表头 设置表头居中
HSSFCellStyle style = wb.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER); // 创建一个居中格式
//声明列对象
HSSFCell cell = null;
//创建标题
for (int i = 0; i < title.length; i++) {
cell = row.createCell(i);
cell.setCellValue(title[i]);
cell.setCellStyle(style);
}
HSSFCellStyle contextstyle =wb.createCellStyle();
//创建内容
for (int i = 0; i < values.length; i++) {
row = sheet.createRow(i + 1);
for (int j = 0; j < values[i].length; j++) {
Boolean isNum = false;
Boolean isInteger=false;//data是否为整数
HSSFCell contentCell = row.createCell(j);
//将内容按顺序赋给对应的列对象
if (null!=values[i][j] || "".equals(values[i][j])) {
//判断data是否为数值型
isNum = values[i][j].toString().matches("^(-?\d+)(\.\d+)?$");
//判断data是否为整数(小数部分是否为0)
isInteger=values[i][j].toString().matches("^[-\+]?[\d]*$");
}
// 此处设置数据格式
if (isNum) {
HSSFDataFormat df = wb.createDataFormat();
if (isInteger) {
//数据格式只显示整数
contextstyle.setDataFormat(df.getBuiltinFormat("#,#0"));
}else{
/保留一位小数点
contextstyle.setDataFormat(df.getBuiltinFormat("#,##0.0"));/
}
// 设置单元格格式
contentCell.setCellStyle(contextstyle);
// 设置单元格内容为double类型
contentCell.setCellValue(Double.parseDouble(values[i][j]));
} else {
contentCell.setCellStyle(contextstyle);
// 设置单元格内容为字符型
contentCell.setCellValue(values[i][j]);
}
}
//设置自动列宽
sheet.autoSizeColumn(i);
sheet.setColumnWidth(i, sheet.getColumnWidth(i) * 17 / 10);
}
return wb;
}
复制代码
3)组装数据,将MySQL里查询的数据,组整到一个List<LinkedHashMap<String, Object>>里,为什么是LinkedHashMap而不是Map,这里有一个小细节,MySQL查询出来不一定按照数据库看到的顺序,所以得使用LinkedHashMap去接收,即将其变成有顺的
2.发送邮件
1)发送多人及多个附件
/**
* @param subject 邮件标题
* @param mapList 附件信息 可多附件
* @param content 邮件内容
* @param receiveList 收件人集合
*/
public static void sendEmail(String subject, List<Map<String,Object>> mapList, String content, List<String> receiveList, String smtpHost, String userName, String password) {
logger.info("send report start>>>>>>>>>>>>");
Session session = EmailUtils.getSession(smtpHost, userName, password);
MimeMessage message = new MimeMessage(session);
InternetAddress[] toArray = new InternetAddress[receiveList.size()];
try {
//接收列表
for (int i = 0; i < toArray.length; i++) {
toArray[i] = new InternetAddress(receiveList.get(i));
}
message.setSubject(subject);
message.setSentDate(new Date());
message.setFrom(new InternetAddress(userName));
message.addRecipients(MimeMessage.RecipientType.TO, toArray);
// 创建消息部分
BodyPart messageBodyPart = new MimeBodyPart();
// 消息
messageBodyPart.setContent(content, "text/html;charset=utf-8");
// 创建多重消息及可以发送多个附件
Multipart multipart = new MimeMultipart();
// 设置文本消息部分
multipart.addBodyPart(messageBodyPart);
//添加附件 这里以流形式直接发送
for (Map<String, Object> map : mapList) {
messageBodyPart = new MimeBodyPart();
DataSource source = new ByteArrayDataSource((byte[])map.get("os"), "application/excel");
messageBodyPart.setDataHandler(new DataHandler(source));
//避免中文乱码的处理
messageBodyPart.setFileName(MimeUtility.encodeText(map.get("title") + ".xlsx"));
multipart.addBodyPart(messageBodyPart);
}
// 发送完整消息
message.setContent(multipart);
// 发送消息
Transport.send(message);
logger.info("send email successful>>>>>>>>>>>>");
} catch (Exception e) {
e.printStackTrace();
logger.error("send email error",e.getMessage());
}
}
复制代码
2)发送结果及数据,@1为目标邮件收到邮件的截图,@2为测试数据,@2中箭头标注的就是SQL里的字段,直接查询出中文然后通过createTitle创建表头,并根据其长度生成对应的列
@1
@2 2)关于收件人配置
这里收件人可以配置到数据库或者Redis,需要添加或者删除改了立即生效,因为也不是常用的,没有必要放到配置文件里。
三.常见报错及解决
1.明明已开启SMTP但是过段时间后报了@1的错误,解决办法。重新生成授权码@2并替换之前设置的密码,此处是新浪邮箱为示例。
@1
@2
小结
很多人估计在拿到需求的时候就开始写代码,其实这样写出来的代码质量并不高,拿到需求应该先打个草稿或者画个图应该怎么处理,再怎么处理,代码虽然都是改出来的,后者的效率肯定要高很多,还有就是开发的时候能写灵活方法就写灵活的,别只顾着当前业务实现就行了。另外因为篇幅的限制,部分代码只能是截图的方式展示。