起因
wordpress
的头像加载一般是最头疼的地方。原因有三,其一是头像缓存的服务器在国外。所以国内使用的话,网络慢得可怜;其二是可以使用国内的头像缓存服务,但是指不定哪天就给关闭了;其三,我们可以直接禁用该服务,目前如果我们需要此服务的话,就只能使用默认头像。所以我想到,是不是可以自己写一个服务去缓存这些头像。就像CDN
一样。
Gravatar是一图像跟随著您到访过的网站,当您在博客中留言或发表文章,它将会出现在您的名称旁。头像协助识别您在博客和论坛发表的文章,何乐而不为呢?
cn.gravatar.com
当然,我们也可以直接百度一下,查找本地缓存的例子。博主也查了一下。大部分的代码都年久失修,直接拿来用是不可能的。而他们的一些例子基本上都是写在插件里面,或者functions.php
里面。其实这样做也是不合理的,因为会导致页面加载时间变长。毕竟第一次载入还是有些耗时的。所以只有做成服务再加上懒加载技术,才是解决这样的问题最好的路径。
设计
基于这一诉求。我设计了如下服务流程:

用户访问页面时,使用wordpress
的hook
功能,拦截获取头像的方法,然后注入我们的头像服务地址。接着我们的头像服务会根据请求的查询字符串,生成一串唯一ID,并以这个ID做为文件名称,缓存至指定的路径。这就是缓存的步骤。如果该唯一ID的文件存在,则直接返回该文件即可。
所以头像的访问由原来的
https://secure.gravatar.com/avatar/8d94cb8be36f163c7a6148d4dc9a53d9?s=96&d=mm&r=g
调至
https://taliove.com/avatar/8d94cb8be36f163c7a6148d4dc9a53d9?s=96&d=mm&r=g
这两个链接只有域名不同,其它的均相同,返回的结果也一致。只不过从头像缓存主服务器同步缓存至本机而已。
动工
Let’s do it!
头像服务
- 在网站根目录创建一个目录
wp-avatar
- 在该目录创建文件
avatar_service.php
,并拷贝以下代码至该文件 - 给该目录网站的权限
sudo chown -R nginx:nginx ./wp-avatar
- 我的php-fpm使用的是nginx用户名。不同的服务会不同。所以我这里使用的是nginx
- 修改
nginx
配置vim /etc/nginx/conf.d/www.taliove.com.conf
,新增如下配置:
1 2 3 |
location /avatar { try_files $uri $uri/wp-avatar/ /wp-avatar/avatar_service.php?$args; } |
修改完nginx
后,重启服务systemctl restart nginx
。
头像缓存服务代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
<?php $config = array( "avatar_dir" => "/usr/local/www/avatar/", //头像目录 // "avatar_dir" => "./avatar/", //头像目录 "default_avatar" => "default.jpg"//默认的头像名称 ); function start() { global $config; $request_uri = $_SERVER['REQUEST_URI']; $file_path = $config['default_avatar']; if ($request_uri) { $file_path = cache_file($request_uri); } if (!file_exists($file_path)) { $file_path = $config['avatar_dir'] . $config['default_avatar']; } if (file_exists($file_path)) { $fp = fopen($file_path, "rb"); $image_info = getimagesize($file_path); switch ($image_info[2]) { case IMAGETYPE_JPEG: header("Content-Type: image/jpeg"); break; case IMAGETYPE_GIF: header("Content-Type: image/gif"); break; case IMAGETYPE_PNG: header("Content-Type: image/png"); break; default: header($_SERVER["SERVER_PROTOCOL"] . " 404 Not Found"); break; } header("Content-Length: " . filesize($file_path)); fpassthru($fp); exit; } else { header($_SERVER["SERVER_PROTOCOL"] . " 404 Not Found"); } } /** * 缓存或获取缓存的文件 * @param string $request_uri * @return string 缓存的文件名称 */ function cache_file($request_uri = '') { global $config; $file_name = "avatar_" . md5($request_uri); // 缓存有效时间 $expire_time = 2592000; if (!is_readable($config['avatar_dir'])) { mkdir($config['avatar_dir'], 0777); } $file_path = $config['avatar_dir'] . $file_name . ".png"; if (!is_file($file_path)) { $file_path = $config['avatar_dir'] . $file_name . ".jpg"; } if (!is_file($file_path)) { $file_path = $config['avatar_dir'] . $file_name . ".jpeg"; } if (!is_file($file_path) || (time() - filemtime($file_path)) > $expire_time) { //文件不存在,或者超过有效期时,重新获取 $uri = "https://secure.gravatar.com/{$request_uri}"; $headers = @get_headers($uri); $header = implode(",", $headers); if (!preg_match("/200/", $header)) { // 没有头像,则新建一个空白文件作为标记 $handle = fopen($file_path, 'w'); fclose($handle); } else { preg_match("/Content\-Type\: image\/(\w+)/i", $header, $matches); if ($matches && count($matches) >= 2) { $file_name .= ".{$matches[1]}"; $result = getImage($uri, $config['avatar_dir'], $file_name); $file_path = $result['save_path']; } } } return $file_path; } function getImage($url, $save_dir = '', $filename = '', $type = 0) { if (trim($url) == '') { return array('file_name' => '', 'save_path' => '', 'error' => 1); } if (trim($save_dir) == '') { $save_dir = './'; } if (trim($filename) == '') {//保存文件名 $ext = strrchr($url, '.'); if ($ext != '.gif' && $ext != '.jpg') { return array('file_name' => '', 'save_path' => '', 'error' => 3); } $filename = time() . $ext; } if (0 !== strrpos($save_dir, '/')) { $save_dir .= '/'; } //创建保存目录 if (!file_exists($save_dir) && !mkdir($save_dir, 0777, true)) { return array('file_name' => '', 'save_path' => '', 'error' => 5); } //获取远程文件所采用的方法 if ($type) { $ch = curl_init(); $timeout = 300; curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); $img = curl_exec($ch); curl_close($ch); } else { ob_start(); readfile($url); $img = ob_get_contents(); ob_end_clean(); } //$size=strlen($img); //文件大小 $fp2 = @fopen($save_dir . $filename, 'a'); fwrite($fp2, $img); fclose($fp2); unset($img, $url); return array('file_name' => $filename, 'save_path' => $save_dir . $filename, 'error' => 0); } start(); |
自定义插件
定义好服务之后,我们需要将所有的头像访问地址,都指向我们新的服务地址。使用自定义的插件,可以进行灵活的配置。
在网站根目录wp-content/plugins
创建文件夹taliove
,在其下创建文件functions.php
,添加如下代码:
自定义插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<?php /** * Plugin Name: taliove插件 * Plugin URI: https://taliove.com * Description: 添雨自定义函数插件 * Author: taliove * Author URI: https://taliove.com * Version: 0.0.1 */ if (!function_exists('cache_avatar')) { function tf_log($str = '', $tag = '') { $split = ($tag == '') ? '' : ":\t"; file_put_contents(WP_CONTENT_DIR . '/taliove.debug.log', $tag . $split . $str . "\n", FILE_APPEND); } /** * 缓存头像 * @param $avatar * @param $id_or_email * @param $size * @param $default * @param $alt * @return mixed */ function cache_avatar($avatar, $id_or_email, $size, $default, $alt) { $site = site_url(); return preg_replace("/https?\:\/\/.+?avatar\//","{$site}/avatar/", $avatar); } add_filter('get_avatar', 'cache_avatar', 1, 5); } |
然后在wordpress
管理后台启用刚刚创建好的插件。
结尾
头像服务,是网站的个性化功能之一。有的人喜欢,有的人不喜欢。我是属于前者。基于以上操作后,头像服务不依赖于第三方。且访问速度快很多。当然这些缓存的头像也可以走CDN
,但是https
的CDN
是要收费的。所以暂时先从本地访问。
最终访问效果是,首次访问时,耗时会较长。第二次访问速度将会有质的飞跃。对于这种情况,我们需要安装一下懒加载插件即可。我使用的是a3-lazy-load
插件,效果还是挺OK的。
最后博主采用了自建伪CDN,将这个缓存服务放在了CDN域名下。所有的静态资源能走CDN走CDN。毕竟第三方厂商的https CDN服务是要收费的。
Enjoy it!