让phpmailer支持中文名称的附件

phpmailer设置使用utf-8编码发送邮件以后,已经能够正常的发送中文邮件了,当然你调用时传进去的中文参数必须也是utf-8编码才行,但是我发现,即使这样,发送中文文件名的附件的时候,附件名称不能正确的显示。

比如我们要发送的附件是“测试.txt”,如果在添加附件的时候强制使用指定文件名的方式:

$mail->AddAttachment($attach, $attach);

那么发送过去的附件文件名将会是乱码,如果不指定:

$mail->AddAttachment($attach, $attach);

那么发送过去的文件名中的中文干脆没了,成了“.txt”。

究其原因,打开class.phpmailer.php,在大概第1007行左右,函数AddAttachment中,有一句

$filename = basename($path);

原因就在这里,现在我们使用的php 5.1.2,包括他以前的很多版本,basename函数和dirname函数都是不支持中文文件名的,所以一解析就把中文给过滤掉了。而如果强行指定文件名为什么还乱码呢,这是因为phpmailer虽然会自动根据你设定的编码方式给主题、正文进行utf-8编码化,但是却不会给附件的文件名编码。现在,只需要修改上面这一句,就能够同时解决这两方面的问题了。修改结果如下:

//$filename = basename($path);
if (false === strpos($path, '/'))
    $filename = $this->EncodeHeader($path);
else
    $filename = $this->EncodeHeader(substr($path, strrpos($path, '/') + 1));

不使用basename函数了,改用自己的方法来得到文件名,并且借用了主题Subject的编码函数EncodeHeader来生成utf-8编码形式的附件名称,搞定。

Update @ 2008-04-14

在phpMailer 2.1.0 Beta 2中,这个问题依然没有得到处理,需要作的修改还是一样,不过代码的位置在1018行附近。

另外在发送中文邮件的时候,中文会出现乱码,看了网上有处理的方式,没有讲原因,也粗暴了点,直接把函数截断了,还要改两个地方。

我看了一下源码,乱码的产生大概是在将邮件标题转成几个小的=?utf-8?B?...?=时,可能是无意中把中文给截断了产生的,所以我的修改更简单而又略微温柔一点,修改第1185行:

$maxlen = 75 - 7 - strlen($this->CharSet);
改成:
$maxlen = 75000 - 7 - strlen($this->CharSet);

就行了,把字符串的值设大点,让它不分段就行了。

终于能够通过phpmailer使用gmail账号发送邮件了

phpmailer(现在的版本是1.73)是一个很好用的工具,可以很方便的使用php语言发送邮件,支持smtp及验证,我们一直都用它。

但是,由于gmail的smtp采用了ssl连接:

Outgoing Mail (SMTP) Server – requires TLS: smtp.gmail.com (use authentication) Use Authentication: Yes Use STARTTLS: Yes (some clients call this SSL) Port: 465 or 587

使用phpmailer就无法正常连接gmail的发信服务器了,并且这个问题一直没有得到phpmailer的官方解决,不过在sf.net上面的讨论里倒是找到了一点资料,采用下面这种方法就可以连接gmail了。

修改class.smtp.php,第101行,把

$this->smtp_conn = fsockopen($host, # the host of the server

改成

$this->smtp_conn = fsockopen(‘ssl://’ . $host, # the host of the server

这样就可以了,就是指定使用ssl协议连接主机,当然php的openssl模块必须打开:

extension=php_openssl.dll

但是这样就不能使用非ssl的主机了,我也不像在include目录下面放两份phpmailer,于是,又琢磨出了一种更好的方式:

打开class.phpmailer.php,在大概543行附近,函数SmtpConnect()中,找到:

$this->smtp->do_debug = $this->SMTPDebug; $hosts = explode(“;”, $this->Host); $index = 0; $connection = ($this->smtp->Connected()); // Retry while there is no connection while($index < count($hosts) && $connection == false) { if(strstr($hosts[$index], ":")) list($host, $port) = explode(":", $hosts[$index]); else { $host = $hosts[$index]; $port = $this->Port; }

这一段的部分功能就是,如果你输入的主机名中带有端口号,自动的识别出来,设想虽好,但就是这部分让我们无法直接在调用的时候使用ssl格式的主机名,因为“ssl://xxx”的形式会被误认为是主机:端口号的形式,另外端口号一般都比较固定,我们手工设置好了,也不必一定要和主机一起赋值,所以在上面的代码后面添加:

//Modify by Fwolf @ 2006-4-14, to enable ssl mail connection $host = $this->Host; $port = $this->Port;

就可以了,使用正常smtp主机的时候,用法不变,使用gmail的时候,使用ssl://smtp.gmail.com作为主机名就可以了,唯一稍微麻烦一些的就是端口需要手工指定——其实也不麻烦。

按照上面的配置更改后,使用gmail账号发送邮件时还会有一条警告信息:

Warning: fgets(): SSL: fatal protocol error in H:\php_includes\phpmailer_ssl\cla ss.smtp.php on line 1024

这各警告信息在php的帮助里面有,好像是在使用ssl连接的时候,读文件读到文件尾的时候出现的问题,需要手工屏蔽,或者人为控制读的长度,我们用最简单的方式禁止警告信息显示就可以了,找到class.smtp.php的1024行,在fgets($this->smtp_conn,515)函数前面加上个@就可以了。

下面是一个完整的使用phpmailer发送邮件的函数:

function send_mail($to_address, $to_name ,$subject, $body, $attach = ”) { //使用phpmailer发送邮件 require_once(“phpmailer/class.phpmailer.php”); $mail = new PHPMailer(); $mail->IsSMTP(); // set mailer to use SMTP $mail->CharSet = ‘utf-8’; $mail->Encoding = ‘base64’; $mail->From = ‘fwolf.mailagent@gmail.com’; $mail->FromName = ‘Fwolf’; //$mail->Sender = ‘fwolf.mailagent@gmail.com’; //$mail->ConfirmReadingTo = ‘fwolf.mailagent@gmail.com’; //回执? $mail->Host = ‘ssl://smtp.gmail.com’; $mail->Port = 465; //default is 25, gmail is 465 or 587 $mail->SMTPAuth = true; $mail->Username = “fwolf.mailagent@gmail.com”; $mail->Password = “xxx”; $mail->>ddAddress($to_address, $to_name); //$mail->AddReplyTo(‘fwolf.mailagent@gmail.com’, “Fwolf”); //针对gmail无用,gmail是In-Reply-To:,phpmailer默认生成的是Reply-to: $mail->WordWrap = 50; if (!empty($attach)) $mail->AddAttachment($attach); $mail->IsHTML(false); $mail->Subject = $subject; $mail->Body = $body; //$mail->AltBody = “This is the body in plain text for non-HTML mail clients”; if(!$mail->Send()) { echo “Mail send failed.\r\n”; echo “Error message: ” . $mail->ErrorInfo . “\r\n”; return false; } else { echo(“Send $attach to $to_name <$to_address> successed.\r\n”); return true; } //echo “Message has been sent”; // }

update @ 2006-11-3

感谢RainChen提供的fgets出错提示的更好解决办法(位置大概在class.smtp.php的1024行): 将:

fgets($this->smtp_conn,515)

改为:

!feof($this->smtp_conn) && $str = fgets($this->smtp_conn,515)

这样的代码就规范多了,极力不提倡使用@来屏蔽错误信息。 (未测试, 但有网友反映这种改法是不行的)

Update @ 2008-04-14

试了最新的phpMailer 2.1.0 Beta 2,不用作任何修改,就可以用gmail账号发送邮件了,官方文档中还给出了例子,和我用的方法不太一样。

不过中文附件的问题依然没有解决,需要自己hack。

我的email客户端软件选择历程

上网6、7年了,电子邮件是我一直在使用的互联网服务之一。记得刚开始的时候,免费邮箱满天飞,自己也随大流申请了不下20个免费邮箱,但用的最多的还是原来的广州飞华--现在的TOM邮箱,随着时间的变迁,继263邮箱告别免费走入收费之后,很多邮箱改收费制了,也有很多邮箱网站干脆关门大吉了,于是我的邮箱只剩下TOM的几个。再后来不知不觉的免费邮箱又复活了,并且容量还一个比一个大,比如126yahoogmail等等。

围绕着邮箱使用的,就是我的客户端软件的选择,选择一个好的电子邮件客户端软件真的很难,尤其是对于我这种不愿抽时间把邮件存成.eml的懒人来说。同时,客户端软件的更换也造成了我大量历史邮件的流失--他们都和旧版软件一起压缩在我硬盘的角落里。

Foxmail 张小龙先生的大作,后来卖了出去,其实在它没有卖之前我就因为一个小问题放弃了它。我收到一封带病毒的邮件,于是NAV就把整个邮件存档文件“隔离”了,没有办法,放弃了。

Outlook Express 因为有了强大的NAV,我也就敢于承担据说OE不安全的风险--事实证明,至少在我的NAV保护下,OE没给我的机器染上过病毒。虽然转换几十个邮件帐户 费了我好长的时间,但OE的确使用上比较方便。OE被淘汰的理由也很简单,因为我重装98系统了,OE的邮件和WINDOWS捆绑在一起真的很别扭。

Becky ! 离开OE,我又描上了Becky,当时它差不多算是国外的独立邮件客户端软件中对中文支持的非常好的了。弃用它的原因已经忘记了,不过不是因为它是日货 :-)。

DreamMail 再一次国产软件上场了,这是一款由国人开发的,非常优秀和符合中国人的软件,用起来比较顺手,就是速度有点慢,因为它使用ACCESS存储邮件(现在似乎有所改观),也差不多是因为这个原因,加上中间有段时间作者几乎停止了更新,我很遗憾的又一次改变了方向。

The Bat! 大名鼎鼎的蝙蝠终于来了,大约是2004年末的时候,我终于发现蝙蝠对中文的兼容差不多OK了,就抓了一只回来。不过现在宽带的普及使得蝙蝠的速度优势并 不那么明显,而且它的邮件加密功能我基本用不到,最致命的是,在我重新安装WINDOWS XP后,旧邮件及帐号又一次失踪了。我原以为这个老战士应该不会有这种问题发生的,所以才会容忍它那种奇怪的安装方式,并且说实在的,蝙蝠对中文还是有那 么一点陌生。我又一次打开了Google,搜索,搜索。。。

Thunderbird 最近这只比较轰动的雷鸟落入了我的眼中,在实在没有其它选择的情况下我强迫自己来看看它到底如何。雷鸟选项设定的方式我很不习惯,但静下心来还是很快就搞懂了,现在还不是很成熟,但我觉得Mozilla这面大旗是我们可以信任的,留给时间去证明吧。Thunderbird需要改进的地方真的很多,至少目前的模板功能比上面任何一款都差,并且我安装的英文版也不支持中文搜索,但在没有足够的原因和诱惑的时候,我想先使用一段时间,毕竟它还在完善之中,并且又是跨平台的、免费的好东西。RSS阅读我是不会用的了,因为我没有本本,不可能总在一台机器上看RSS,又不想把自己的订阅阅读的进度记下来,交给Bloglines处理吧。

? 下一个会是谁?我不知道,不过已经有人从Thunderbird过渡到GMail了,我应该是不会的,因为在我这里,GMail比较慢,比较慢。

BTW: Jan从Outlook直接跳到了Thunderbird,比我效率高多了。

Update @ 2008-07-02

从2006年底换用mutt,至今未变。

参考