PHP: обнаружение сбоев fopen () при загрузке изображений - PullRequest
0 голосов
/ 03 ноября 2010

У меня есть карточная игра (скриншот ниже), в которой я отображаю аватары игроков.

Для аватаров я написал короткий скрипт proxy.php, который будет принимать URL-адрес изображения, переданный ему в качестве параметра ? 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 или $ cached в обратный вызов или не могу установить переменную $ length в обратном вызове для события STREAM_NOTIFY_FILE_SIZE_IS, а затем сравнить его с размером файла ( $ 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 файл, поэтому это одновременное написание здесь не проблема.

1 Ответ

1 голос
/ 03 ноября 2010

Библиотека curl - это то, что вы хотите, чтобы мы загрузили изображение.Он обрабатывает таймауты, перенаправления и проверки ошибок.Например, вы можете проверить ответ 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
        ...
    }
}
...