SpringBoot实现excel生成并且通过邮件发送的步骤详解
作者:java大能虫
1. 开发环境
以下演示jdk选用1.8版本。springboot采用2.3.7.RELEASE版本。
excel生成通过alibaba的EasyExcel组件来实现,采用最新的稳定版本3.1.1
2. 思路
我们的核心实现分成两步: 1、生成一个excel 2、将excel作为附件添加到邮件中进行发送
于是基于此思路,我们结合EasyExcel提供的write方法来生成excel文件,该excel文件在邮件发送完成后需要删除;然后通过之前我们讲解过的邮件发送工具类来实现邮件发送
3. 实操
1、书写邮件发送工具类,其实现参考上述博文
<dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>3.1.1</version> </dependency>
3、实现easyExcel的数据监听类
/** * @author benjamin_5 * @Description 数据监听类 * @date 2022/10/5 */ @EqualsAndHashCode(callSuper = true) public class DataListener<T> extends AnalysisEventListener<T> { /** * 缓存数据列表 */ private final List<T> dataList = new ArrayList<>(); @Override public void invoke(T data, AnalysisContext context) { dataList.add(data); } @Override public void doAfterAllAnalysed(AnalysisContext context) { } @Override public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) { this.invokeHeadMap(ConverterUtils.convertToStringMap(headMap,context),context); } public List<T> getDataList() { return dataList; } }
4、创建生成excel文件的工具类ExcelUtil
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、创建导出数据的实体类
/** * @author benjamin_5 * @Description * @date 2022/10/5 */ @Data public 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实现的邮件发送工具类
@GetMapping("generateExcelAndSend") public void generateExcelAndSend(){ List<CustomerData> dataList = new ArrayList<>(); // 构造假数据 for (int i = 0; i < 100; i++) { CustomerData data = 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()); 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。于是我们创建一个启动执行类来单独设置
@Configuration public class EmailToLongConfig { @PostConstruct private void init(){ // 解决邮件附件名称太长会自动截取,导致附件变成.bin格式问题 System.setProperty("mail.mime.splitlongparameters","false"); } }
当然我们也可以将System.setProperty("
mail.mime.splitlongparameters","false");放到邮件发送的方法中去。
8、启动项目,浏览器访问接口测试
http://localhost:8080/excel/generateExcelAndSend
可以看到上述邮件发送成功,excel附件也接收正常,查看附件内容也正常
优化
上述的实现,需要先创建一个文件然后又删除,不是很方便,我们可以采取直接用流输入输出
1、首先生成excel的方法调整为返回输出流
public static <T> ByteArrayOutputStream generateExcel(List<T> dataList, Class<T> clazz) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); // excel写入 EasyExcel.write(out,clazz).sheet(0).doWrite(dataList); return out; }
2、其次发送邮件的方法调整为,直接接收输入流
public void sendEmail(String subject, String content, boolean contentIsHtml, String fromMailPersonalName, String toMail, String ccMail, String bccMail, String fileName, InputStreamSource fileInput) throws MessagingException, UnsupportedEncodingException { MimeMessage message = javaMailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(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、主方法调整:不生成文件,而是通过流来传输
@GetMapping("generateExcelAndSend2") public void generateExcelAndSend2() throws IOException { long start = System.currentTimeMillis(); List<CustomerData> dataList = new ArrayList<>(); // 构造假数据 for (int i = 0; i < 100; i++) { CustomerData data = 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()); ByteArrayOutputStream out = 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字符串的功能
总结
excel的生成以及邮件的发送,都应该尽可能的提取为工具类,如果实现的功能更多的更需要提取的单独的服务,通过pom依赖引入,更大化的实现方法的通用,和业务代码与通用代码之间的解耦。
以上就是SpringBoot实现excel生成并且通过邮件发送的步骤详解的详细内容,更多关于SpringBoot实现excel生成的资料请关注脚本之家其它相关文章!