najlepszy sposób na określenie, czy adres URL jest obrazem w PHP

Używając PHP, podanego adresu URL, jak Mogę określić, czy jest to obraz?

Nie ma kontekstu dla adresu URL - jest on po prostu w środku zwykłego pliku tekstowego, a może po prostu ciągiem znaków.

Nie chcę wysokich kosztów (np. czytania zawartości adresu URL) , ponieważ może to być wywołane dla wielu adresów URL na stronie. Biorąc pod uwagę to ograniczenie, nie jest istotne, aby wszystkie obrazy były identyfikowane, ale chciałbym dość dobrego zgadywania.

W tej chwili patrzę właśnie na rozszerzenie pliku, ale wydaje mi się, że powinien być lepszy sposób niż ten.

Oto co aktualnie mam:

  function isImage( $url )
  {
    $pos = strrpos( $url, ".");
    if ($pos === false)
      return false;
    $ext = strtolower(trim(substr( $url, $pos)));
    $imgExts = array(".gif", ".jpg", ".jpeg", ".png", ".tiff", ".tif"); // this is far from complete but that's always going to be the case...
    if ( in_array($ext, $imgExts) )
      return true;
    return false;
  }

Edit: w przypadku, gdy jest to przydatne dla kogokolwiek innego tutaj jest ostateczna funkcja przy użyciu techniki z odpowiedzi Emila H:

  function isImage($url)
  {
     $params = array('http' => array(
                  'method' => 'HEAD'
               ));
     $ctx = stream_context_create($params);
     $fp = @fopen($url, 'rb', false, $ctx);
     if (!$fp) 
        return false;  // Problem with url

    $meta = stream_get_meta_data($fp);
    if ($meta === false)
    {
        fclose($fp);
        return false;  // Problem reading data from url
    }

    $wrapper_data = $meta["wrapper_data"];
    if(is_array($wrapper_data)){
      foreach(array_keys($wrapper_data) as $hh){
          if (substr($wrapper_data[$hh], 0, 19) == "Content-Type: image") // strlen("Content-Type: image") == 19 
          {
            fclose($fp);
            return true;
          }
      }
    }

    fclose($fp);
    return false;
  }
Author: danio, 2009-03-24

8 answers

Możesz użyć żądania HTTP HEAD i sprawdzić typ zawartości. To może być dobry kompromis. Można to zrobić za pomocą strumieni PHP. Wez Furlong ma Artykuł, który pokazuje, jak używać tego podejścia do wysyłania żądań post, ale można go łatwo dostosować do wysyłania żądań HEAD. Możesz pobrać nagłówki z odpowiedzi http za pomocą stream_get_meta_data().

Oczywiście to nie jest naprawdę 100%. Niektóre serwery wysyłają nieprawidłowe nagłówki. Będzie jednak obsługa przypadków, w których obrazy są dostarczane za pośrednictwem skryptu, a prawidłowe rozszerzenie pliku nie jest dostępne. Jedynym sposobem, aby być naprawdę pewnym, jest odzyskanie obrazu - albo całego, albo kilku pierwszych bajtów, jak zasugerował thomasrutter.

 28
Author: Emil H,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-24 11:31:32

Istnieje kilka różnych podejść.

  • Powąchaj zawartość, szukając magicznej liczby na początku pliku. Na przykład GIF używa GIF87 lub GIF89 jako pierwszych pięciu bajtów pliku (w ascii). Niestety nie można stwierdzić, czy obraz zawiera błąd lub czy zawiera złośliwą zawartość. Oto kilka magicznych liczb dla różnych typów plików graficznych (możesz ich używać):

    "\xff\xd8\xff" => 'image/jpeg',
    "\x89PNG\x0d\x0a\x1a\x0a" => 'image/png',
    "II*\x00" => 'image/tiff',
    "MM\x00*" => 'image/tiff',
    "\x00\x00\x01\x00" => 'image/ico',
    "\x00\x00\x02\x00" => 'image/ico',
    "GIF89a" => 'image/gif',
    "GIF87a" => 'image/gif',
    "BM" => 'image/bmp',
    

    Wąchanie takiej treści prawdopodobnie będzie najlepiej dopasuj się do swoich wymagań; będziesz musiał tylko przeczytać i dlatego pobrać kilka pierwszych bajtów pliku(obok nagłówka).

  • Załaduj obraz za pomocą biblioteki GD, aby sprawdzić, czy ładuje się bez błędów. Dzięki temu dowiesz się, czy obraz jest poprawny, bez błędu, czy nie. Niestety to prawdopodobnie nie spełnia twoich wymagań, ponieważ wymaga pobrania pełnego obrazu.

  • Jeśli naprawdę nie chcesz w ogóle wysyłać żądania HTTP dla obrazu, to wyklucza to zarówno sniffing jak i getting HTTP headers. Możesz jednak spróbować określić, czy coś jest obrazem po kontekście, w którym jest powiązane. Coś związanego za pomocą atrybutu src w elemencie

Niestety, możliwe jest, że plik może być zarówno poprawnym obrazem, jak i plikiem ZIP zawierającym szkodliwe treści, które mogą być uruchamiane jako Java przez szkodliwą stronę - zobacz exploit GIFAR. Możesz prawie na pewno zapobiec tej luce, ładując obraz w bibliotece takiej jak GD i wykonując na nim jakiś nietrywialny filtr, taki jak zmiękczenie lub wyostrzenie go w niewielkiej ilości (np. używając filtra splotowego) i zapisując go do świeżego pliku bez przenoszenia jakichkolwiek metadanych.

Próba określenia, czy coś jest obrazem przez sam typ zawartości, jest dość zawodna, prawie tak samo zawodna jak sprawdzanie rozszerzenia pliku. Podczas ładowania obrazu za pomocą elementu

 13
Author: thomasrutter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-24 11:31:07
if(is_array(getimagesize($urlImg)))
    echo 'Yes it's an image!';
 12
Author: Pedro Soares,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-06 14:27:43

Oprócz odpowiedzi Emila H:

Użycie get_headers() do sprawdzenia typu zawartości url bez pobierania całego pliku za pomocą getimagesize()

    $url_headers=get_headers($url, 1);

    if(isset($url_headers['Content-Type'])){

        $type=strtolower($url_headers['Content-Type']);

        $valid_image_type=array();
        $valid_image_type['image/png']='';
        $valid_image_type['image/jpg']='';
        $valid_image_type['image/jpeg']='';
        $valid_image_type['image/jpe']='';
        $valid_image_type['image/gif']='';
        $valid_image_type['image/tif']='';
        $valid_image_type['image/tiff']='';
        $valid_image_type['image/svg']='';
        $valid_image_type['image/ico']='';
        $valid_image_type['image/icon']='';
        $valid_image_type['image/x-icon']='';

        if(isset($valid_image_type[$type])){

            //do something

        }
    }
 10
Author: RafaSashi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-24 14:48:03

Edit: dla obrazów statycznych z popularnym rozszerzeniem obrazu.

<?php
$imgExts = array("gif", "jpg", "jpeg", "png", "tiff", "tif");
$url ='path/to/image.png';
$urlExt = pathinfo($url, PATHINFO_EXTENSION);
if (in_array($urlExt, $imgExts)) {
    echo 'Yes, '.$url.' is an Image';
}

?>
 4
Author: TheMonkeyKing,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-17 20:09:13

Podobne do niektórych udzielonych odpowiedzi, ale z nieco inną logiką.

$headers = @get_headers($url, 1); // @ to suppress errors. Remove when debugging.
if (isset($headers['Content-Type'])) {
  if (strpos($headers['Content-Type'], 'image/') === FALSE) {
    // Not a regular image (including a 404).
  }
  else {
    // It's an image!
  }
}
else {
  // No 'Content-Type' returned.
}

@ jest operatorem kontroli błędów .

Uwaga użyliśmy operatora "strict" === FALSE w warunku, ponieważ strpos($headers['Content-Type'], 'image/') zwraca 0 w naszym przypadku użycia, jeśli igła znajduje się w stogu siana. Z odlewaniem typu przy użyciu ==, które błędnie interpretuje się jako FALSE.

 2
Author: Martin Postma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-14 00:28:24

Możemy użyć exif_imagetype do sprawdzenia typu obrazu, więc nie jest to dozwolone dla innych typów zawartości. Zezwala tylko na obrazy i możemy ograniczyć je do kilku typów obrazów, poniższy przykładowy kod pokazuje, jak zezwolić na typ obrazu GIF.

if (exif_imagetype('image.gif') != IMAGETYPE_GIF) {
    echo 'The picture is not a gif';
}

Możesz użyć następujących typów obrazów,

 IMAGETYPE_GIF
 IMAGETYPE_JPEG
 IMAGETYPE_PNG
 IMAGETYPE_SWF
 IMAGETYPE_PSD
 IMAGETYPE_BMP
 IMAGETYPE_TIFF_II (intel byte order)
 IMAGETYPE_TIFF_MM (motorola byte order)
 IMAGETYPE_JPC
 IMAGETYPE_JP2
 IMAGETYPE_JPX
 IMAGETYPE_JB2
 IMAGETYPE_SWC
 IMAGETYPE_IFF
 IMAGETYPE_WBMP
 IMAGETYPE_XBM
 IMAGETYPE_ICO

Więcej Szczegółów : link

 1
Author: Janith Chinthana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-29 05:47:05

Szybkie rozwiązanie dla uszkodzonych lub nie znalezionych obrazów link
polecam, że nie używaj getimagesize (), ponieważ będzie 1st pobrać obraz, a następnie sprawdzi rozmiar obrazków+jeśli to nie Obraz, to wyrzuci wyjątek, więc użyj poniższego kodu

if(checkRemoteFile($imgurl))
{
//found url, its mean
echo "this is image";
}

function checkRemoteFile($url)
{
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL,$url);
    // don't download content
    curl_setopt($ch, CURLOPT_NOBODY, 1);
    curl_setopt($ch, CURLOPT_FAILONERROR, 1);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    if(curl_exec($ch)!==FALSE)
    {
        return true;
    }
    else
    {
        return false;
    }
}

Uwaga: ten bieżący kod pomaga zidentyfikować uszkodzony lub nie znaleziony obraz url to nie pomoże Ci zidentyfikować typu obrazu lub nagłówków

 0
Author: Hassan Saeed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-23 17:14:20