摘要: JavaMail发送基本邮件ATA上有的是,但这次有个需求提出要实现会议邮件,呃,习惯性看有没有同学已经实现了,居然少之又少,不知道是不是有其他团队有这种需求,目测也不多哈哈,实现了这个功能感觉还挺有意思的,分享给大家交流交流。(可能有些理解不对,请大家指出,谢谢) 前期真的是比较懵圈,虽然一开始已经有实现了普通邮件发送,是通过Spring提供的*MimeMessageHelper*这个组件
JavaMail发送基本邮件ATA上有的是,但这次有个需求提出要实现会议邮件,呃,习惯性看有没有同学已经实现了,居然少之又少,不知道是不是有其他团队有这种需求,目测也不多哈哈,实现了这个功能感觉还挺有意思的,分享给大家交流交流。(可能有些理解不对,请大家指出,谢谢)
前期真的是比较懵圈,虽然一开始已经有实现了普通邮件发送,是通过Spring提供的MimeMessageHelper这个组件,说是摆脱繁杂的JavaMail API,封装了一些实现,简化了使用,但如果用这它去实现会议邮件,我试了很久都不行。呃,深入方法去看实现,呃,原来它的底层是MimeMessage,提供setText()也是。所以,我想如果要实现会议邮件,只能考虑抛弃helper这个组件去用JavaMail API重新实现。中间联系过阿里云邮的同学,他们告诉我没有提供HSF服务或通用API,只需要实现SMTP协议就可以了,行吧,开工。
实现效果图先晒为敬:

邮件内容效果图

日历效果图
下面讲下实现代码:
最关键是两点,一是实现RFC2445标准(日历数据交换),会议邮件的核心;二是理解JavaMail实现发送邮件的关联,下面关联图的左边部分就是了。没实现前觉得挺难,理解了就是拼装的事。

第一点我是利用实现的,maven(org.mnode.ical4j:ical4j:1.0.7)
ical4j
邮件内容我是用velocity生成的String填充进去的,百度就有这里不做介绍,freemarker大家也可以试试。
邮件中还可以根据需要添加附件,addBodyPart实现就可以了。
[Java]
纯文本查看
复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 | // 注入bean必备private final static String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";@Autowiredprivate JavaMailSender mailSender;@Resourceprivate VelocityEngine velocityEngine;// 请根据你们自己的需要或申请配置@Value("${spring.mail.username}")private String fromMailAddress_preview;@Value("${spring.mail.host}")private String mailHost;@Value("${spring.mail.username}")private String mailUsername;@Value("${spring.mail.password}")private String mailPassword;@Value("${spring.mail.port}")private String mailPort;@Value("${spring.mail.properties.mail.smtp.socketFactory.port}")private String mailSmtpSocketFactoryPort;@Value("${spring.mail.properties.mail.smtp.auth}")private String mailSmtpAuth; |

发送会议邮件模板方法:
[Java]
纯文本查看
复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | /** * 发送会议邀请邮件 * * @param toMailAddress 收件人(邀约人),支持多个 * @param mailSubject 邮件主题 * @param mailContent 邮件内容(建议传入velocity去构建生成的HTML内容) * @param summary 摘要,即日历(日程)上显示的标题 * @param startTimestamp 会议开始时间 * @param endTimestamp 会议结束时间 * @param locationContent 会议位置 * @return 发送结果 */ public Boolean sendMeetingMailTemplate(String[] toMailAddressArray, String mailSubject, String mailContent, String summary, Long startTimestamp, Long endTimestamp, String locationContent) { if (toMailAddressArray == null || toMailAddressArray.length <= 0 || StringUtils.isEmpty(fromMailAddress_preview) || StringUtils.isEmpty(mailSubject) || StringUtils.isEmpty(mailContent) || StringUtils.isEmpty(summary)) { return false; } boolean sendStatus = false; Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider()); Properties prop = new Properties(); prop.put("mail.smtp.host", mailHost); prop.put("mail.smtp.auth", mailSmtpAuth); prop.put("mail.smtp.port", mailPort); prop.setProperty("mail.smtp.socketFactory.class", SSL_FACTORY); prop.setProperty("mail.smtp.socketFactory.fallback", "false"); prop.setProperty("mail.smtp.socketFactory.port", mailSmtpSocketFactoryPort); MailAuthenticator authenticator = new MailAuthenticator(mailUsername, mailPassword); Session session = Session.getDefaultInstance(prop, authenticator); MimeMessage message = new MimeMessage(session); try { message.addHeaderLine("method=REQUEST"); message.addHeaderLine("charset=UTF-8"); message.addHeaderLine("component=VEVENT"); message.setFrom(new InternetAddress(fromMailAddress_preview)); InternetAddress[] addressArray = new InternetAddress[toMailAddressArray.length]; for (int i = 0; i < toMailAddressArray.length; i++) { addressArray[i] = new InternetAddress(toMailAddressArray[i]); } message.addRecipients(Message.RecipientType.TO, addressArray); message.setSubject(mailSubject); } catch (MessagingException e) { e.printStackTrace(); } // 会议内容核心拼装 BodyPart meetingBodyPart = new MimeBodyPart(); try { meetingBodyPart.setHeader("Content-Class", "urn:content- classes:calendarmessage"); meetingBodyPart.setHeader("Content-ID", "calendar_message"); meetingBodyPart.setDataHandler(new DataHandler(new ByteArrayDataSource( buildCalendar(summary, startTimestamp, endTimestamp, locationContent, toMailAddressArray).toString(), "text/calendar"))); } catch (IOException | MessagingException e) { e.printStackTrace(); } // 邮件原文组合+发送 Multipart multipart = new MimeMultipart(); try { multipart.addBodyPart(meetingBodyPart); BodyPart contentBodyPart = new MimeBodyPart(); // 普通文件赋值 //contentBodyPart.setText(mailContent); /* HTML内容赋值 Map<String, Object> model = new HashMap<>(); model.put("sscontent", "test测试师善"); VelocityContext velocityContext = new VelocityContext(); model.put("sscontent", model.get("sscontent")); String text = VelocityEngineUtils.mergeTemplateIntoString(velocityEngine, "/mail/test.vm", "UTF-8", model);*/ contentBodyPart.setContent(mailContent, "text/html; charset=utf-8"); multipart.addBodyPart(contentBodyPart); message.setContent(multipart); Transport.send(message); sendStatus = true; } catch (MessagingException e) { e.printStackTrace(); } return sendStatus; } |
构建日历对象方法:
[Java]
纯文本查看
复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | /** * 构建会议邀约日历对象 * * @param summary 摘要,会议邮件显示在日历插件上的标题 * @param startTimestamp 会议开始时间,GMT+8 * @param endTimestamp 会议结束时间,GMT+8 * @param LocationContent 会议位置 * @param toMailAddressArray 邀约人 * @return */public Calendar buildCalendar(String summary, Long startTimestamp, Long endTimestamp, String LocationContent, String[] toMailAddressArray) { TimeZoneRegistry registry = TimeZoneRegistryFactory.getInstance().createRegistry(); TimeZone timezone = registry.getTimeZone("Asia/Shanghai"); VTimeZone tz = timezone.getVTimeZone(); // 创建日历 Calendar calendar = new Calendar(); calendar.getProperties().add(new ProdId("-//Ben Fortuna//iCal4j 1.0//EN")); calendar.getProperties().add(Version.VERSION_2_0); calendar.getProperties().add(CalScale.GREGORIAN); // ⭐️下面这行很关键,缺少的话钉钉IOS邮箱会显示1970--01-01 08:00 calendar.getProperties().add(Method.REQUEST); DateTime start = new DateTime(startTimestamp); start.setTimeZone(timezone); DateTime end = new DateTime(endTimestamp); end.setTimeZone(timezone); VEvent event = new VEvent(start, end, summary); event.getProperties().add(new Location(LocationContent)); try { // 生成唯一标示 event.getProperties().add(new Uid(new UidGenerator("iCal4j").generateUid().getValue())); // 添加时区信息 event.getProperties().add(tz.getTimeZoneId()); // 组织者 event.getProperties().add(new Organizer("mailto:preview@alibaba-inc.com")); } catch (SocketException | URISyntaxException e) { e.printStackTrace(); } // 添加邀请者 for (int i = 0; i < toMailAddressArray.length; i++) { Attendee dev = new Attendee(URI.create("mailto:" + toMailAddressArray[i])); dev.getParameters().add(Role.REQ_PARTICIPANT); dev.getParameters().add(new Cn("Developer " + (i + 1))); event.getProperties().add(dev); } /* // 重复事件 Recur recur = new Recur(Recur.WEEKLY, Integer.MAX_VALUE); recur.getDayList().add(WeekDay.MO); recur.getDayList().add(WeekDay.TU); recur.getDayList().add(WeekDay.WE); recur.getDayList().add(WeekDay.TH); recur.getDayList().add(WeekDay.FR); RRule rule = new RRule(recur); event.getProperties().add(rule); */ // 提醒,提前10分钟 VAlarm valarm = new VAlarm(new Dur(0, 0, -10, 0)); valarm.getProperties().add(new Summary("事件提醒")); valarm.getProperties().add(Action.DISPLAY); valarm.getProperties().add(new Description("会议提醒描述,待定,不确定使用方式")); // 将VAlarm加入VEvent event.getAlarms().add(valarm); // 添加事件 calendar.getComponents().add(event); // 验证 try { calendar.validate(); } catch (ValidationException e) { e.printStackTrace(); } return calendar;} |
更多技术资讯可关注:gzitcast