使用JavaMail向outlook发送会议邀请(日历)邮件

最近公司招聘系统有一个需求,就是给面试官发送面试邀请邮件的时候,需要发送一个面试提醒,能自动加入到日历中,面试前给面试官提醒,以免忘记。之前没做过发邮件功能,这次网上各种找资料,折腾了好久......

所需依赖:


    com.sun.mail
    javax.mail
    1.6.2

Demo:

public class Test1 {
    public static void main(String[] args) {
        sendMeetingInvitationEmail();
    }

    private static Properties props;
    private static Session session;

    public static void sendMeetingInvitationEmail() {
        try {
            props = new Properties();
            //发件人
            String fromEmail = props.getProperty("fromEmail", "[email protected]");
            //收件人(面试官)
            String toEmail = props.getProperty("toEmail", "[email protected]");
            props.put("mail.smtp.port", "587");
            props.put("mail.smtp.host", "smtp.office365.com");
            //当前smtp host设为可信任 否则抛出javax.mail.MessagingException: Could not                   convert socket to TLS
            props.put("mail.smtp.ssl.trust", "smtp.office365.com");
            props.put("mail.transport.protocol", "smtp");
            props.put("mail.smtp.auth", "true");
            props.put("mail.smtp.starttls.enable", "true");
            props.put("mail.smtp.ssl", "true");
            //开启debug调试,控制台会打印相关信息
            props.put("mail.debug", "true");
            Authenticator authenticator = new Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    //发件人邮箱账号
                    String userId = props.getProperty("userId", "[email protected]");
                    //发件人邮箱密码(qq、163等邮箱用的是授权码,outlook是密码)
                    String password = props.getProperty("password", "XXXXXXXX");
                    return new PasswordAuthentication(userId, password);
                }
            };
            session = Session.getInstance(props, authenticator);
            MimeMessage message = new MimeMessage(session);
            message.setFrom(new InternetAddress(fromEmail));
            message.addRecipient(Message.RecipientType.TO, new InternetAddress(toEmail));
            //标题
            message.setSubject("XXX公司诚邀应聘");
            //面试开始时间
            String startTime = getUtc("2019-09-04 14:00");
            //面试结束时间
            String endTime = getUtc("2019-09-04 15:00");
            StringBuffer buffer = new StringBuffer();
            buffer.append("BEGIN:VCALENDAR\n"
                    + "PRODID:-//Microsoft Corporation//Outlook 9.0 MIMEDIR//EN\n"
                    + "VERSION:2.0\n"
                    + "METHOD:REQUEST\n"
                    + "BEGIN:VEVENT\n"
                    //参会者
                    + "ATTENDEE;ROLE=REQ-PARTICIPANT;RSVP=TRUE:MAILTO:你和应聘者\n"
                    //组织者
                    //+ "ORGANIZER:MAILTO:张三\n"
                    + "DTSTART:" + startTime + "\n"
                    + "DTEND:" + endTime + "\n"
                    //面试地点
                    + "LOCATION:会议室01\n"
                    //如果id相同的话,outlook会认为是同一个会议请求,所以使用uuid。
                    + "UID:" + UUID.randomUUID().toString() + "\n"
                    + "CATEGORIES:\n"
                    //会议描述
                    //+ "DESCRIPTION:Stay Hungry.
Stay Foolish.\n\n" + "SUMMARY:面试邀请\n" + "PRIORITY:5\n" + "CLASS:PUBLIC\n" + "BEGIN:VALARM\n" //提前10分钟提醒 + "TRIGGER:-PT10M\n" + "ACTION:DISPLAY\n" + "DESCRIPTION:Reminder\n" + "END:VALARM\n" + "END:VEVENT\n" + "END:VCALENDAR"); BodyPart messageBodyPart = new MimeBodyPart(); messageBodyPart.setDataHandler(new DataHandler(new ByteArrayDataSource(buffer.toString(), "text/calendar;method=REQUEST;charset=\"UTF-8\""))); MimeMultipart multipart = new MimeMultipart(); MimeBodyPart mimeBodyPart = new MimeBodyPart(); //String emailText = getHtmlContent(sendEmailApi.getTemplateContent(tempValue),tempMap); //文本类型正文 mimeBodyPart.setText("尊敬的张三:\r您好!\r特邀您..."); //html类型正文 //mimeBodyPart.setContent(emailText,"text/html;charset=UTF-8"); //添加正文 multipart.addBodyPart(mimeBodyPart); //添加日历 multipart.addBodyPart(messageBodyPart); message.setContent(multipart); message.setSentDate(new Date()); message.saveChanges(); Transport.send(message); } catch (MessagingException me) { me.printStackTrace(); } catch (Exception ex) { ex.printStackTrace(); } } /** * 转utc时间 * * @param str * @return */ private static String getUtc(String str) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm"); long millionSeconds = 0; try { millionSeconds = sdf.parse(str).getTime(); } catch (ParseException e1) { e1.printStackTrace(); } //utc时间差8小时 long currentTime = millionSeconds - 8 * 60 * 60 * 1000; Date date = new Date(currentTime); //格式化日期 DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String nowTime = ""; nowTime = df.format(date); //转换utc时间 String utcTime = nowTime.replace("-", "").replace(" ", "T").replace(":", ""); return utcTime; } }

日历功能的话,ical4j也是可以实现的,我这里既然也可实现同样的效果,就偷了个懒。

发送成功的效果:

image

注意事项:

这样的需求虽然不是满大街,但是网上的帖子也不是少的可怜那种,看着就这么点代码,真的折腾了好久,不是报这个错就是报那个错......

  • 发件人邮箱密码:

如果是qq,163等的邮箱,用的是授权码,关于授权码如何获取,请自行百度。如果是outlook邮箱,因为outlook没有什么授权码,用的就是邮箱的登录密码。

  • 服务器地址,端口参数:
props.put("mail.smtp.port", "587");
props.put("mail.smtp.host", "smtp.office365.com");

outlook邮箱如果是个人邮箱,请登录邮箱后设置里面去找(应该都是一样的吧),如下:

outlookSMTP.jpg
outlook邮箱如果不是个人邮箱,是公司邮箱,比如邮箱后缀是@XX公司.com,邮箱服务器地址就是公司的邮箱 服务器地址,端口好像就是常用的25吧。

如果是qq,163邮箱,端口就是25,服务器地址分别为smtp.qq.com、smtp.163.com。其他邮箱的话,抱歉没  有试过。
  • 证书的问题:
javax.mail.MessagingException: Could not convert socket to TLS 
sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

不能把socket解析为TLS,不能对访问的目标提供有效证书。

在公司运行的时候,报了这个错。家里用自己outlook账号的话,倒是不会有任何问题...,不用加如下配置,证书也不用导入。

  1. 可以加这个配置,可以把当前邮箱服务器地址设为可信任的。
props.put("mail.smtp.ssl.trust", "smtp.office365.com");//邮箱服务器地址
  1. 另外还可以导入证书

    用工具类生成安全证书或者从浏览器下载证书,放在\JAVA_HOME\jre\lib\security下面

    工具类生成证书:

https://www.tuicool.com/articles/zUjiIb

需要注意的是,帖子里面先javac编译java文件,后执行class文件,执行方式:

Java InstallCert hostname 

hostname 就是发件邮箱的服务器地址,如smtp.office365.com。

浏览器下载证书:

https://blog.csdn.net/frankcheng5143/article/details/52164939

  • 日历参数-参会者:
//参会者
+ "ATTENDEE;ROLE=REQ-PARTICIPANT;RSVP=TRUE:MAILTO:你和应聘者\n"

这个参数我在公司明明是注释的,没有加,可以发送邮件,不知道为什么回到家用自己邮箱发送,不加的话就会报错,一脸懵逼......,现在都还不知道怎么回事。

//无法发送邮件,因为他不包含任何收件人
com.sun.mail.smtp.SMTPSendFailedException: 554 5.2.0 STOREDRV.Submission.Exception:InvalidRecipientsException; Failed to process message due to a permanent exception with message A message can't be sent because it contains no recipients. InvalidRecipientsException: A message can't be sent because it contains no recipients. [Hostname=PS2PR02MB2725.apcprd02.prod.outlook.com]

另外outlook邮箱如果没有绑定手机号(需要验证账户),发送邮件也是会报错的。

  • 身份验证失败:
javax.mail.SendFailedException: Send failure (javax.mail.AuthenticationFailedException: 535 5.7.3 Authentication unsuccessful)

这个问题有点坑人啊,线上环境时不时的发不出邮件,后来一看就是报这个错,邮箱账号密码没错啊......

后来经过排查,发现是这个配置的问题,注释掉就可以了。

props.put("mail.smtp.auth", "true");

这个配置的意思是:是否需要身份验证(默认不验证),公司的邮箱服务器是不需要的,所以注释了也可以发,然后session里面的验证信息也不用传了,不需要账号密码。

session = Session.getInstance(props);

至于为什么设置了需要验证,传了账号密码,有时能发邮件,有时发不出那就不知道了.....这里先记录一下,如果哪位大神知道还望指出。

你可能感兴趣的:(使用JavaMail向outlook发送会议邀请(日历)邮件)