自建头像缓存服务
起因
wordpress
的头像加载一般是最头疼的地方。原因有三,其一是头像缓存的服务器在国外。所以国内使用的话,网络慢得可怜;其二是可以使用国内的头像缓存服务,但是指不定哪天就给关闭了;其三,我们可以直接禁用该服务,目前如果我们需要此服务的话,就只能使用默认头像。所以我想到,是不是可以自己写一个服务去缓存这些头像。就像CDN
一样。
Gravatar是一图像跟随著您到访过的网站,当您在博客中留言或发表文章,它将会出现在您的名称旁。头像协助识别您在博客和论坛发表的文章,何乐而不为呢?
当然,我们也可以直接百度一下,查找本地缓存的例子。博主也查了一下。大部分的代码都年久失修,直接拿来用是不可能的。而他们的一些例子基本上都是写在插件里面,或者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
,新增如下配置:
location /avatar {
try_files $uri $uri/wp-avatar/ /wp-avatar/avatar_service.php?$args;
}
修改完nginx
后,重启服务systemctl restart nginx
。
头像缓存服务代码
<?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
,添加如下代码:
自定义插件
<?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!