前言
在前面的文章里有介绍过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而不是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
@22)关于收件人配置
这里收件人可以配置到数据库或者Redis,需要添加或者删除改了立即生效,因为也不是常用的,没有必要放到配置文件里。
三.常见报错及解决
1.明明已开启SMTP但是过段时间后报了@1的错误,解决办法。重新生成授权码@2并替换之前设置的密码,此处是新浪邮箱为示例。
@1
@2
小结
很多人估计在拿到需求的时候就开始写代码,其实这样写出来的代码质量并不高,拿到需求应该先打个草稿或者画个图应该怎么处理,再怎么处理,代码虽然都是改出来的,后者的效率肯定要高很多,还有就是开发的时候能写灵活方法就写灵活的,别只顾着当前业务实现就行了。另外因为篇幅的限制,部分代码只能是截图的方式展示。