我有一個紙牌遊戲(屏幕截圖如下),其中顯示玩家頭像。PHP:下載圖像時檢測fopen()失敗
對於頭像我寫了一個短proxy.php腳本,這將需要傳遞給它圖片的網址?IMG =參數,下載它,並在在/ var/WWW/cached_avatars/md5_of_that_url保存在我的CentOS 5機器上。下次使用相同的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或$緩存步入回調或者我可以「T設定在STREAM_NOTIFY_FILE_SIZE_IS事件的回調$長度是可變的,然後將其與主腳本文件大小($緩存)比較(我能檢測到失配和刪除文件):
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捲曲庫,但是看不到它怎麼能幫到我。
下面是我的劇本,我省略了網址合理性檢查爲簡潔:
<?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個文件,所以這個同時寫入在這裏不是問題。
謝謝,但:1)如果我使用curl將圖像讀入緩衝區,如何保持內存使用率低?在我原來的腳本中,我正在閱讀8192塊。 2)如果我使用curl將圖像直接讀入文件,我如何確保文件不會被另一個腳本實例損壞?在我的原始文件中,我使用fopen(...,「xb」)。 3)最重要的是:我怎樣才能確保curl不會下載太大的文件,DOS會攻擊我的服務器?在使用curl時,我沒有看到設置下載文件限制的方法(CURLOPT_RANGE只是對服務器的「推薦」)。 – 2010-11-03 19:29:25
您將CURLOPT_RETURNTRANSFER設置爲false,則不傳輸數據,只傳輸標題。然後你可以檢查標題的大小。但無論如何,PHP只會使用配置中設置的內存限制(典型值爲8MB),所以您實際上無法DOS攻擊您的服務器。 – 2010-11-03 20:54:55
謝謝,我會嘗試使用CURL。儘管我遇到了cookie問題:http://stackoverflow.com/questions/4096470/php-curl-curl-setopt-fopencookie-failed – 2010-11-04 12:09:39