0. 引言

实际开发中,特别是在B端产品的开发中,我们经常会遇到导出excel的功能,更进阶一点的需要我们定期生成统计报表,然后通过邮箱发送给指定的人员。

这样的一个功能我们将其拆分出来包括三方面的知识点:定时任务框架、excel生成、邮件发送

今天要带大家来实现的就是excel生成并通过邮件发送

1. 开发环境

以下演示jdk选用1.8版本。springboot采用2.3.7.RELEASE版本。

excel生成通过alibaba的EasyExcel组件来实现,采用最新的稳定版本3.1.1

2. 思路

我们的核心实现分成两步: 1、生成一个excel 2、将excel作为附件添加到邮件中进行发送

于是基于此思路,我们结合EasyExcel提供的write方法来生成excel文件,该excel文件在邮件发送完成后需要删除;然后通过之前我们讲解过的邮件发送工具类来实现邮件发送

邮件发送工具类不再单独讲解,不清楚的同学可以查看之前几期的内容:

springboot:java实现邮件及附件发送、HTML正文的三种方式(一)

springboot:java实现邮件及附件发送、HTML正文的三种方式(二)

springboot:java实现邮件及附件发送、HTML正文的三种方式(三)

3. 实操

1、书写邮件发送工具类,其实现参考上述博文

2、引入excel依赖:

xml复制代码<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.1</version></dependency>

3、实现easyExcel的数据监听类

java复制代码/*** @authorbenjamin_5*@Description 数据监听类* @date 2022/10/5*/@EqualsAndHashCode(callSuper = true)public class DataListener<T> extends AnalysisEventListener<T> {/*** 缓存数据列表*/private final List<T> dataList = new ArrayList<>();@Overridepublic void invoke(T data, AnalysisContext context) {dataList.add(data);}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {}@Overridepublic void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {this.invokeHeadMap(ConverterUtils.convertToStringMap(headMap,context),context);}public List<T> getDataList() {return dataList;}}

4、创建生成excel文件的工具类ExcelUtil

java复制代码public class ExcelUtil {/*** 生成excel文件* @param fileName excel文件路径* @param dataList 数据列表* @param clazz 导出对象类* @param <T>* @return* @throws IOException*/public static<T> File generateExcel(String fileName,List<T> dataList, Class<T> clazz) throws IOException {// 生成文件File excel = new File(fileName);// excel写入EasyExcel.write(excel,clazz).sheet(0).doWrite(dataList);return excel;}}

5、创建导出数据的实体类

java复制代码/*** @author benjamin_5* @Description* @date 2022/10/5*/@Datapublic class CustomerData implements Serializable {@ExcelProperty(value = "客户名称")@ColumnWidth(value = 25)private String name;@ExcelProperty(value = "客户地址")@ColumnWidth(value = 50)private String address;@ExcelProperty(value = "联系电话")private String phone;@ExcelProperty(value = "金额")private BigDecimal amount;@ExcelProperty(value = "注册日期")@DateTimeFormat(value = "yyyy-MM-dd")private Date createDate;}

6、实现excel数据生成及邮件发送的接口。为了演示方便我直接在controller中书写了,实际工作中应该把生成并发送的方法提取到工具类或者Service层中。

这里调用的是基于spring-boot-starter-mail实现的邮件发送工具类

java复制代码@GetMapping("generateExcelAndSend")public voidgenerateExcelAndSend(){List<CustomerData> dataList =new ArrayList<>();// 构造假数据for (int i = 0; i < 100; i++) {CustomerData data = newCustomerData();data.setName("客户"+i);data.setAddress("贵州省贵阳市观山湖区101号");data.setPhone("13889999999");data.setAmount(BigDecimal.valueOf(Math.random()*10000));data.setCreateDate(new Date());dataList.add(data);}// 获取资源文件存放路径,用于临时存放生成的excel文件String path = Objects.requireNonNull(this.getClass().getClassLoader().getResource("")).getPath();// 文件名:采用UUID,防止多线程同时生成导致的文件重名String fileName = String.format("%s客户统计数据-%s.xlsx",path,UUID.randomUUID());try {// 生成excel文件File excel = ExcelUtil.generateExcel(fileName, dataList, CustomerData.class);// 发送邮件String content = "";String toMail = "wuhanxue5@sina.com";String ccMail = "wuhanxue5@163.com";emailSpringUtil.sendEmail("客户统计数据",content,true,"邮件提醒系统",toMail,ccMail,null, Collections.singletonList(fileName));// 邮件发送完成后删除临时生成的excel文件excel.delete();} catch (IOException e) {logger.error(String.format("生成excel失败,原因:%s",e));e.printStackTrace();}catch (MessagingException e) {logger.error(String.format("邮件发送失败,原因:%s",e));e.printStackTrace();}}

7、同时因为我们的附件名称后面添加一个了个UUID,mime.mail中的参数splitlongparameters默认为 true,当附件名过长时,他会自动截取,就会导致我们接收到的附件格式变成.bin形式的。

要解决该问题就需要将其设置为false。于是我们创建一个启动执行类来单独设置

java复制代码@Configurationpublic class EmailToLongConfig {@PostConstructprivate void init(){// 解决邮件附件名称太长会自动截取,导致附件变成.bin格式问题System.setProperty("mail.mime.splitlongparameters","false");}}

当然我们也可以将

System.setProperty("

mail.mime.splitlongparameters","false");放到邮件发送的方法中去。

8、启动项目,浏览器访问接口测试http://localhost:8080/excel/generateExcelAndSend

可以看到上述邮件发送成功,excel附件也接收正常,查看附件内容也正常

优化

上述的实现,需要先创建一个文件然后又删除,不是很方便,我们可以采取直接用流输入输出

1、首先生成excel的方法调整为返回输出流

java复制代码public static <T> ByteArrayOutputStreamgenerateExcel(List<T> dataList, Class<T> clazz) throws IOException {ByteArrayOutputStream out = new ByteArrayOutputStream();// excel写入EasyExcel.write(out,clazz).sheet(0).doWrite(dataList);return out;}

2、其次发送邮件的方法调整为,直接接收输入流

java复制代码public void sendEmail(String subject, String content, boolean contentIsHtml, String fromMailPersonalName,String toMail, String ccMail, String bccMail, StringfileName, InputStreamSource fileInput) throws MessagingException, UnsupportedEncodingException {MimeMessage message = javaMailSender.createMimeMessage();MimeMessageHelper helper =newMimeMessageHelper(message,true);helper.setFrom(mailProperties.getUsername(), fromMailPersonalName);helper.setTo(toMail);if(!ObjectUtils.isEmpty(ccMail)) {helper.setCc(ccMail);}if(!ObjectUtils.isEmpty(bccMail)) {helper.setBcc(bccMail);}helper.setSubject(subject);helper.setText(content, contentIsHtml);// 设置附件(注意这里的fileName必须是服务器本地文件名,不能是远程文件链接)if (fileInput != null) {helper.addAttachment(fileName, fileInput);}javaMailSender.send(message);}

3、主方法调整:不生成文件,而是通过流来传输

java复制代码@GetMapping("generateExcelAndSend2")publicvoid generateExcelAndSend2() throws IOException {long start = System.currentTimeMillis();List<CustomerData> dataList = new ArrayList<>();// 构造假数据for (int i = 0; i < 100; i++) {CustomerDatadata = new CustomerData();data.setName("客户"+i);data.setAddress("贵州省贵阳市观山湖区101号");data.setPhone("13889999999");data.setAmount(BigDecimal.valueOf(Math.random()*10000));data.setCreateDate(new Date());dataList.add(data);}// 获取资源文件存放路径,用于临时存放生成的excel文件String path = Objects.requireNonNull(this.getClass().getClassLoader().getResource("")).getPath();// 文件名:采用UUID,防止多线程同时生成导致的文件重名String fileName = String.format("%s客户统计数据-%s.xlsx",path,UUID.randomUUID());ByteArrayOutputStreamout = null;try {// 生成excel文件out = ExcelUtil.generateExcel(dataList, CustomerData.class);// 发送邮件String content = "客户统计数据如附件所示";String toMail = "wuhanxue5@sina.com";String ccMail = "wuhanxue5@163.com";emailSpringUtil.sendEmail("客户统计数据",content,false,"邮件提醒系统",toMail,ccMail,null, fileName, new ByteArrayResource(out.toByteArray()));} catch (IOException e) {logger.error(String.format("生成excel失败,原因:%s",e));e.printStackTrace();} catch (MessagingException e) {logger.error(String.format("邮件发送失败,原因:%s",e));e.printStackTrace();}finally {if(out != null){out.close();}long end = System.currentTimeMillis();System.out.println("耗时:" + (end - start));}}

4、最终测试下来,第二种方法要比之前方法快600ms左右

邮件正文中直接显示表格数据

有时候我们的统计数据不是很多,会更希望我们直接在邮件中展示表格数据,而不用再单独下载附件查看,这就需要用到HTML格式的邮件正文的实现

比较简单的实现就是循环数据集合,通过字符串拼接生成html的字符串。因为实现比较简单,这里就仅提供思路,如果有不清楚的同学可以留言提问。后续我们单独抽离成一个组件来实现生成html字符串的功能

演示源码

以上演示的源码可以在如下地址中下载

git源码地址

总结

excel的生成以及邮件的发送,都应该尽可能的提取为工具类,如果实现的功能更多的更需要提取的单独的服务,通过pom依赖引入,更大化的实现方法的通用,和业务代码与通用代码之间的解耦。

还是那句话,看十遍,不如动手操作一遍。

原文链接:

https://juejin.cn/post/7237830541177045052
分类: 百科知识 标签: 暂无标签

评论

暂无评论数据

暂无评论数据

目录