我有一个纸牌游戏(下面的截图),其中我显示了玩家头像。
对于头像,我写了一个简短的proxy.php脚本,它会将传递给它的图像网址作为?img = 参数,下载并保存在 / var /下我的CentOS 5机器上有www / cached_avatars / md5_of_that_url 。下次使用相同的URL调用脚本时,它将在目录中找到该图像并直接将其提供给STDOUT。
这种方法效果很好,但是对于一些头像来说,初始下载失败了(我想它会超时)并且你没有看到播放器图片的下半部分:
我想检测此图像下载失败并删除缓存的部分文件,以便在下一次proxy.php调用时重新下载。
我已尝试在回调中检测到STREAM_NOTIFY_FAILURE或STREAM_NOTIFY_COMPLETED事件,但不会触发它们。我看到的唯一事件是:STREAM_NOTIFY_CONNECT,STREAM_NOTIFY_MIME_TYPE_IS,STREAM_NOTIFY_FILE_SIZE_IS,STREAM_NOTIFY_REDIRECTED,STREAM_NOTIFY_PROGRESS:
Nov 3 18:48:27 httpd: 2 0
Nov 3 18:48:27 httpd: 4 image/jpeg 0
Nov 3 18:48:27 httpd: 5 Content-Length: 45842 0
Nov 3 18:48:27 httpd: 7 0
Nov 3 18:48:27 last message repeated 16 times
Nov 3 18:48:39 httpd: 2 0
Nov 3 18:48:40 httpd: 4 image/jpeg 0
Nov 3 18:48:40 httpd: 5 Content-Length: 124537 0
Nov 3 18:48:40 httpd: 7 0
我更大的问题是,我无法将$ img或$ cached等变量传递给回调,或者我无法在STREAM_NOTIFY_FILE_SIZE_IS事件的回调中设置$ length变量,然后将其与filesize进行比较(主脚本中的$ cached)(我可以检测到不匹配并删除文件):
Nov 3 18:50:17 httpd: PHP Notice: Undefined variable: length in /var/www/html/proxy.php on line 58
Nov 3 18:50:17 httpd: length=
有没有人能解决我的问题?
我看过PHP curl库,但是看不出它对我有什么帮助。
下面是我的脚本,为简洁起见,我省略了URL健全性检查:
<?php
define('MAX_SIZE', 1024 * 1024);
define('CACHE_DIR', '/var/www/cached_avatars/');
$img = urldecode($_GET['img']);
$opts = array(
'http' => array(
'method' => 'GET'
)
);
$cached = CACHE_DIR . md5($img);
$finfo = finfo_open(FILEINFO_MIME);
$readfh = @fopen($cached, 'rb');
if ($readfh) {
header('Content-Type: ' . finfo_file($finfo, $cached));
header('Content-Length: ' . filesize($cached));
while (!feof($readfh)) {
$buf = fread($readfh, 8192);
echo $buf;
}
fclose($readfh);
finfo_close($finfo);
exit();
}
$ctx = stream_context_create($opts);
stream_context_set_params($ctx, array('notification' => 'callback'));
$writefh = fopen($cached, 'xb');
$webfh = fopen($img, 'r', FALSE, $ctx);
if ($webfh) {
$completed = TRUE;
while (!feof($webfh)) {
$buf = fread($webfh, 8192);
echo $buf;
if ($writefh)
fwrite($writefh, $buf);
}
fclose($webfh);
if ($writefh)
fclose($writefh);
# XXX can't access $length in callback
error_log('length=' . $length);
# XXX can't access $completed in callback
if (!$completed)
unlink($cached);
}
function callback($code, $severity, $message, $message_code, $bytes_transferred, $bytes_total) {
error_log(join(' ', array($code, $message, $message_code)));
if ($code == STREAM_NOTIFY_PROGRESS && $bytes_transferred > MAX_SIZE) {
exit('File is too big: ' . $bytes_transferred);
} else if ($code == STREAM_NOTIFY_FILE_SIZE_IS) {
if ($bytes_total > MAX_SIZE)
exit('File is too big: ' . $bytes_total);
else {
header('Content-Length: ' . $bytes_total);
# XXX can't pass to main script
$length = $bytes_total;
}
} else if ($code == STREAM_NOTIFY_MIME_TYPE_IS) {
if (stripos($message, 'image/gif') !== FALSE ||
stripos($message, 'image/png') !== FALSE ||
stripos($message, 'image/jpg') !== FALSE ||
stripos($message, 'image/jpeg') !== FALSE) {
header('Content-Type: ' . $message);
} else {
exit('File is not image: ' . $mime);
}
} else if ($code == STREAM_NOTIFY_FAILURE) {
$completed = FALSE;
}
}
?>
我的脚本中没有使用任何文件锁定:从缓存中读取一段时间后返回一个不完整的文件(因为它仍在下载中)是可以的。但是我希望我的缓存不受任何部分下载的图像的影响。另外,如果你查看我的脚本,我会使用“xb”,它可以防止多个脚本写入1个文件,所以这里同时写入不是问题。
答案 0 :(得分:1)
您希望我们下载图像的卷曲库。它处理超时,重定向和错误检查。例如,您可以检查要连接的服务器的404(丢失文件)响应。如果一切正常,那么使用fopen将内容写入缓存文件。
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_URL, $img_url);
$content = curl_exec($ch);
$info = curl_getinfo($ch);
$errorCode = curl_errno($ch);
$errorMsg = curl_error($ch);
curl_close($ch);
// Check for errors
if ( $errorCode==0 ) {
// No connection errors, just for response type
if ($info['http_code'] != 200) {
// Something happened on the other side
...
} else {
// Image is in $content variable, save to cache file
...
}
}