轶哥

妄图改变世界的全栈程序员。

PHP实现Github头像缓存

在开发博客Github登录功能,缓存Github头像的时候,发现下载头像是个很费劲的事情。

利用位于HK的PHP虚拟主机,可以轻松解决这个问题。

伪静态依赖Apache的Rewrite模块。

PHP代码

// 屏蔽所有报错,避免图片加载失败
error_reporting(0);

// 设置需要返回的头名称
$proxied_headers = array('Set-Cookie', 'Content-Type', 'Cookie', 'Location');

// 获取URL路径
$proxy_request_url = $_SERVER['REQUEST_URI'];

// 设置请求地址HOST
$github_res_host = 'githubusercontent.com';

// 设置当前PHP文件所在相对路径,根目录设置为"/"
$proxy_base_url = '/avatar';

// 如果包含 /index.php 路径
if (strpos($proxy_request_url, $proxy_base_url . '/index.php') === 0) {
    // 兼容非伪静态模式
    $proxy_request_url = ltrim(substr($proxy_request_url, strlen($proxy_base_url . '/index.php')), '/');
} else {
    // 依赖Apache的Rewrite模块
    $proxy_request_url = ltrim(substr($proxy_request_url, strlen($proxy_base_url)), '/');
}

// 判断请求地址是否符合$github_res_host规定,防止滥用
if (strpos(substr($proxy_request_url, 0, strpos($proxy_request_url, '/')), $github_res_host) === false) {
    die("403");
}

// 最终请求的远程地址
$proxy_request_url = "https://" . $proxy_request_url;

// 初始化 CURL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $proxy_request_url);
curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);

// 设置请求的 Cookie,此处用不到
// if (isset($_SERVER['HTTP_COOKIE'])) {
//   $client_headers[] = "Cookie: " . $_SERVER['HTTP_COOKIE'];
// }

// 设置请求的 User-Agent
if (isset($_SERVER['HTTP_USER_AGENT'])) {
    $client_headers[] = "User-Agent: " . $_SERVER['HTTP_USER_AGENT'];
}

curl_setopt($ch, CURLOPT_HTTPHEADER, $client_headers);

$res = curl_exec($ch);
curl_close($ch);

// 解析请求到的结果
list($headers, $body) = explode("\r\n\r\n", $res, 2);

$headers = explode("\r\n", $headers);
$hs = array();

foreach ($headers as $header) {
    if (false !== strpos($header, ':')) {
        list($h, $v) = explode(':', $header);
        $hs[$h][] = $v;
    } else {
        $header1 = $header;
    }
}

// 设置返回头
list($proto, $code, $text) = explode(' ', $header1);
header($_SERVER['SERVER_PROTOCOL'] . ' ' . $code . ' ' . $text);

foreach ($proxied_headers as $header_name) {
    if (isset($hs[$header_name])) {
        foreach ($hs[$header_name] as $v) {
            if ($header_name !== 'Set-Cookie') {
                header($header_name . ": " . $v, false);
            } else {
                header($header_name . ": " . $v);
            }
        }
    }
}

die($body);

伪静态配置

Apache伪静态配置文件.htaccess

RewriteEngine on
RewriteBase /avatar
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond $1 !^(index\.php)
RewriteRule ^(.*)$ index.php?/$1 [L]

开启伪静态前:https://域名/avatar/index.php/avatars2.githubusercontent.com/u/6657330?v=4

开启伪静态后:https://域名/avatar/avatars2.githubusercontent.com/u/6657330?v=4

测试地址:https://apio.xyz/avatar/avatars2.githubusercontent.com/u/6657330?v=4

JavaScript使用示例

const avatorDownloadURL = 'https://apio.xyz/avatar/' + avatarURL.replace('https://', '')

如此,GitHub OAuth 第三方登录缓存头像时便可快速下载。

为什么不直接使用URL参数?

通过拼接路径并直接返回图片Header头(Content-Type是对应图片的格式)的方式,方便直接在前端<img>标签中引用图片,浏览器兼容性更好。实际使用时建议同步下载图片后上传到对象存储。

打赏
交流区(6)
凉拌萝卜丝不好吃吗

666

2020年7月23日 14:50回复
轶哥

😄

2020年7月23日 14:51回复
小苦瓜一点也不苦

有意思

2020年7月23日 17:45回复
小苦瓜一点也不苦

可以🉑

2020年7月23日 17:49回复
遇见卡瓦博格

666

2020年7月23日 17:53回复
轶哥

赶紧给你博客也加上登录!!😂

2020年7月23日 17:54回复
尚未登陆
发布
  上一篇 (将新网址推送到百度、谷歌、Bing搜索引擎)
下一篇 (解决XML报错:Input is not proper UTF-8, indicate encoding)  

评论回复提醒