Как с помощью PHP проверить, существует ли удаленный файл?

86

Лучшее, что я смог найти, if fclose fopenшрифт, заставляет страницу загружаться очень медленно.

В основном я пытаюсь сделать следующее: у меня есть список веб-сайтов, и я хочу отображать их значки рядом с ними. Однако, если на сайте его нет, я бы хотел заменить его другим изображением, а не отображать неработающее изображение.


источник
Я думаю, вы можете использовать CURL и проверить его коды возврата. Но если проблема в скорости, просто сделайте это в автономном режиме и кешируйте.
Michał Tatarynowicz
Да, но я все же рекомендую использовать автономный скрипт (запускаемый из cron), который анализирует список веб-сайтов, проверяет, есть ли у них значки, и кеширует эти данные для внешнего интерфейса. Если вы не используете / не можете использовать cron, по крайней мере кешируйте результаты для каждого нового проверяемого URL.
Michał Tatarynowicz
3
Для замены неработающего изображения изображением-заполнителем в браузере, пожалуйста, рассмотрите возможность решения на стороне клиента, использующего onerrorизображение, например, решение с использованием jQuery

Ответы:

135

Вы можете указать curl использовать метод HTTP HEAD через CURLOPT_NOBODY.

Более менее

$ch = curl_init("http://www.example.com/favicon.ico");

curl_setopt($ch, CURLOPT_NOBODY, true);
curl_exec($ch);
$retcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
// $retcode >= 400 -> not found, $retcode = 200, found.
curl_close($ch);

В любом случае, вы экономите только на HTTP-передаче, но не на установлении и закрытии TCP-соединения. А поскольку фавиконы маленькие, особых улучшений вы не увидите.

Кэширование результата локально кажется хорошей идеей, если он оказывается слишком медленным. HEAD проверяет время файла и возвращает его в заголовках. Вы можете делать как браузеры и получать CURLINFO_FILETIME значка. В вашем кеше вы можете сохранить URL => [значок, временная метка]. Затем вы можете сравнить отметку времени и перезагрузить значок.

Рамон Пока
источник
6
просто примечание: retcodeошибки во всех 400 кодах, поэтому проверка будет >=не простой>
Джастин Булл
4
Некоторые сайты блокируют доступ, если вы не предоставляете строку пользовательского агента, поэтому я предлагаю следовать этому руководству, чтобы добавить CURLOPT_USERAGENT в дополнение к CURLOPT_NOBODY: davidwalsh.name/set-user-agent-php-curl-spoof
rlorenzo
6
Реткоды @Lyth 3XX - это не ошибка, а перенаправление. Их следует обрабатывать вручную или с помощью CURLOPT_FOLLOWLOCATION.
Рамон Пока,
6
Используйте curl_setopt ($ ch, CURLOPT_SSL_VERIFYPEER, false); а также чтобы убедиться, что тот же код работает для URL, начинающихся с HTTPS!
Кришан Гопал
61

Как говорят пироги, вы можете использовать cURL. Вы можете заставить cURL выдавать только заголовки, но не тело, что может ускорить работу. Плохой домен всегда может занять некоторое время, потому что вы будете ждать истечения времени ожидания запроса; вы, вероятно, можете изменить длину тайм-аута с помощью cURL.

Вот пример:

function remoteFileExists($url) {
    $curl = curl_init($url);

    //don't fetch the actual page, you only want to check the connection is ok
    curl_setopt($curl, CURLOPT_NOBODY, true);

    //do request
    $result = curl_exec($curl);

    $ret = false;

    //if request did not fail
    if ($result !== false) {
        //if request was ok, check response code
        $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);  

        if ($statusCode == 200) {
            $ret = true;   
        }
    }

    curl_close($curl);

    return $ret;
}

$exists = remoteFileExists('http://stackoverflow.com/favicon.ico');
if ($exists) {
    echo 'file exists';
} else {
    echo 'file does not exist';   
}
Том Хей
источник
3
remoteFileExists (' stackoverflow.com/' ) также вернет true, но это всего лишь ссылка. Эта функция не проверяет тип содержимого ссылки - файл.
Донатас Навидонскис
36

Решение CoolGoose хорошее, но это быстрее для больших файлов (поскольку оно пытается прочитать только 1 байт):

if (false === file_get_contents("http://example.com/path/to/image",0,null,0,1)) {
    $image = $default_image;
}
луБар
источник
+1. Есть ли у этого решения недостатки по сравнению с CURL?
Adriano Varoli Piazza
1
вы можете просто использовать fopen- если код возврата запроса 404, fopen вернет false.
s3v3n 02
это очень медленно и не сработало для меня (то есть, если путь к файлу был неправильным, изображение все равно отображалось некорректно)
Гельмут
Этот подход не работает, если сервер выполняет перенаправление всякий раз, когда изображение или файл не существует. Это происходит, когда сайт использует mod_rewrite или какие-то другие «правила» обработки запросов.
Эрик Черпняк,
28

Это не ответ на ваш исходный вопрос, а лучший способ сделать то, что вы пытаетесь сделать:

Вместо того, чтобы пытаться напрямую получить значок сайта (что очень неудобно, учитывая, что это может быть /favicon.png, /favicon.ico, /favicon.gif или даже /path/to/favicon.png), используйте google:

<img src="http://www.google.com/s2/favicons?domain=[domain]">

Выполнено.

Мала
источник
4
Синтаксис немного запутал. Итак, вот один пример: <img src = " google.com/s2/favicons?domain=stackoverflow.com ">
Habeeb Perwad
19

Полная функция ответа, получившего наибольшее количество голосов:

function remote_file_exists($url)
{
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_NOBODY, 1);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); # handles 301/2 redirects
    curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    if( $httpCode == 200 ){return true;}
}

Вы можете использовать это так:

if(remote_file_exists($url))
{
    //file exists, do something
}
Педро Лобито
источник
Ой! Я отсутствовал последние пару дней, но начало месяца было почти круглосуточным. Спасибо, что дали мне знать!
Педро Лобито
Это не работает, если сервер не отвечает ни на один HTTP-код (или cUrl его не улавливает). Что со мной случается довольно часто. Например. в случае изображений.
Vaci
что, если URL-адрес перенаправлен на другой URL-адрес или версию https? В этом случае код curl не сможет выполнить эту работу. лучший способ - получить информацию из заголовка и найти строку «200 ok» без учета регистра.
Infoconic
@Infoconic Можно добавить curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);. Я обновил ответ, чтобы обрабатывать 302перенаправления.
Педро Лобито,
18

Если вы имеете дело с изображениями, используйте getimagesize. В отличие от file_exists, эта встроенная функция поддерживает удаленные файлы. Он вернет массив, содержащий информацию об изображении (ширина, высота, тип и т. Д.). Все, что вам нужно сделать, это проверить первый элемент в массиве (ширину). используйте print_r для вывода содержимого массива

$imageArray = getimagesize("http://www.example.com/image.jpg");
if($imageArray[0])
{
    echo "it's an image and here is the image's info<br>";
    print_r($imageArray);
}
else
{
    echo "invalid image";
}
Эйяд Фаллатах
источник
Выдает предупреждение 404, когда удаленный ресурс недоступен. На данный момент я справился с этим, подавив ошибку using @перед getimagesize, но чувствуя себя виноватым за этот взлом.
В моем случае это был лучший подход, потому что меня перенаправляют всякий раз, когда изображение / файл не существует. Во-вторых, подавление ошибок с помощью @ недопустимо, но в данном случае это было необходимо.
Эрик Черпняк
Я понял, что мы также можем использовать exif_imagetype, и это намного быстрее stackoverflow.com/a/38295345/1250044
yckart
7

Это можно сделать, получив код состояния HTTP (404 = не найден), что возможно с помощью file_get_contentsДокументов, использующих параметры контекста. Следующий код учитывает перенаправления и возвращает код состояния конечного пункта назначения ( Демо ):

$url = 'http://example.com/';
$code = FALSE;

$options['http'] = array(
    'method' => "HEAD",
    'ignore_errors' => 1
);

$body = file_get_contents($url, NULL, stream_context_create($options));

foreach($http_response_header as $header)
    sscanf($header, 'HTTP/%*d.%*d %d', $code);

echo "Status code: $code";

Если вы не хотите следовать перенаправлениям, вы можете сделать это аналогично ( Демо ):

$url = 'http://example.com/';
$code = FALSE;

$options['http'] = array(
    'method' => "HEAD",
    'ignore_errors' => 1,
    'max_redirects' => 0
);

$body = file_get_contents($url, NULL, stream_context_create($options));

sscanf($http_response_header[0], 'HTTP/%*d.%*d %d', $code);

echo "Status code: $code";

Некоторые из используемых функций, параметров и переменных более подробно описаны в моем блоге: HEAD first with PHP Streams .

hakre
источник
Для получения дополнительной информации о PHP $http_response_headerсм. Php.net/manual/en/reserved.variables.httpresponseheader.php .
Big McLargeHuge
1
Второй вариант сработал для меня, и по сравнению с вызовом file_get_contents по умолчанию (без настраиваемого stream_context) он был на 50% быстрее, то есть с 3,4 до 1,7 секунды для запроса.
Эрик Черпняк
@ ErikČerpnjak: Если нет "пользовательского" stream_context, он используется по умолчанию. Вы можете получить параметры из контекста по умолчанию и посмотреть, как они отличаются от вашего пользовательского контекста. Это должно дать вам некоторое представление о том, почему различаются тайминги. - php.net/stream-context-get-default и php.net/stream-context-get-options
hakre
6
if (false === file_get_contents("http://example.com/path/to/image")) {
    $image = $default_image;
}

Должно сработать ;)

CoolGoose
источник
add @ before function
Тебе
6

Встроенные функции PHP могут не работать для проверки URL, если параметр allow_url_fopen отключен по соображениям безопасности. Curl - лучший вариант, поскольку нам не нужно будет изменять наш код на более позднем этапе. Ниже приведен код, который я использовал для проверки действительного URL:

$url = str_replace(' ', '%20', $url);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_exec($ch);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);  
curl_close($ch);
if($httpcode>=200 && $httpcode<300){  return true; } else { return false; } 

Пожалуйста, обратите внимание на параметр CURLOPT_SSL_VERIFYPEER, который также проверяет URL-адрес, начинающийся с HTTPS.

Кришан Гопал
источник
6

exif_imagetypeСледует предпочесть проверку наличия изображений getimagesize, так как это намного быстрее.

Чтобы подавить E_NOTICE, просто добавьте оператор контроля ошибок ( @).

if (@exif_imagetype($filename)) {
  // Image exist
}

В качестве бонуса с возвращаемым значением ( IMAGETYPE_XXX) exif_imagetypeмы также можем получить тип mime или расширение файла с image_type_to_mime_type/ image_type_to_extension.

yckart
источник
4

Радикальным решением было бы отображать значки как фоновые изображения в блоке div над значком по умолчанию. Таким образом, все накладные расходы будут возложены на клиента, но при этом не будут отображаться битые изображения (отсутствующие фоновые изображения игнорируются во всех браузерах AFAIK).

труппо
источник
1
+1, если вы не проверяете несколько мест для их значка (favicon.ico, favicon.gif, favicon.png), это, кажется, лучшее решение
Гален
3
function remote_file_exists($url){
   return(bool)preg_match('~HTTP/1\.\d\s+200\s+OK~', @current(get_headers($url)));
}  
$ff = "http://www.emeditor.com/pub/emed32_11.0.5.exe";
    if(remote_file_exists($ff)){
        echo "file exist!";
    }
    else{
        echo "file not exist!!!";
    }
доктор linux
источник
3

Вы можете использовать следующее:

$file = 'http://mysite.co.za/images/favicon.ico';
$file_exists = (@fopen($file, "r")) ? true : false;

Работал для меня при попытке проверить, существует ли изображение по URL-адресу

Рикус Хармс
источник
2

Ты можешь использовать :

$url=getimagesize(“http://www.flickr.com/photos/27505599@N07/2564389539/”);

if(!is_array($url))
{
   $default_image =”…/directoryFolder/junal.jpg”;
}
CP Soni
источник
2

Это работает для меня, чтобы проверить, существует ли удаленный файл в PHP:

$url = 'https://cdn.sstatic.net/Sites/stackoverflow/img/favicon.ico';
    $header_response = get_headers($url, 1);

    if ( strpos( $header_response[0], "404" ) !== false ) {
        echo 'File does NOT exist';
        } else {
        echo 'File exists';
        }
user7018984
источник
1

Вы должны отправлять запросы HEAD, а не GET, потому что вам вообще не нужно содержимое URI. Как сказано выше, вы должны проверить код состояния (в диапазонах 200–299, при желании вы можете следовать перенаправлениям 3xx).

Вопрос с ответами содержит много примеров кода, которые могут быть полезны: PHP / Curl: HEAD Request занимает много времени на некоторых сайтах

Drdaeman
источник
1

Есть еще более изощренная альтернатива. Вы можете проверить все на стороне клиента, используя трюк JQuery.

$('a[href^="http://"]').filter(function(){
     return this.hostname && this.hostname !== location.hostname;
}).each(function() {
    var link = jQuery(this);
    var faviconURL =
      link.attr('href').replace(/^(http:\/\/[^\/]+).*$/, '$1')+'/favicon.ico';
    var faviconIMG = jQuery('<img src="favicon.png" alt="" />')['appendTo'](link);
    var extImg = new Image();
    extImg.src = faviconURL;
    if (extImg.complete)
      faviconIMG.attr('src', faviconURL);
    else
      extImg.onload = function() { faviconIMG.attr('src', faviconURL); };
});

Из http://snipplr.com/view/18782/add-a-favicon-near-external-links-with-jquery/ (исходный блог в настоящее время недоступен)

С. Пэнгборн
источник
1

все ответы здесь, которые используют get_headers (), выполняют запрос GET. Гораздо быстрее / дешевле просто выполнить запрос HEAD.

Чтобы убедиться, что get_headers () выполняет запрос HEAD вместо GET, вы должны добавить это:

stream_context_set_default(
    array(
        'http' => array(
            'method' => 'HEAD'
        )
    )
);

поэтому, чтобы проверить, существует ли файл, ваш код будет выглядеть примерно так:

stream_context_set_default(
    array(
        'http' => array(
            'method' => 'HEAD'
        )
    )
);
$headers = get_headers('http://website.com/dir/file.jpg', 1);
$file_found = stristr($headers[0], '200');

Очевидно, $ file_found вернет либо false, либо true.

Людо - Не для записи
источник
0

Не знаю, работает ли это быстрее, когда файл не существует удаленно, is_file () , но вы можете попробовать .

$favIcon = 'default FavIcon';
if(is_file($remotePath)) {
   $favIcon = file_get_contents($remotePath);
}
ПатрикАкерстранд
источник
Из документации: «Начиная с PHP 5.0.0, эту функцию также можно использовать с некоторыми оболочками URL. См. Поддерживаемые протоколы и оболочки, чтобы определить, какие оболочки поддерживают семейство функций stat ()».
PatrikAkerstrand
Вы имеете в виду, что это может сработать, если вы зарегистрируете оболочку потока? Отредактируйте свой вопрос, чтобы показать рабочий пример, и я удалю свой голос против (и проголосую за вас, если смогу). Но на данный момент я протестировал is_file из php cli с удаленным файлом и получил false.
greg0ire
нет рабочего примера:var_dump(is_file('http://cdn.sstatic.net/stackoverflow/img/sprites.png')); bool(false)
greg0ire
0

Если файл не размещен на внешнем сервере, вы можете преобразовать удаленный URL-адрес в абсолютный путь на своем веб-сервере. Таким образом, вам не нужно вызывать CURL или file_get_contents и т. Д.

function remoteFileExists($url) {

    $root = realpath($_SERVER["DOCUMENT_ROOT"]);
    $urlParts = parse_url( $url );

    if ( !isset( $urlParts['path'] ) )
        return false;

    if ( is_file( $root . $urlParts['path'] ) )
        return true;
    else
        return false;

}

remoteFileExists( 'https://www.yourdomain.com/path/to/remote/image.png' );

Примечание: ваш веб-сервер должен заполнить DOCUMENT_ROOT, чтобы использовать эту функцию

Бастиан Фиссингер
источник
0

Если вы используете фреймворк Symfony, есть также более простой способ HttpClientInterface:

private function remoteFileExists(string $url, HttpClientInterface $client): bool {
    $response = $client->request(
        'GET',
        $url //e.g. http://example.com/file.txt
    );

    return $response->getStatusCode() == 200;
}

Документация для HttpClient также очень хороша и, возможно, стоит изучить, если вам нужен более конкретный подход: https://symfony.com/doc/current/http_client.html

Филнор
источник
-1

Вы можете использовать файловую систему: используйте Symfony \ Component \ Filesystem \ Filesystem; используйте Symfony \ Component \ Filesystem \ Exception \ IOExceptionInterface;

и отметьте $ fileSystem = new Filesystem (); if ($ fileSystem-> exists ('path_to_file') == true) {...

Ленве Галатил
источник