PHP实现Outlook日历添加事件失败技术求助
PHP生成Outlook日历邀请失败问题排查
近2-3天尝试用PHP代码向Outlook日历添加事件,始终失败。收到的邮件格式异常,直接显示HTML代码和iCal内容,无法被识别为日历邀请。以下是相关代码及收到的邮件内容,需排查问题:
我的PHP代码
$domain = 'dopasi.org'; $from_name = 'FromName'; // From $from_address = 'fromEmail@gmail.com'; // From Email $to_name = 'ToName'; $to_address = 'toEmail@dopasi.org'; $startTime = '13/11/2023' . ' ' . '11:00'; $endTime = '13/11/2023' . ' ' . '12:00'; $subject = 'Meeting Title'; $description = 'Meeting Description'; $location = "TestCity"; //Create Email Headers $mime_boundary = "----Meeting Booking----".MD5(TIME()); $headers = "From: ".$from_name." <".$from_address.">\n"; $headers .= "Reply-To: ".$from_name." <".$from_address.">\n"; $headers .= "MIME-Version: 1.0\n"; $headers .= "Content-Type: multipart/alternative; boundary=\"$mime_boundary\"\n"; $headers .= "Content-class: urn:content-classes:calendarmessage\n"; //Create Email Body (HTML) $message = "--$mime_boundary\r\n"; $message .= "Content-Type: text/html; charset=UTF-8\n"; $message .= "Content-Transfer-Encoding: 8bit\n\n"; $message .= "<html>\n"; $message .= "<body>\n"; $message .= '<p>Dear '.$to_name.',</p>'; $message .= '<p>'.$description.'</p>'; $message .= "</body>\n"; $message .= "</html>\n"; $message .= "--$mime_boundary\r\n"; $ical = 'BEGIN:VCALENDAR' . "\r\n" . 'PRODID:-//Microsoft Corporation//Outlook 10.0 MIMEDIR//EN' . "\r\n" . 'VERSION:2.0' . "\r\n" . 'METHOD:REQUEST' . "\r\n" . 'BEGIN:VTIMEZONE' . "\r\n" . 'TZID:Asia/Karachi' . "\r\n" . 'BEGIN:STANDARD' . "\r\n" . 'DTSTART:20231114T020000' . "\r\n" . 'RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=11' . "\r\n" . 'TZOFFSETFROM:-0400' . "\r\n" . 'TZOFFSETTO:-0500' . "\r\n" . 'TZNAME:EST' . "\r\n" . 'END:STANDARD' . "\r\n" . 'BEGIN:DAYLIGHT' . "\r\n" . 'DTSTART:20231114T020000' . "\r\n" . 'RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=2SU;BYMONTH=3' . "\r\n" . 'TZOFFSETFROM:-0500' . "\r\n" . 'TZOFFSETTO:-0400' . "\r\n" . 'TZNAME:EDST' . "\r\n" . 'END:DAYLIGHT' . "\r\n" . 'END:VTIMEZONE' . "\r\n" . 'BEGIN:VEVENT' . "\r\n" . 'ORGANIZER;CN="'.$from_name.'":MAILTO:'.$from_address. "\r\n" . 'ATTENDEE;CN="'.$to_name.'";ROLE=REQ-PARTICIPANT;RSVP=TRUE:MAILTO:'.$to_address. "\r\n" . 'LAST-MODIFIED:' . date("Ymd\TGis") . "\r\n" . 'UID:'.date("Ymd\TGis", strtotime($startTime)).rand()."@".$domain."\r\n" . 'DTSTAMP:'.date("Ymd\TGis") . "\r\n" . 'DTSTART;TZID="Asia/Karachi":'.date("Ymd\THis", strtotime($startTime)) . "\r\n" . 'DTEND;TZID="Asia/Karachi":'.date("Ymd\THis", strtotime($endTime)) . "\r\n" . 'TRANSP:OPAQUE'. "\r\n" . 'SEQUENCE:1'. "\r\n" . 'SUMMARY:' . $subject . "\r\n" . 'LOCATION:' . $location . "\r\n" . 'CLASS:PUBLIC'. "\r\n" . 'PRIORITY:5'. "\r\n" . 'BEGIN:VALARM' . "\r\n" . 'TRIGGER:-PT15M' . "\r\n" . 'ACTION:DISPLAY' . "\r\n" . 'DESCRIPTION:Reminder' . "\r\n" . 'END:VALARM' . "\r\n" . 'END:VEVENT'. "\r\n" . 'END:VCALENDAR'. "\r\n"; $message .= 'Content-Type: text/calendar;name="meeting.ics";method=REQUEST'."\n"; $message .= "Content-Transfer-Encoding: 8bit\n\n"; $message .= $ical; $mailsent = mail($to_address, $subject, $message, $headers); echo ($mailsent) ? 'Success' : 'Failed';
收到的邮件内容
------Meeting Booking----fcf0f13b816a3795030bbfc724382b1d Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: 8bit <html> <body> <p>Dear ToName,</p><p>Meeting Description</p></body> </html> ------Meeting Booking----fcf0f13b816a3795030bbfc724382b1d Content-Type: text/calendar; name="meeting.ics";method=REQUEST Content-Transfer-Encoding: 8bit BEGIN:VCALENDAR PRODID:-//Microsoft Corporation//Outlook 10.0 MIMEDIR//EN VERSION:2.0 METHOD:REQUEST BEGIN:VTIMEZONE TZID: Asia/Karachi BEGIN:STANDARD DTSTART:20231114T020000 RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=11 TZOFFSETFROM:-0400 TZOFFSETTO:-0500 TZNAME:EST END:STANDARD BEGIN:DAYLIGHT DTSTART:20231114T020000 RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=2SU;BYMONTH=3 TZOFFSETFROM:-0500 TZOFFSETTO:-0400 TZNAME:EDST END:DAYLIGHT END:VTIMEZONE BEGIN:VEVENT ORGANIZER;CN="fromName":MAILTO:fromEmail@gmail.com ATTENDEE;CN="toName";ROLE=REQ-PARTICIPANT;RSVP=TRUE:MAILTO:toEmail#gmail.com LAST-MODIFIED:20231114T112000 UID:19700101T500002143262541@mydomain.com DTSTAMP:20231114T112000 DTSTART;TZID="Asia/Karachi":19700101T050000 DTEND;TZID="Asia/Karachi":19700101T050000 TRANSP:OPAQUE SEQUENCE:1 SUMMARY: Meeting Title LOCATION: TestCity CLASS:PUBLIC PRIORITY:5 BEGIN:VALARM TRIGGER:-PT15M ACTION:DISPLAY DESCRIPTION: Reminder END:VALARM END:VEVENT END:VCALENDAR
问题排查与修复方案
核心问题点
- MIME类型错误:使用
multipart/alternative类型,日历邀请应使用multipart/mixed——前者用于提供内容的不同表现形式(如HTML和纯文本),后者用于包含附件类独立内容。 - 日期解析失败:
startTime采用日/月/年格式,strtotime无法正确识别,导致事件时间被解析为Unix纪元起始时间(1970-01-01)。 - MIME边界格式错误:
- 部分换行符混用
\n和\r\n,导致邮件结构解析异常 - 邮件末尾未添加闭合边界(
--$mime_boundary--)
- 部分换行符混用
- 时区配置错误:
VTIMEZONE中DAYLIGHT和STANDARD的DTSTART重复,且时区名称(EST/EDST)与Asia/Karachi不匹配- Asia/Karachi时区无夏令时,无需配置DAYLIGHT部分
修复后的代码
$domain = 'dopasi.org'; $from_name = 'FromName'; $from_address = 'fromEmail@gmail.com'; $to_name = 'ToName'; $to_address = 'toEmail@dopasi.org'; // 使用年/月/日格式确保strtotime正确解析 $startTime = '2023-11-13 11:00'; $endTime = '2023-11-13 12:00'; $subject = 'Meeting Title'; $description = 'Meeting Description'; $location = "TestCity"; $mime_boundary = "----Meeting Booking----" . MD5(TIME()); // 更换为multipart/mixed类型 $headers = "From: $from_name <$from_address>\r\n"; $headers .= "Reply-To: $from_name <$from_address>\r\n"; $headers .= "MIME-Version: 1.0\r\n"; $headers .= "Content-Type: multipart/mixed; boundary=\"$mime_boundary\"\r\n"; $headers .= "Content-class: urn:content-classes:calendarmessage\r\n"; $message = "--$mime_boundary\r\n"; $message .= "Content-Type: text/html; charset=UTF-8\r\n"; $message .= "Content-Transfer-Encoding: 8bit\r\n\r\n"; $message .= "<html>\r\n<body>\r\n"; $message .= "<p>Dear $to_name,</p>\r\n"; $message .= "<p>$description</p>\r\n"; $message .= "</body>\r\n</html>\r\n"; $message .= "--$mime_boundary\r\n"; $message .= "Content-Type: text/calendar; name=\"meeting.ics\"; method=REQUEST\r\n"; $message .= "Content-Transfer-Encoding: 8bit\r\n\r\n"; // Asia/Karachi无夏令时,简化时区配置 $ical = "BEGIN:VCALENDAR\r\n"; $ical .= "PRODID:-//Microsoft Corporation//Outlook 10.0 MIMEDIR//EN\r\n"; $ical .= "VERSION:2.0\r\n"; $ical .= "METHOD:REQUEST\r\n"; $ical .= "BEGIN:VTIMEZONE\r\n"; $ical .= "TZID:Asia/Karachi\r\n"; $ical .= "BEGIN:STANDARD\r\n"; $ical .= "DTSTART:19700101T000000\r\n"; $ical .= "TZOFFSETFROM:+0500\r\n"; $ical .= "TZOFFSETTO:+0500\r\n"; $ical .= "TZNAME:PKT\r\n"; $ical .= "END:STANDARD\r\n"; $ical .= "END:VTIMEZONE\r\n"; $ical .= "BEGIN:VEVENT\r\n"; $ical .= "ORGANIZER;CN=\"$from_name\":MAILTO:$from_address\r\n"; $ical .= "ATTENDEE;CN=\"$to_name\";ROLE=REQ-PARTICIPANT;RSVP=TRUE:MAILTO:$to_address\r\n"; $ical .= "LAST-MODIFIED:" . date("Ymd\TGis") . "\r\n"; $ical .= "UID:" . date("Ymd\TGis", strtotime($startTime)) . rand() . "@$domain\r\n"; $ical .= "DTSTAMP:" . date("Ymd\TGis") . "\r\n"; $ical .= "DTSTART;TZID=\"Asia/Karachi\":" . date("Ymd\THis", strtotime($startTime)) . "\r\n"; $ical .= "DTEND;TZID=\"Asia/Karachi\":" . date("Ymd\THis", strtotime($endTime)) . "\r\n"; $ical .= "TRANSP:OPAQUE\r\n"; $ical .= "SEQUENCE:1\r\n"; $ical .= "SUMMARY:$subject\r\n"; $ical .= "LOCATION:$location\r\n"; $ical .= "CLASS:PUBLIC\r\n"; $ical .= "PRIORITY:5\r\n"; $ical .= "BEGIN:VALARM\r\n"; $ical .= "TRIGGER:-PT15M\r\n"; $ical .= "ACTION:DISPLAY\r\n"; $ical .= "DESCRIPTION:Reminder\r\n"; $ical .= "END:VALARM\r\n"; $ical .= "END:VEVENT\r\n"; $ical .= "END:VCALENDAR\r\n"; $message .= $ical; // 添加闭合边界 $message .= "--$mime_boundary--\r\n"; $mailsent = mail($to_address, $subject, $message, $headers); echo $mailsent ? 'Success' : 'Failed';
内容的提问来源于stack exchange,提问作者Khurshid
相关产品推荐
相关产品推荐

