Як можна перевірити, чи існує віддалений файл за допомогою PHP?


86

Найкраще, що я міг знайти, щось на зразок if fclose fopenшрифта, робить завантаження сторінки дуже повільним.

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


Я думаю, ви можете використовувати CURL і перевірити його коди повернення. Але якщо проблема полягає в швидкості, просто зробіть це в автономному режимі та кешуйте.
— Міхал Татаринович

Так, але я все одно рекомендую використовувати автономний скрипт (запущений з cron), який аналізує список веб-сайтів, перевіряє, чи є у них значки, та кешує ці дані для інтерфейсу. Якщо ви не можете / не можете використовувати cron, принаймні кешуйте результати для кожної нової перевіреної URL-адреси.
— Міхал Татаринович

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 => [favicon, timestamp]. Потім можна порівняти позначку часу та перезавантажити значок.


6
лише примітка: retcodeпомилки на всіх 400 кодах, тому перевірка буде >=не просто>
— Джастін Булл

4
Деякі сайти блокують доступ, якщо ви не надаєте рядок агента користувача, тому я пропоную слідувати цьому посібнику, щоб додати CURLOPT_USERAGENT на додаток до CURLOPT_NOBODY: davidwalsh.name/set-user-agent-php-curl-spoof
— rlorenzo

6
Реткоди @Lyth 3XX - це не помилка, а переспрямування. Їх слід обробляти вручну або за допомогою CURLOPT_FOLLOWLOCATION.
— Ramon Poca

6
Використовуйте curl_setopt ($ ch, CURLOPT_SSL_VERIFYPEER, false); а також переконатися, що той самий код працює для URL-адрес, починаючи з HTTPS!
— Krishan Gopal

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, але це просто посилання. Ця функція не перевіряє тип вмісту посилання - файл.
— Donatas Navidonskis

36

Рішення CoolGoose хороше, але це швидше для великих файлів (оскільки він намагається прочитати лише 1 байт):

if (false === file_get_contents("http://example.com/path/to/image",0,null,0,1)) {
    $image = $default_image;
}

+1. Чи існують які недоліки цього рішення проти CURL?
— Адріано Варолі Пьяцца

1
ви можете просто використовувати fopen- якщо код повернення запиту - 404, fopen повертає false.
— s3v3n

це дійсно повільно і не спрацювало для мене (мається на увазі, що воно все ще відображало зламане зображення, якщо шлях до файлу був неправильним)
— Гельмут,

Цей підхід не працює, якщо сервер робить переспрямування, коли зображення чи файл не існує. Це трапляється, коли сайт використовує 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 ">
— Хабіб Первад

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 його не вловлює). Що трапляється у мене досить часто. Напр. у разі зображень.
— Ваці

що робити, якщо URL-адресу переспрямовано на іншу URL-адресу або версію https? У цьому випадку цей код curl не зможе виконати цю роботу. найкращий спосіб - отримати інформацію про заголовок та знайти рядок, що не враховує регістр, "200 добре"
— 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 попередження, коли віддалений ресурс недоступний. На даний момент я вирішив це, придушивши помилку, використовуючи @перед 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 .




Більше про PHP $http_response_headerдив. На php.net/manual/en/reserved.variables.httpresponseheader.php .
— Big McLargeHuge

1
Другий варіант працював у мене, і порівняно із викликом file_get_contents за замовчуванням (без власного потокового_тексту) він був на 50% швидшим, тобто від 3,4 до 1,7 с для запиту.
— Ерік Черпняк,

@ ErikČerpnjak: Якщо немає "спеціального" потокового_контексту, це за замовчуванням. Ви можете отримати параметри із контексту за замовчуванням і подивитися, як вони відрізняються від вашого користувацького контексту. Це має дати вам певне розуміння, чому час відрізняється. - 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;
}

Має працювати;)


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.


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!!!";
    }

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”;
}

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';
        }

1

Вам слід надсилати запити HEAD, а не GET, оскільки вміст URI вам зовсім не потрібен. Як писав Пирог вище, вам слід перевірити наявність коду стану (у діапазонах 200-299, і ви можете за бажанням перейти за переспрямуванням 3xx).

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


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 (); якщо ($ fileSystem-> існує ('path_to_file') == true) {...

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.