2010-11-03 66 views
0

我有一個紙牌遊戲(屏幕截圖如下),其中顯示玩家頭像。PHP:下載圖像時檢測fopen()失敗

對於頭像我寫了一個短proxy.php腳本,這將需要傳遞給它圖片的網址?IMG =參數,下載它,並在在/ var/WWW/cached_avatars/md5_of_that_url保存在我的CentOS 5機器上。下次使用相同的URL調用腳本時,它會在目錄中找到該圖像並直接將其提供給STDOUT。

這個作品大都不錯,但對於一些化身的初始下載失敗(我想它超時),你看不到玩家的畫面下部:

alt text

我會喜歡檢測此圖像下載失敗並刪除緩存的部分文件,以便在下次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

捲曲庫是你想要我們下載圖像。它處理超時,重定向和錯誤檢查。例如,您可以檢查連接到的服務器的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 
     ... 
    } 
} 
+0

謝謝,但:1)如果我使用curl將圖像讀入緩衝區,如何保持內存使用率低?在我原來的腳本中,我正在閱讀8192塊。 2)如果我使用curl將圖像直接讀入文件,我如何確保文件不會被另一個腳本實例損壞?在我的原始文件中,我使用fopen(...,「xb」)。 3)最重要的是:我怎樣才能確保curl不會下載太大的文件,DOS會攻擊我的服務器?在使用curl時,我沒有看到設置下載文件限制的方法(CURLOPT_RANGE只是對服務器的「推薦」)。 – 2010-11-03 19:29:25

+0

您將CURLOPT_RETURNTRANSFER設置爲false,則不傳輸數據,只傳輸標題。然後你可以檢查標題的大小。但無論如何,PHP只會使用配置中設置的內存限制(典型值爲8MB),所以您實際上無法DOS攻擊您的服務器。 – 2010-11-03 20:54:55

+0

謝謝,我會嘗試使用CURL。儘管我遇到了cookie問題:http://stackoverflow.com/questions/4096470/php-curl-curl-setopt-fopencookie-failed – 2010-11-04 12:09:39