本站也是用的国产typecho开发,简单好用,但是对于技术开发人员想去修改里面的一些东西还是比较麻烦,因为不是所有人都懂PHP代码的,顶多在设计页面的时候根据自己的想法,有些改动的问题确实还是需要自己琢磨,搞不懂的地方上网搜一下,但是很多时候网上回答的那些东西不是陈年老旧就是误人子弟,大部分做了无用功,花了时间和经历都没打到想要的效果。

下面原文写的代码运行起来是可以的,但是图片量一多,回导致请求数据缓慢,阻塞页面的加载导致页面的渲染变慢,在做测试的时候我是用的本地测试很快的,没经过线上跑后面才发现这个问题,其中每个页面的渲染时间平均都超过8秒的时间,是因为本站的侧边栏有8张评论者的头像获取,平均下来一个头像就需要 1 秒的时间等待,找到问题之后去掉之前的获取代码,在网上翻找了一圈之后发现很多人也都在说这个问题,后面才翻到一个博主说的解决办法,使用 cravatar 代理支持,可以支持 Typecho 处理头像,引用官网的一句话。

Cravatar 完美兼容所有 Gravatar 头像 API 接口,同时如果你未在 Cravatar 设置头像,则会先尝试调用 Gravatar 上的头像数据,其后是 QQ 头像,最后会返回我们为你准备的一组默认头像

看明白这句话就知道该怎么做了,去掉之前的获取头像加密的方式,改成以下代码。其实下面评论有兄弟指出来问题了,我以为这是头像的地址问题,差不多,没想到两者对页面的访问速度相差十万八千里,这算是一次优化吧。

$qqMail = '1174225038@qq.com';
// $geturl = 'http://ptlogin2.qq.com/getface?&imgtype=1&uin=' . $qqMail;
// $qquser = file_get_contents($geturl);
// $str1 = explode('&k=', $qquser);
// $str2 = explode('&s=', $str1[1]);
// echo 'https://thirdqq.qlogo.cn/g?b=qq&k=' . $str2[0] . '&s=100';

// 使用 md5 处理以下,再拼接到 https://cravatar.cn/avatar/ 网址即可
$hash = md5($qqMail);
echo 'https://cravatar.cn/avatar/' . $hash;

测试以下线上访问速度,也还是很快的。


需要加密原因

这次碰到问题是在用户评论的时候会把自己邮箱绑定,这样也便于本人回复的时候可以及时做邮件通知,第二个,也是为了通过邮箱的形式拿到用户的头像,大家都知道 typecho 默认的头像访问是需要梯子的,不然会访问不上导致加载速度的变慢。所以在默认的情况下我拿到用的邮箱时,做了一个拼接或是 QQ 或者其他,国内的话就用 CDN 加速,如果是 QQ 的话就直接取得头像。如果不是 QQ 的还好,不会涉及到号码的暴露,就是说在动技术的人面前,前端的静态页面直接是可以在控制台查看的,找到头像的地方,查看图片的路径就可以看到用的 QQ 号码了,这样会被有心思的人拿做他图。所以为了避免这样的情况,只需在拿到 QQ 的加密 k 值就好,然后再拼接一起。

加密处理思路

上面也说到直接拿到头像地址拼接(如下),其中 nk 是用户的 QQ 邮箱跟着后面的 s 则是头像大小,具体可以设置100,160,640 三种可选,一般为了减少请求大小可以选 100 大小即可。

https://thirdqq.qlogo.cn/g?b=qq&nk=1174225038&s=100

这个方式是最初的那头像的方式,正如大家所看到的,如果那这个链接放在页面上,链接上的 QQ 显而易见。下面换一个方式,可以看一下如下地址。
注意:下面的值得是 k 而不是上面的 nk

https://thirdqq.qlogo.cn/g?b=qq&k=6juKG2qAgkyUF0p8sJMR5Q&s=100

可以看到地址上把 QQ 邮箱做了一个加密,用户不管怎么操作不能做到反解密的,这样有效的避免了号码暴露的问题。那我是怎么做到加密可以被官方识别的呢?其实很简单,只需在拿到号码之后,通过号码获取解密需要的 k 即可,用到的链接如下。

http://ptlogin2.qq.com/getface?&imgtype=1&uin=1174225038

可以看到我把 QQ 是用过后面拼接到 uin 的字段上了,拿到这个地址之后返回的是一个带有加密的字符 k 值,再通过截取字符,拿到加密值,最后在拼到如上第二个地址上,得到完整的链接就可以放在页面上了。看下面如何实现

获取邮箱号码加密字段

在 PHP 中有两种获取数据的方式(本人不是开发,只是找到的结果)。

  1. 使用 file_get_contents() 函数获取数据
  2. 使用 curl

两种方式各有优劣,但都可以获取。

使用 file_get_contents()

从文档找了一下这个函数的定义,file_get_contents() 把整个文件读入一个字符串中。
该函数是用于把文件的内容读入到一个字符串中的首选方法。如果服务器操作系统支持,还会使用内存映射技术来增强性能。

使用:file_get_contents(path, include_path, context, start, max_length);

我们只需要第一个参数。

$qqMail = '1174225038';
$geturl = 'http://ptlogin2.qq.com/getface?&imgtype=1&uin=' . $qqMail;
$qquser = file_get_contents($geturl);
$str1 = explode('&k=', $qquser);
$str2 = explode('&s=', $str1[1]);
echo 'https://thirdqq.qlogo.cn/g?b=qq&k=' . $str2[0] . '&s=100';

使用 curl

相比 file_get_contents() 的简单,用 curl 就要复杂一点了,可以先封装一个函数处理,然后再调用。

function _curl($url)
{
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, $url);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_NOSIGNAL, 1);
  curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, 3000);
  curl_setopt($ch, CURLOPT_TIMEOUT_MS, 3000);
  if (strpos($url, 'https') !== false) {
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
  }
  curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36');
  $result = curl_exec($ch);
  curl_close($ch);
  return $result;
}

// 下面的代码和使用 file_get_contents() 那段基本一致
function _getAvatarByMail($mail)
{
  // 这里是自己在模板管理做了一个 cdn 配置,如果不是qq邮箱就可以使用其他的方式加载邮箱的头像
  $gravatarsUrl = Helper::options()->CustomAvatarSource ? Helper::options()->CustomAvatarSource : 'https://gravatar.ihuan.me/avatar/';
  $mailLower = strtolower($mail);
  $md5MailLower = md5($mailLower);
  $qqMail = str_replace('@qq.com', '', $mailLower);
  if (strstr($mailLower, "qq.com") && is_numeric($qqMail) && strlen($qqMail) < 11 && strlen($qqMail) > 4) {
    $geturl = 'http://ptlogin2.qq.com/getface?&imgtype=1&uin=' . $qqMail;
    $qquser = _curl($geturl);
    $str1 = explode('&k=', $qquser);
    $str2 = explode('&s=', $str1[1]);
    $k = $str2[0];
    echo 'https://thirdqq.qlogo.cn/g?b=qq&k=' . $k . '&s=100';
  } else {
    echo $gravatarsUrl . $md5MailLower . '?d=mm';
  }
}

把这段代码可以放到处理邮箱的地方,返回的链接数据根据自己的博客内容做填入。