Правильный способ PHP проверить, существует ли внешнее изображение?

Я знаю, что есть по крайней мере 10 одинаковых вопросов с ответами, но ни один из них не работает для меня безупречно. Я пытаюсь проверить, существует ли внутреннее или внешнее изображение (действителен ли URL-адрес изображения?).

  1. fopen($url, 'r') не работает, если я не использую @fopen():

    Warning: fopen(http://example.com/img.jpg) [function.fopen]: failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found in file.php on line 21
    
  2. getimagesize($img) не работает, если изображение не существует (PHP 5.3.8):

    Warning: getimagesize() [function.getimagesize]: php_network_getaddresses: getaddrinfo failed
    
  3. CURL не работает, потому что он не поддерживается некоторыми серверами (хотя он присутствует в основном везде).
  4. fileExists() терпит неудачу, потому что он не работает с внешними URL-адресами и не может проверить, имеем ли мы дело с изображением.

Четыре метода, которые являются наиболее распространенными ответами на этот вопрос, неверны. Каким будет правильный способ сделать это?


person Atadj    schedule 19.12.2012    source источник
comment
cURL - это правильный ответ. Если он не поддерживается вашим сервером, настройте его.   -  person Brad    schedule 19.12.2012
comment
@Bard Я разрабатываю темы WP - я не могу просить людей настроить это. Это должно работать из коробки для всех.   -  person Atadj    schedule 19.12.2012
comment
Все ваши другие методы потерпят неудачу, если url fopen отключен, поэтому вы ничего не получите.   -  person Tom B    schedule 19.12.2012
comment
@Paul, я никогда не использовал провайдера виртуального хостинга, у которого не был включен cURL. Если вы не можете ожидать, что люди будут использовать cURL, вы, вероятно, не можете ожидать, что они будут использовать что-либо. Вам нужен способ получить код состояния HTTP из ответа... cURL является стандартом де-факто и правильным способом сделать это.   -  person Brad    schedule 19.12.2012
comment
Я знаю, что @ обычно не одобряют, но я считаю, что это один из немногих законных вариантов его использования. Пока вы предвидите и обрабатываете потенциальное состояние ошибки, подавление сообщения об ошибке приемлемо (ИМХО). Сказав это, вы все равно не должны беспокоиться о сообщении об ошибке в рабочей среде, потому что display_errors должно быть отключено ;-)   -  person DaveRandom    schedule 19.12.2012
comment
@DaveRandom, если cURL недоступен, вы можете поспорить, что оболочки HTTP fopen также недоступны. fopen на HTTP встречается гораздо реже, чем cURL.   -  person Brad    schedule 19.12.2012
comment
@Brad Ну, в таком случае ты все равно облажался. Если сервер не предоставляет никакого механизма для выполнения HTTP-запроса, игра окончена.   -  person DaveRandom    schedule 19.12.2012
comment
@DaveRandom, точно. Даже если вы хотите открыть сокетное соединение, оно также обычно отключается.   -  person Brad    schedule 19.12.2012
comment
Спасибо Bard, TomB и DaveRandom за ваши ответы! Я думаю, что все ответы на этот вопрос могут добавить что-то интересное. Я выберу один из этих методов, потому что они на самом деле имеют почти 100% вероятность работы (на самом деле, CURL тоже).   -  person Atadj    schedule 19.12.2012
comment
Зачем вообще использовать getimagesize()? Разве вы не хотели просто посмотреть, существует ли файл?   -  person Ja͢ck    schedule 19.12.2012


Ответы (8)


getimagesize($img) fails when image doesn't exist: не уверен, что вы понимаете, чего хотите .....

ИЗ PHP-ДОКА

Функция getimagesize() определяет размер любого заданного файла изображения и возвращает размеры вместе с типом файла и текстовой строкой высоты/ширины, которая будет использоваться внутри обычного тега HTML IMG и соответствующего типа содержимого HTTP.

В случае ошибки возвращается FALSE.

Пример

$img = array("http://i.stack.imgur.com/52Ha1.png","http://example.com/img.jpg");
foreach ( $img as $v ) {
    echo $v, getimagesize($v) ? " = OK  \n" : " = Not valid \n";
}

Выход

http://i.stack.imgur.com/52Ha1.png = OK  
http://example.com/img.jpg = Not valid 

getimagesize работает нормально

Редактировать

@Paul Пол, но ваш вопрос, по сути, говорит о том, как мне справиться с этим, чтобы я не получил ошибку при возникновении ошибки. И ответ на это: вы не можете. Потому что все эти функции вызовут ошибку при наличии условия ошибки. Итак (если вам не нужна ошибка), вы подавляете ее. Ничто из этого не должно иметь значения в рабочей среде, потому что вы все равно не должны отображать ошибки ;-) – DaveRandom

person Baba    schedule 19.12.2012
comment
В моем случае на PHP 5.3 он возвращает вышеупомянутое предупреждение PHP. Можешь взглянуть на это? Было бы отлично, если бы он возвращал false. У меня средний виртуальный хостинг (но хороший с множеством включенных функций). - person Atadj; 19.12.2012
comment
Проблема в том, что getimagesize() просто использует оболочку HTTP fopen, где и происходит ваше предупреждение. - person Brad; 19.12.2012
comment
@Bard Есть ли способ превратить это во что-то, что не будет возвращать предупреждение PHP? Это просто странно - я впервые вижу и использую эту функцию, и, похоже, она не работает на виртуальном хостинге. - person Atadj; 19.12.2012
comment
@Baba С подавлением ошибок многое будет работать, но кажется неправильным иметь предупреждения, уведомления и ошибки. - person Atadj; 19.12.2012
comment
Он отлично работает на PHP 5.3.19, см. codepad.viper-7.com/LXWqaB, какая версия PHP вы используете? - person Baba; 19.12.2012
comment
@Paul ... но ваш вопрос, по сути, говорит о том, как мне справиться с этим, чтобы я не получил ошибку при возникновении ошибки. И ответ на это: вы не можете. Потому что все эти функции вызовут ошибку при наличии условия ошибки. Итак (если вам не нужна ошибка), вы подавляете ее. Ничто из этого не должно иметь значения в производстве, потому что вы все равно не должны отображать ошибки ;-) - person DaveRandom; 19.12.2012
comment
@Baba Я тебе верю, но я только говорю, что это не сработало для меня и до сих пор не работает для меня на PHP 5.3.8, поэтому я просто подумал, что это неправильный способ сделать это. - person Atadj; 19.12.2012
comment
@Baba Да, fopen и getimagesize с подавлением ошибок будут работать везде с вероятностью 99% :) Я думаю, что теперь понятно, что нужно использовать и что на самом деле все эти методы правильные. Я просто пытаюсь избежать ошибок PHP и по возможности возвращать false. Спасибо за вашу помощь! - person Atadj; 19.12.2012
comment
Из комментария к документации PHP getimagesize загрузит все изображение перед он проверяет запрошенную информацию. Это очень медленно работает с большими изображениями, доступ к которым осуществляется удаленно. - person Fahmi; 18.08.2016

Этот код на самом деле предназначен для проверки файла... Но он работает для изображений!

$url = "http://www.myfico.com/Images/sample_overlay.gif";
$header_response = get_headers($url, 1);
if ( strpos( $header_response[0], "404" ) !== false )
{
   // FILE DOES NOT EXIST
} 
else 
{
   // FILE EXISTS!!
}
person sk8terboi87 ツ    schedule 19.12.2012
comment
если вы действительно хотите, чтобы он проверял изображения, используйте что-то вроде: $header_response['Content-Type'] == image/jpeg - person Jonathan Joosten; 29.04.2014
comment
А что, если изображение повреждено? - person Leonardo Sapuy; 09.06.2014

Если вы используете PHP >=5.0.0, вы можете передать дополнительный параметр в fopen, чтобы указать параметры контекста для HTTP, в том числе игнорировать ли коды состояния сбоя.

$contextOptions = array( 'http' => array('ignore_errors' => true));

$context = stream_context_create($contextOptions);

$handle = fopen($url, 'r', false, $context);
person Brian Driscoll    schedule 19.12.2012
comment
Кажется, хороший ответ! И может использоваться вместе с getimagesize($img). - person Atadj; 19.12.2012

Используйте fsockopen, подключитесь к серверу, отправьте запрос HEAD и посмотрите, какой статус вы получите обратно.

Единственный раз, когда вам нужно знать о проблемах, это если домен не существует.

Пример кода:

$file = "http://example.com/img.jpg";
$path = parse_url($file);
$fp = @fsockopen($path['host'],$path['port']?:80);
if( !$fp) echo "Failed to connect... Either server is down or host doesn't exist.";
else {
  fputs($fp,"HEAD ".$file." HTTP/1.0\r\n"
     ."Host: ".$path['host']."\r\n\r\n");
  $firstline = fgets($fp);
  list(,$status,$statustext) = explode(" ",$firstline,3);
  if( $status == 200) echo "OK!";
  else "Status ".$status." ".$statustext."...";
}
person Niet the Dark Absol    schedule 19.12.2012
comment
Есть еще множество хостов, которые не поддерживают запрос HEAD. К сожалению, GET по-прежнему требуется, если вы не хотите сначала попробовать HEAD и вернуться к GET. - person Brad; 19.12.2012

Для этого вы можете использовать пакет PEAR/HTTP_Request2. Вы можете найти его здесь

Вот пример. В примере предполагается, что вы правильно установили или загрузили пакет HTTP_Request2. Он использует адаптер сокета старого стиля, а не завиток.

<?php

require_once 'HTTP/Request2.php';
require_once 'HTTP/Request2/Adapter/Socket.php';

$request = new HTTP_Request2 (
    $your_url, 
    HTTP_Request2::METHOD_GET,
    array('adapter' => new HTTP_Request2_Adapter_Socket())
);

switch($request->send()->getResponseCode()) {

    case 404 : 
        echo 'not found';
        break;

    case 200 :
        echo 'found';
        break;

    default :
        echo 'needs further attention';

}
person hek2mgl    schedule 19.12.2012
comment
Если у него есть проблемы с наличием cURL под рукой, это, безусловно, не удастся, если оно не связано с темой. - person Brad; 19.12.2012
comment
Он также поддерживает адаптер сокета старого стиля. это работает без завитка. подготовка примера - person hek2mgl; 19.12.2012

Я нашел, попробуйте поймать лучшее решение для этого. Он отлично работает со мной.

try{
      list($width, $height) = getimagesize($h_image->image_url);
   }
catch (Exception $e)
    {
    }
person VijayRana    schedule 31.12.2015

Я знаю, что вы написали «без завитка», но все же кому-то это может показаться полезным:

function curl_head($url) {

    $ch = curl_init($url);

    //curl_setopt($ch, CURLOPT_USERAGENT, 'Your user agent');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_HEADER, 1); # get headers
    curl_setopt($ch, CURLOPT_NOBODY, 1); # omit body
    //curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1); # do SSL check
    //curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); # verify domain within cert
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); # follow "Location" redirs
    //curl_setopt($ch, CURLOPT_TIMEOUT_MS, 700); # dies after 700ms

    $result = curl_exec($ch);

    curl_close($ch);

    return $result;
}

print_r(curl_head('https://www.example.com/image.jpg'));

Вы увидите что-то вроде этого HTTP/1.1 200 OK или HTTP/1.1 404 Not Found в возвращаемом массиве заголовков. Вы также можете выполнять несколько параллельных запросов с помощью curl multi.

person Jasom Dotnet    schedule 27.02.2017

Есть несколько шагов, нет единого решения:

  1. Подтвердить URL-адрес
  2. Проверьте, доступен ли файл (можно сделать непосредственно с шага 3)
  3. Загрузите образ в файл tmp.
  4. Используйте getimagesize, чтобы проверить размер изображения.

Для такого рода работы вы можете поймать исключения и хорошо их обработать, чтобы определить свой ответ. В этом случае вы можете даже подавить ошибки, потому что предполагается, что их трюк может потерпеть неудачу. Таким образом, вы правильно обрабатываете ошибки.

Потому что невозможно проверить его на 100%, не загрузив фактическое изображение. Поэтому шаги 1 и 2 обязательны, а 3 и 4 необязательны для более точного ответа.

person Luc Franken    schedule 19.12.2012