iOS - 第三方库SKPSMTPMessage的各种坑

2,180 阅读6分钟

因为最近公司业务需求,需要用到静默方式发送邮件的功能,所以接触到了SKPSMTPMessage这个第三方库。话说iOS有关SMTP封装的第三方库真的好少惹,仅有的这么一个库还年久失修,网上的资料也较少而且很多有误的,导致使用过程中真的遇到了非常多的坑,所以在这里跟大家分享一下我使用过程中遇到的问题跟解决方案,希望对大家有所帮助,如有不对的地方欢迎指正哈。比心❤️😁

基本使用:

 SKPSMTPMessage *Msg = [[SKPSMTPMessage alloc] init];
    Msg.fromEmail = @"发件人邮箱";
    Msg.toEmail = @"收件人邮箱";
    Msg.relayHost = @"smtp.126.com";//发送邮件代理服务器(这里我用的是126邮箱做的示范)
    Msg.requiresAuth = YES;//是否需要认证(登陆)
    Msg.login = @"发件人邮箱账号";
    Msg.pass = @"密码或者授权码";//
    Msg.delegate = self;
    Msg.wantsSecure = YES;//是否用ssl
    //Msg.validateSSLChain = NO;//是否验证SSL证书
    Msg.subject = @"邮件标题";//邮件标题
    
    //1.邮件内容: 正文文字
    NSDictionary *plainPart = [NSDictionary dictionaryWithObjectsAndKeys:@"text/plain; charset=UTF-8",kSKPSMTPPartContentTypeKey,
                               @"邮件由系统自动发送,请勿回复。",kSKPSMTPPartMessageKey,@"8bit",kSKPSMTPPartContentTransferEncodingKey,nil];
    dispatch_async(dispatch_get_main_queue(), ^{
        //发送邮件
        [Msg send];
    });


发送之后,会调用delegate方法。
//成功,调用:
- (void)messageSent:(SKPSMTPMessage *)message;
//失败,调用:
- (void)messageFailed:(SKPSMTPMessage *)message error:(NSError *)error;

一、SMTP服务器连接失败的问题

网上很多教程都是直接开启多线程发送,但经我测试行不通,无法连接服务器。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
 [Msg send];
});

提示错误:SMTP服务器连接失败

C: Attempting to connect to server at:smtp.126.com:2025**

解决方法: 1.需要在后面再加一个运行循环: [[NSRunLoop currentRunLoop] run]; 因为如果创建新线程,runloop不会自动运行; 我们必须手动启动,要不然重试其他端口的操作不会进行。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

 [Msg send];
 [[NSRunLoop currentRunLoop] run];

 });

2.或者在主线程中发送邮件

 dispatch_async(dispatch_get_main_queue(), ^{
 [myMsg send];
 });

二、开启 SMTP 服务与授权码的问题

1.发送者邮箱需要开启SMTP服务,不然邮件无法从发送方邮箱发出。

 Msg.relayHost = @"smtp.126.com";
//发送邮件代理服务器(这里我用的是126邮箱做的示范,如果你用的是公司邮箱那就填的公司的SMTP服务器地址)

2.目前普遍的一些第三方邮箱(如QQ、163邮箱等)现在都需要设置授权码才能正常发送邮件。

 Msg.pass = @"授权码";

这是126邮箱开启授权码的示范,在邮箱设置即可:

授权码.png

  1. 这里注意一下:像我使用的是公司服务器搭建的邮箱,我们公司并没有设置授权码,所以我这种情况填写邮箱密码即可。
 Msg.pass = @"邮箱密码";

三、邮件中文乱码的问题

1.标题中文乱码

导致乱码的原因是因为SKPSMTPMessage库之前使用的是ASCII的编码,所以我们把它改为utf8即可。

  //我们只需要将SKPSMTPMessage.m里以下这句代码:
NSData *messageData = [message dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];

 //改为这句代码就好了
NSData *messageData = [message dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];

2.附件中文标题乱码

这里我用发送图片附件做的示范

代码修改前:

UIImage *image = [UIImage imageNamed:@"WechatIMG372"];
    //图片大于1M时建议做压缩处理
NSData *imgData = UIImageJPEGRepresentation(image,1.0f);
   
NSDictionary *imgPart = [NSDictionary dictionaryWithObjectsAndKeys:@"image/jpg;\r\n\tx-unix-mode=0644;\r\n\tname=\"图片附件名.jpg\"",kSKPSMTPPartContentTypeKey,
                                   @"attachment;\r\n\tfilename=\"图片附件名.jpg\"",kSKPSMTPPartContentDispositionKey,[imgData encodeBase64ForData],kSKPSMTPPartMessageKey,@"base64",kSKPSMTPPartContentTransferEncodingKey,nil];
    

代码修改后:

UIImage *image = [UIImage imageNamed:@"WechatIMG372"];
    //图片大于1M时建议做压缩处理
NSData *imgData = UIImageJPEGRepresentation(image,1.0f);
NSString *encodeFileName = [NSString stringWithFormat:@"=?UTF-8?B?%@?=",[[@"附件标题名" dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0]];
    
    //解决附件中文乱码的问题
NSDictionary *imagePart = [NSDictionary dictionaryWithObjectsAndKeys: [NSString stringWithFormat:@"image/jpg;\r\n\tx-unix-mode=0644;\r\n\tname=\"%@.jpg\"",encodeFileName],kSKPSMTPPartContentTypeKey,
                               [NSString stringWithFormat:@"attachment;\r\n\tfilename=\"%@\"",encodeFileName],kSKPSMTPPartContentDispositionKey,[imgData encodeBase64ForData],kSKPSMTPPartMessageKey,@"base64",kSKPSMTPPartContentTransferEncodingKey,nil];

可以看到以上代码重点是修改了filename,对其进行处理。 例如:这个是邮件头的编码格式,B表示是Base64编码,UTF-8表示字符集的编码。

=?UTF-8?B?6ZmE5Lu25qCH6aKY5ZCN?=

使用这个就可以使附件的中文名不再乱码。

四、证书链验证失败的问题

在我将上面的坑都填完之后,使用第三方邮箱来收发邮箱都是没有问题的,但是在我使用公司邮箱作为发件者邮箱的时候却报了这个错误:

error - Error Domain=NSOSStatusErrorDomain Code=-9807 "(null)" UserInfo={_kCFStreamErrorCodeKey=-9807, _kCFStreamErrorDomainKey=3}

网上没有找到这个问题的解决方法,只有有关于9807这个错误的解释:

  1. 客户端的https证书不对
  2. 客户端的时间不对

因为我们公司邮箱是用的自己的服务器,公司的服务器是没有做相关证书验证的,所以基本能确定是是证书的原因了。SKPSMTPMessage库里有为我们提供一个BOOL值的属性:validateSSLChain,这个属性是用来验证证书链的,默认是YES;我将他改为NO邮件就顺利发送成功了。

  myMsg.validateSSLChain = NO;//关闭证书链的验证

五、如何在邮件正文里插入图片

废话不多说先上代码

    //4.正文插入图片
    //4.1图片转base64字符串
    NSString *baseStr = [imgData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
    
    //4.2 正文文本html
    NSString *str = @"<p style=\"color:black\">正文测试测试测试</p>";
    ///</p><img src=\"data:image/png;base64," ;
    
    //4.3 图片html
    NSString *str1 = @"<img src=\"data:image/png;base64,";
    NSString *str2 = @"\" />";
    //将图片base64字符串与html标签拼接
    NSString *imgHtml = [NSString stringWithFormat:@"%@%@%@",str1,baseStr,str2];
    
    //4.4 拼接出正文加图片的html字符串
    NSString *body = [NSString stringWithFormat:@"%@%@",str,imgHtml];
    
    NSDictionary *htmlPart = [NSDictionary dictionaryWithObjectsAndKeys:@"text/html;charset=UTF-8",kSKPSMTPPartContentTypeKey,
                              body,kSKPSMTPPartMessageKey,@"8bit",kSKPSMTPPartContentTransferEncodingKey,nil];
    Msg.parts = [NSArray arrayWithObjects:htmlPart,nil];

这个问题我网上找了很久都没有找到解决方案,因为SKPSMTPMessage库part的元素字典一个key只能单独设置一种格式,像 text/plain或者image/jpg等;同时设置2个格式的话会报错,试了很多方法都不行,最后换了个思路使用text/html格式解决了这个问题。

1.我们要把text/plain这个key替换成text / html

//body: html文本
NSDictionary *htmlPart = [NSDictionary dictionaryWithObjectsAndKeys:@"text/html;charset=UTF-8",kSKPSMTPPartContentTypeKey,
                          body,kSKPSMTPPartMessageKey,@"8bit",kSKPSMTPPartContentTransferEncodingKey,nil];

2.使用html标签拼接出正文跟图片

起初我是用富文本生成html字符串的,但是这种方式生成的html字符串发送过去显示一堆乱码,后来就直接改用html标签来实现了,实现过程有点low,大家如果有更好的实现方法也可以在评论下方告诉我。

//1.文本html标签
"<p style=\"color:black\">正文文本文字</p>";

//2.图片html标签
<img src="data:image/png;base64,图片base64字符串" />

//3.把文本html跟图片html按照你的需求拼接即可

3.上面代码最终实现效果:(我使用的QQ邮箱做的收件邮箱)

邮件嵌入图片.jpeg

参考链接: jeffssss.github.io/ios/2016/01…