SpringBoot使用JavaMailSender實(shí)現(xiàn)發(fā)送郵件+Excel附件
需求描述:項(xiàng)目審批完畢后,需要發(fā)送郵件通知相關(guān)人員,并且要附帶數(shù)據(jù)庫表生成的Excel表格,這就要求不光是郵件發(fā)送功能,還要臨時(shí)生成Excel表格做為附件
1.生成Excel表格
使用huTool工具包的Excel表格生成功能
1.依賴設(shè)置
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.22</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.2</version> </dependency>
Hutool-all中包含了Hutool的所有工具類,由于需要生成Excel文件需要依賴poi
2.代碼:
@Override public void publish(xxxxxxPublishVo publishVo) { /** * 生成Excel表格 */ //在內(nèi)存操作,寫到輸出流中 ExcelWriter writer = ExcelUtil.getWriter(true); //自定義標(biāo)題別名 writer.addHeaderAlias("projectCode", "xx編號(hào)"); writer.addHeaderAlias("projectName", "xx名稱"); writer.addHeaderAlias("targetType", "xx類型"); writer.addHeaderAlias("targetName", "xx名稱"); writer.addHeaderAlias("targetForMp", "xxxx目標(biāo)"); writer.addHeaderAlias("symbols", "xx限制符"); //獲取數(shù)據(jù) QpmxxxxTargetMg query = new QpmxxxxTargetMg(); query.setProjectCode(publishVo.getProjectCode()); List<xxxxxxListDTO> data = selectxxxxxxByCondition(query); //整理數(shù)據(jù),以便于生成Excel表格 List<Object> dataNew = new ArrayList<>(); Set<String> stageCollectSet = new HashSet<>(); for (xxxxxxListDTO target: data){ List<xxxxxxTargetMgStage> stageList = target.getxxxxxxStageList(); Map<String, Object> addProperties = new HashMap<>(); for (xxxxxxMgStage stage: stageList){ if (!stageCollectSet.contains(stage)){ stageCollectSet.add(stage.getStage()); //給Excel增加列 writer.addHeaderAlias(stage.getStage(), stage.getStage() + "目標(biāo)"); } //為對(duì)象動(dòng)態(tài)增加屬性 addProperties.put(stage.getStage(), stage.getStageTarget()); } //生成新的包含了新增字段的對(duì)象 Object targetNew = ReflectUtil.getTarget(target, addProperties); dataNew.add(targetNew); } //只保留別名的數(shù)據(jù) writer.setOnlyAlias(true); writer.write(dataNew,true); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); // excel寫入到輸出流 writer.flush(outputStream,true);
上述代碼中,調(diào)用了工具類ReflectUtil給對(duì)象動(dòng)態(tài)增加屬性。由于數(shù)據(jù)中有子類,需要獲取到子類中的某個(gè)字段并生成Excel表格,所以Excel表格構(gòu)造就需要對(duì)數(shù)據(jù)對(duì)象進(jìn)行改造,簡單來說就是需要給對(duì)象動(dòng)態(tài)增加新的屬性(成員對(duì)象的屬性),如下所示示例:
把studentList里面的關(guān)鍵屬性數(shù)據(jù),新增給Test類
示例不是很合適,湊合著用吧
class Test { private String class; ............................................. private List<Student> studentList; }
工具類ReflectUtil,用于給對(duì)象動(dòng)態(tài)增加新的屬性:
import com.google.common.collect.Maps; import net.sf.cglib.beans.BeanGenerator; import net.sf.cglib.beans.BeanMap; import org.apache.commons.beanutils.PropertyUtilsBean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; /** * 為實(shí)體類動(dòng)態(tài)增加屬性,用于生成Excel表格時(shí)的特殊情況,例如表格中的列需要?jiǎng)討B(tài)增加 */ public class ReflectUtil { static Logger logger = LoggerFactory.getLogger(ReflectUtil.class); public static Object getTarget(Object dest, Map<String, Object> addProperties) { // get property map PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean(); PropertyDescriptor[] descriptors = propertyUtilsBean.getPropertyDescriptors(dest); Map<String, Class> propertyMap = Maps.newHashMap(); for (PropertyDescriptor d : descriptors) { if (!"class".equalsIgnoreCase(d.getName())) { propertyMap.put(d.getName(), d.getPropertyType()); } } // add extra properties for (Map.Entry<String, Object> entry : addProperties.entrySet()) { propertyMap.put(entry.getKey(), entry.getValue().getClass()); } // addProperties.forEach((k, v) -> propertyMap.put(k, v.getClass())); // new dynamic bean DynamicBean dynamicBean = new DynamicBean(dest.getClass(), propertyMap); // add old value for (Map.Entry<String, Class> entry : propertyMap.entrySet()) { try { // filter extra properties if (!addProperties.containsKey(entry.getKey())) { dynamicBean.setValue(entry.getKey(), propertyUtilsBean.getNestedProperty(dest, entry.getKey())); } } catch (Exception e) { logger.error(e.getMessage(), e); } } ; // add extra value for (Map.Entry<String, Object> entry : addProperties.entrySet()) { try { dynamicBean.setValue(entry.getKey(), entry.getValue()); } catch (Exception e) { logger.error(e.getMessage(), e); } } ; Object target = dynamicBean.beanMap; return target; } public static class DynamicBean { /** * 目標(biāo)對(duì)象 */ private Object target; /** * 屬性集合 */ private BeanMap beanMap; public DynamicBean(Class superclass, Map<String, Class> propertyMap) { this.target = generateBean(superclass, propertyMap); this.beanMap = BeanMap.create(this.target); } /** * bean 添加屬性和值 * * @param property * @param value */ public void setValue(String property, Object value) { beanMap.put(property, value); } /** * 獲取屬性值 * * @param property * @return */ public Object getValue(String property) { return beanMap.get(property); } /** * 獲取對(duì)象 * * @return */ public Object getTarget() { return this.target; } /** * 根據(jù)屬性生成對(duì)象 * * @param superclass * @param propertyMap * @return */ private Object generateBean(Class superclass, Map<String, Class> propertyMap) { BeanGenerator generator = new BeanGenerator(); if (null != superclass) { generator.setSuperclass(superclass); } BeanGenerator.addProperties(generator, propertyMap); return generator.create(); } } }
至此,我們就生成了Excel表格,并且把數(shù)據(jù)寫入到了輸出流中。
下面我們需要從輸出流中拿到Excel表格數(shù)據(jù),并做為郵件的附件發(fā)送出去。
2.郵件發(fā)送
1.郵件發(fā)送功能實(shí)現(xiàn)-帶附件
Spring Email 抽象的核心是 JavaMailSender接口,通過實(shí)現(xiàn)JavaMailSender接口把 Email 發(fā)送給郵件服務(wù)器,由郵件服務(wù)器實(shí)現(xiàn)郵件發(fā)送的功能。
Spring 自帶了一個(gè) JavaMailSender的實(shí)現(xiàn) JavaMailSenderImpl。SpringBoot 應(yīng)用在發(fā)送 Email 之前,我們需要在配置文件中對(duì)JavaMailSender進(jìn)行屬性配置,這樣就可以利用Springboot的自動(dòng)裝配機(jī)制,將 JavaMailSenderImpl 裝配為 Spring容器的一個(gè) bean。
spring.mail.host: xxxxxxx.com # 設(shè)置端口 spring.mail.port: 25 # 設(shè)置用戶名 spring.mail.username: xxxxxxxxxx # 設(shè)置密碼,該處的密碼是QQ郵箱開啟SMTP的授權(quán)碼而非QQ密碼 spring.mail.password: xxxxxxxxx # 設(shè)置是否需要認(rèn)證,如果為true,那么用戶名和密碼就必須的, # 如果設(shè)置false,可以不設(shè)置用戶名和密碼,當(dāng)然也得看你的對(duì)接的平臺(tái)是否支持無密碼進(jìn)行訪問的。 spring.mail.properties.mail.smtp.auth: false # STARTTLS[1] 是對(duì)純文本通信協(xié)議的擴(kuò)展。它提供一種方式將純文本連接升級(jí)為加密連接(TLS或SSL),而不是另外使用一個(gè)端口作加密通信。 spring.mail.properties.mail.smtp.starttls.enable: true spring.mail.properties.mail.smtp.starttls.required: fasle spring.mail.properties.mail.imap.starttls.socketFactory.fallback: false spring.mail.properties.mail.smtp.starttls.socketFactory.class: com.ey.model.MailCommand
繼上面完整的Excel生成代碼,現(xiàn)在繼續(xù)寫郵件發(fā)送代碼:
@Autowired private JavaMailSender springMailSender; @Override public void publish(xxxxxxPublishVo publishVo) { //........這里不再復(fù)制上面的代碼,只從Excel表格寫入輸出流開始......... // excel寫入輸出流 writer.flush(outputStream,true); //郵件附件名稱 String fileName = String.format("制定xx目標(biāo)-%s.xlsx",UUID.randomUUID()); //這個(gè)地方無需再配置,springboot自動(dòng)裝配,配置信息在nacos配置中心 // springMailSender.setDefaultEncoding("UTF-8"); // springMailSender.setHost("mx.goertek.com"); // springMailSender.setPort(25); // springMailSender.setProtocol(JavaMailSenderImpl.DEFAULT_PROTOCOL); // springMailSender.setUsername("tims.sys@goertek.com"); // springMailSender.setPassword("Khkd0804"); // Properties p = new Properties(); // p.setProperty("mail.smtp.timeout", "25000"); // p.setProperty("mail.smtp.auth", "true"); // p.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); // springMailSender.setJavaMailProperties(p); MimeMessage mimeMessage = springMailSender.createMimeMessage(); System.getProperties().setProperty("mail.mime.splitlongparameters", "false"); MimeMessageHelper messageHelper = null; try { messageHelper = new MimeMessageHelper(mimeMessage, true, "UTF-8"); } catch (MessagingException e) { throw new RuntimeException(e); } try { LoginUser userInfo = UserUtil.getCurrentUser(); // String currentUserEmail = userInfo.getEmaila(); // messageHelper.setFrom(currentUserEmail); messageHelper.setFrom(mailUserName); //設(shè)置收件人 String[] emailArr = publishVo.getEmails().replaceAll("\\s+", "").split(","); messageHelper.setTo(emailArr); //設(shè)置抄送人 if (!StringUtils.isBlank(publishVo.getCcEmails())){ String[] ccEmailArr = publishVo.getCcEmails().replaceAll("\\s+", "").split(","); messageHelper.setCc(ccEmailArr); } messageHelper.setSubject("項(xiàng)目-" + publishVo.getProjectName().concat(": 制定品質(zhì)目標(biāo)完畢")); messageHelper.setText("項(xiàng)目-" + publishVo.getProjectName().concat(": 制定品質(zhì)目標(biāo)完畢")); try { //messageHelper.addInline("doge.gif", new File("xx/xx/doge.gif")); messageHelper.addAttachment(MimeUtility.encodeWord(fileName,"utf-8","B"), new ByteArrayResource(outputStream.toByteArray())); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } springMailSender.send(mimeMessage); } catch (MessagingException e) { throw new RuntimeException(e); }
2.踩過的坑
上述郵件發(fā)送功能實(shí)現(xiàn)過程中踩過的坑:
附件名中文亂碼問題
附件的名字是中文,發(fā)送成功后,在郵件中的附件名字中文亂碼,怎樣解決這個(gè)問題?
1. 設(shè)置系統(tǒng)值:
System.setProperty("mail.mime.splitlongparameters", "false");
2. 這里,在創(chuàng)建對(duì)象的時(shí)候定義編碼格式(utf-8):
MimeMessageHelper messageHelper = new MimeMessageHelper(mes, true, "utf-8");
3. 其次,在添加附件的時(shí)候,附件名是需要定義編碼:
messageHelper.addAttachment(MimeUtility.encodeWord(附件名,"utf-8","B"), 附件輸入流));
3.參考文章:
使用hutool工具進(jìn)行導(dǎo)入導(dǎo)出excel表格_java_腳本之家 (jb51.net)
SpringBoot實(shí)現(xiàn)excel生成并且通過郵件發(fā)送的步驟詳解_java_腳本之家 (jb51.net)
以上就是SpringBoot使用JavaMailSender實(shí)現(xiàn)發(fā)送郵件+Excel附件的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot JavaMailSender發(fā)送郵件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
springMVC+velocity實(shí)現(xiàn)仿Datatables局部刷新分頁方法
下面小編就為大家分享一篇springMVC+velocity實(shí)現(xiàn)仿Datatables局部刷新分頁方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-02-02ReadWriteLock接口及其實(shí)現(xiàn)ReentrantReadWriteLock方法
下面小編就為大家?guī)硪黄猂eadWriteLock接口及其實(shí)現(xiàn)ReentrantReadWriteLock方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-06-06SpringCloud OpenFeign超詳細(xì)講解模板化遠(yuǎn)程通信的實(shí)現(xiàn)
這篇文章主要介紹了SpringCloudSpringboot集成OpenFeign實(shí)現(xiàn)模板化遠(yuǎn)程通信,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2022-07-07Java實(shí)現(xiàn)遞歸讀取文件夾下的所有文件
這篇文章主要為大家詳細(xì)介紹了如何利用Java實(shí)現(xiàn)遞歸讀取文件夾下的所有文件,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-02-02基于Spring Security前后端分離的權(quán)限控制系統(tǒng)問題
本文給大家分享基于Spring Security前后端分離的權(quán)限控制系統(tǒng)問題,需要了解權(quán)限如何加載,權(quán)限匹配規(guī)則和登錄的實(shí)現(xiàn)代碼,對(duì)Spring Security權(quán)限控制系統(tǒng)相關(guān)知識(shí)感興趣的朋友一起看看吧2021-06-06基于@RequestParam name和value屬性的區(qū)別
這篇文章主要介紹了@RequestParam name和value屬性的區(qū)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08SpringBoot如何使用@Aspect注解實(shí)現(xiàn)AOP
這篇文章主要介紹了SpringBoot如何使用@Aspect注解實(shí)現(xiàn)AOP問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07如何通過Java實(shí)現(xiàn)PDF轉(zhuǎn)高質(zhì)量圖片
在Java中,將PDF文件轉(zhuǎn)換為高質(zhì)量的圖片可以使用不同的庫,其中最常用的庫之一是?Apache?PDFBox,下面我們就來看看這個(gè)庫的具體使用吧2024-10-10springboot實(shí)現(xiàn)以代碼的方式配置sharding-jdbc水平分表
這篇文章主要介紹了springboot實(shí)現(xiàn)以代碼的方式配置sharding-jdbc水平分表,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11淺談synchronized加鎖this和class的區(qū)別
synchronized 是 Java 語言中處理并發(fā)問題的一種常用手段,本文主要介紹了synchronized加鎖this和class的區(qū)別,具有一定的參考價(jià)值,感興趣的可以了解一下2021-11-11