最近工作中遇到一个问题,本来很简单的一个问题,困扰了我2周
具体:java发送会议邮件到exchange服务器
奇怪:系统有80多家客户,基本大半都有会议邮件的发送,不管是outlook,网易闪电邮,Foxmail另外wps邮箱都可以接收到会以邮件
只有exchange不能接收到会议邮件,收到的只是
BEGIN:VCALENDAR PRODID:-//Events Calendar//iCal4j 1.0//EN CALSCALE:GREGORIAN VERSION:2.0 METHOD:REQUEST BEGIN:VEVENT DTSTAMP:20160805T115502Z DTSTART:20160808T160000 DTEND:20160808T223000 SUMMARY:test LOCATION:test UID:3f3acf56-0b54-4963-bba2-f02d2c222a8c ORGANIZER:1 SEQUENCE:0 ATTENDEE;ROLE=OPT-PARTICIPANT;CN=Developer1:mailto:[email protected] BEGIN:VALARM TRIGGER:-PT10M REPEAT:1 DURATION:PT10M SUMMARY:Event Alarm ACTION:DISPLAY DESCRIPTION:Progress Meeting at 9:30am END:VALARM END:VEVENT END:VCALENDAR
并且邮件中的正文,已附件的形式存在,并且看了网上很多类似的解决方式,综合起来的代码
/** * 发送邀请 * @param cycleModel */ protected void sendEmail(CycleMailModel cycleModel) { Properties properties = new Properties(); properties.put("mail.transport.protocol", "smtp"); properties.put("mail.smtp.user", "smtp.XXX.com"); properties.put("mail.smtp.host", cycleModel.getEmailHost()); properties.put("mail.smtp.port", "25"); properties.put("mail.smtp.auth", "true"); final String username = cycleModel.getEmailAddress(); final String password = cycleModel.getEmailPass(); Authenticator authenticator = new Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(username, password); } }; Transport transport = null; try { Session session = Session.getDefaultInstance(properties, authenticator); MimeMessage mimeMessage = new MimeMessage(session); mimeMessage.setSubject(cycleModel.getSubject()); if(null != attendee && attendee.size() > 0){ for (int i = 0; i < attendee.size(); i++) { JSONObject mail = attendee.getJSONObject(i); if(mail.containsKey("address")){ mimeMessage.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(mail.getString("address"))); } } } mimeMessage.setFrom(new InternetAddress(username)); //method=REQUEST; mimeMessage.setSentDate(new Date()); mimeMessage.setContent(getContentText(cycleModel)); /*if(cycleModel.getMethodType() == 1){ }else{ mimeMessage.setContent(cancelVEvent(cycleModel)); }*/ mimeMessage.saveChanges(); transport = session.getTransport("smtp"); transport.connect("mail.sfsctech.com",username, password); transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients()); } catch (MessagingException e) { // TODO Auto-generated catch block JSONObject exception = JSONObject.fromObject(e); //异常邮箱 if(exception.containsKey("invalidAddresses")){ JSONArray invalidAddresses = exception.getJSONArray("invalidAddresses"); if(null != invalidAddresses && invalidAddresses.size() > 0){ for (int i = 0; i < invalidAddresses.size(); i++) { //修改发送失败状态 JSONObject mailObject = invalidAddresses.getJSONObject(i); this.dao.updateInvalidAddressesState(cycleModel.getPrimaryKey(), mailObject.getString("address"),2,2); } logger.error("异常邮箱:"+invalidAddresses.toString()); } } //被退回邮箱 if(exception.containsKey("validUnsentAddresses")){ JSONArray validUnsentAddresses = exception.getJSONArray("validUnsentAddresses"); if(validUnsentAddresses.size() > 0){ this.attendee=validUnsentAddresses; sendEmail(cycleModel); System.out.println("会议邮件 发送失败,再次发送"); } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { if (transport != null) try { transport.close(); } catch (MessagingException logOrIgnore) { } } } /** * 邀请内容 * @param cycleModel * @return * @throws Exception */ protected Multipart getContentText(CycleMailModel cycleModel) throws Exception { // 时区 /* * TimeZoneRegistry registry = TimeZoneRegistryFactory.getInstance() * .createRegistry(); TimeZone timezone = * registry.getTimeZone("Asia/Shanghai"); // 会议地点 // 会议时间 * java.util.Calendar cal = java.util.Calendar.getInstance(); * * cal.setTimeZone(timezone); cal.set(2015, 6, 2, 14, 0); // 月份是要早一个月 * DateTime start = new DateTime(cal.getTime()); cal.set(2015, 6, 2, 14, * 30); DateTime end = new DateTime(cal.getTime()); */ 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); DateTime start = new DateTime(format.parse(cycleModel.getBeginTime().substring(0, 16)).getTime()); DateTime end = new DateTime(format.parse(cycleModel.getEndTime().substring(0, 16)).getTime()); VEvent vevent = new VEvent(start, end, cycleModel.getSubject()); vevent.getProperties().add(new Location(cycleModel.getLocation()));// 会议地点 vevent.getProperties().add(new Uid(cycleModel.getEventUid())); Organizer org = new Organizer(URI.create("mailto:" + cycleModel.getEmailAddress())); vevent.getProperties().add(org); //主键 Sequence sequence = new Sequence(cycleModel.getSequenceId()); vevent.getProperties().add(sequence); if(cycleModel.getEventType() == 2){ String endTime = cycleModel.getBeginTime().substring(0, 10) + " " + cycleModel.getEndTime().substring(11, 16); vevent.getEndDate().setDate(new DateTime(format.parse(endTime))); //添加事件规则 天、周 vevent.getProperties().add(getRRule(cycleModel)); } // 与会人 int i = 1; for (int j = 0; j < this.attendee.size(); j++) { JSONObject emailObject = this.attendee.getJSONObject(j); if(emailObject.containsKey("address")){ Attendee attendee = new Attendee(URI.create("mailto:" + emailObject.getString("address"))); attendee.getParameters().add(Role.OPT_PARTICIPANT); attendee.getParameters().add(new Cn("Developer" + i)); vevent.getProperties().add(attendee); i++; } } // --------VEvent Over---------- // --------VAlarm Start---------- // 提醒,提前10分钟 VAlarm valarm = new VAlarm(new Dur(0, 0, -10, 0)); valarm.getProperties().add(new Repeat(1)); valarm.getProperties().add(new Duration(new Dur(0, 0, 10, 0))); // 提醒窗口显示的文字信息 valarm.getProperties().add(new Summary("Event Alarm")); valarm.getProperties().add(Action.DISPLAY); valarm.getProperties().add( new Description("Progress Meeting at 9:30am")); // --------VAlarm Over------------- // --------日历对象 Start--------------- Calendar icsCalendar = new Calendar(); icsCalendar.getProperties().add( new ProdId("-//Events Calendar//iCal4j 1.0//EN")); icsCalendar.getProperties().add(CalScale.GREGORIAN); icsCalendar.getProperties().add(Version.VERSION_2_0); String method = "REQUEST"; //新增或者编辑 if(cycleModel.getMethodType().equals(1)){ vevent.getAlarms().add(valarm);// 将VAlarm加入VEvent icsCalendar.getProperties().add(Method.REQUEST); }else{ icsCalendar.getProperties().add(Method.CANCEL); method = "CANCEL"; } icsCalendar.getComponents().add(vevent);// 将VEvent加入Calendar icsCalendar.validate(); // 将日历对象转换为二进制流 CalendarOutputter co = new CalendarOutputter(false); ByteArrayOutputStream os = new ByteArrayOutputStream(); co.output(icsCalendar, os); byte[] mailbytes = os.toByteArray(); // --------日历对象 Over------------------ BodyPart mbp = new MimeBodyPart(); mbp.setContent(mailbytes, "text/calendar;method="+method+";charset=UTF-8"); System.out.println("会议邮件 method = "+method); BodyPart content = new MimeBodyPart(); content.setContent(cycleModel.getBody(), "text/html;charset=UTF-8"); System.out.println("会议邮件 Body = "+cycleModel.getBody()); MailcapCommandMap mc = (MailcapCommandMap) CommandMap .getDefaultCommandMap(); mc .addMailcap("text/html;; x-java-content-handler=com.sun.mail.handlers.text_html"); mc .addMailcap("text/xml;; x-java-content-handler=com.sun.mail.handlers.text_xml"); mc .addMailcap("text/plain;; x-java-content-handler=com.sun.mail.handlers.text_plain"); mc .addMailcap("multipart/*;; x-java-content-handler=com.sun.mail.handlers.multipart_mixed"); mc .addMailcap("message/rfc822;; x-java-content-handler=com.sun.mail.handlers.message_rfc822"); CommandMap.setDefaultCommandMap(mc); MimeMultipart mm = new MimeMultipart(); mm.setSubType("related"); mm.addBodyPart(mbp); mm.addBodyPart(content); return mm; }
BodyPart对象不管怎么转码都不能解决问题,并且exchange的服务器设置也没有什么特殊的地方,也没有过滤什么东西,这是为什么呢?
网上找了两种方法:用ical4j和不用
我这里用的就是ical4j,但是不行,不知道其他人是怎么做的
然后我就尝试不用ical4j
protected void sendEmail2(CycleMailModel cycleModel) { try { Properties props = new Properties(); try { props.put("mail.smtp.port", "25"); // props.put("mail.smtp.host", "smtp.xxx.com"); props.put("mail.smtp.host", cycleModel.getEmailHost()); 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"); } catch (Exception e) { e.printStackTrace(); } String fromEmail = cycleModel.getEmailAddress(); String toEmail = this.attendee.getJSONObject(0).getString("address"); Session session; final String username = cycleModel.getEmailAddress(); final String password = cycleModel.getEmailPass(); class EmailAuthenticator extends Authenticator { protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(username, password); } } Authenticator authenticator = new EmailAuthenticator(); 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(cycleModel.getSubject()); StringBuffer buffer = new StringBuffer(); DateFormat format1 = new SimpleDateFormat("yyyy-MM-dd HH:mm"); DateFormat format2 = new SimpleDateFormat("yyyyMMdd:HHmmss"); String beginTime = format2.format(format1.parse(cycleModel.getBeginTime())); beginTime = beginTime.replace(":", "T"); String endTime = format2.format(format1.parse(cycleModel.getEndTime())); endTime = endTime.replace(":", "T"); 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:"+toEmail+"\n" + "ORGANIZER:MAILTO:"+toEmail+"\n" + "DTSTART:" + beginTime+"\n" + "DTEND:" + endTime+"\n" // + "LOCATION:该邮件只是为了在中outlook日历中您请假、外出的情况,无需回复或接受!\n" + "UID:"+UUID.randomUUID().toString()+"\n"//如果id相同的话,outlook会认为是同一个会议请求,所以使用uuid。 + "CATEGORIES:SuccessCentral Reminder\n" + "DESCRIPTION:Remind\n\n" + "SUMMARY:Test meeting request\n" + "PRIORITY:5\n" + "CLASS:PUBLIC\n" + "BEGIN:VALARM\n" + "TRIGGER:-PT15M\n" + "ACTION:DISPLAY\n" + "DESCRIPTION:Reminder\n" + "END:VALARM\n" + "END:VEVENT\n" + "END:VCALENDAR"); BodyPart messageBodyPart = new MimeBodyPart(); // 测试下来如果不这么转换的话,会以纯文本的形式发送过去, //如果没有method=REQUEST;charset=\"UTF-8\",outlook会议附件的形式存在,而不是直接打开就是一个会议请求 messageBodyPart.setDataHandler(new DataHandler(new ByteArrayDataSource(buffer.toString(), "text/calendar;method=REQUEST;charset=\"gb2312\""))); System.out.println("邮件:"+buffer.toString()); Multipart multipart = new MimeMultipart(); multipart.addBodyPart(messageBodyPart); message.setContent(multipart); Transport.send(message); } catch (MessagingException me) { // TODO Auto-generated catch block JSONObject exception = JSONObject.fromObject(me); //异常邮箱 if(exception.containsKey("invalidAddresses")){ JSONArray invalidAddresses = exception.getJSONArray("invalidAddresses"); if(null != invalidAddresses && invalidAddresses.size() > 0){ for (int i = 0; i < invalidAddresses.size(); i++) { //修改发送失败状态 JSONObject mailObject = invalidAddresses.getJSONObject(i); this.dao.updateInvalidAddressesState(cycleModel.getPrimaryKey(), mailObject.getString("address"),2,2); } logger.error("异常邮箱:"+invalidAddresses.toString()); } } //被退回邮箱 if(exception.containsKey("validUnsentAddresses")){ JSONArray validUnsentAddresses = exception.getJSONArray("validUnsentAddresses"); if(validUnsentAddresses.size() > 0){ this.attendee=validUnsentAddresses; sendEmail2(cycleModel); System.out.println("会议邮件 发送失败,再次发送"); } } me.printStackTrace(); } catch (Exception ex) { ex.printStackTrace(); } }
这样做了,竟然就可以接收到了,我很无语
仔细看代码,没有ical4j的日历对象,也没有把邮件的正文或者日历对象转成 text/html;charset=UTF-8
为什么就可以了呢,很不解,并且这样的写法,其他的邮件服务器也是可以用的
时间: 2024-10-14 10:51:02