【问题标题】:best way to determine if a URL is an image in PHP确定 URL 是否是 PHP 中的图像的最佳方法
【发布时间】:2010-10-15 04:27:28
【问题描述】:

使用 PHP,给定一个 URL,我如何判断它是否是一个图像?

URL 没有上下文 - 它只是在纯文本文件的中间,或者可能只是一个字符串。

我不想要高开销(例如读取 URL 的内容),因为这可能会为页面上的许多 URL 调用。鉴于此限制,识别所有图像并不是必需的,但我希望有一个相当好的猜测。

目前我只是在看文件扩展名,但感觉应该有比这更好的方法。

这是我目前拥有的:

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

编辑:如果它对其他人有用,这里是使用 Emil 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;
  }

【问题讨论】:

    标签: php image url


    【解决方案1】:
    if(is_array(getimagesize($urlImg)))
        echo 'Yes it is an image!';
    

    【讨论】:

    • getimagesize() 下载整个图像文件。
    • 可能不是您要查找的内容:这会下载整个文件,并且会大大减慢整个过程
    【解决方案2】:

    损坏或找不到图片链接的快速解决方案
    我建议你不要使用 getimagesize() 因为它会第一次下载图像然后它会检查图像大小+如果这不会图像那么它会抛出异常所以使用下面的代码

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

    注意: 此当前代码可帮助您识别损坏或未找到的 url 图像这不会帮助您识别图像类型或标题

    【讨论】:

    • 这只会告诉您该 URL 是否存在,因此甚至说它会找到“图像”是一种误导,因为它会找到“任何东西”。
    【解决方案3】:

    类似于某些给定的答案,但逻辑略有不同。

    $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.
    }
    

    @ 是一个error control operator

    请注意,我们在条件中使用了“严格”​​运算符 === FALSE,因为如果在大海捞针中找到针,strpos($headers['Content-Type'], 'image/') 在我们的用例中确实返回 0。使用 == 进行类型转换会错误地解释为 FALSE

    【讨论】:

      【解决方案4】:

      除了 Emil H 的回答:

      使用get_headers()来检查一个url的内容类型而不用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
      
              }
          }
      

      【讨论】:

      • $url_headers['Content-Type'] 很可能是一个数组,在分配$type 之前应该检查一下。另外,请注意跟踪您读取数组的顺序(如果只对最终内容类型感兴趣,在所有重定向之后,那么您将需要数组中的最后一项)
      【解决方案5】:

      我们可以使用 exif_imagetype 来检查图像类型,所以它不允许任何其他内容类型。它只允许图像,我们可以将它们限制为几种图像类型,下面的示例代码展示了如何允许 GIF 图像类型。

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

      您可以使用以下图像类型,

       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
      

      更多详情:link

      【讨论】:

        【解决方案6】:

        编辑:适用于带有流行图片扩展名的静态图片。

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

        【讨论】:

        • 这是一张图片contulmeu.moldcell.md/ps/selfcare_uni/crypt/… 但此网址不会通过您的验证!你错了。
        • Jenechka 指出这种技术非常有限: 1. 它假定所有图像文件都有有效的扩展; 2. 不支持作者没有想到/程序编写后创建的图像格式
        • 这不是完美的解决方案。但我喜欢它,因为它很简单
        【解决方案7】:

        您可以使用HTTP HEAD 请求并检查内容类型。这可能是一个很好的妥协。可以使用PHP Streams 来完成。 Wez Furlong 有一个article,它展示了如何使用这种方法来发送 post 请求,但它可以很容易地适应发送 HEAD 请求。您可以使用 stream_get_meta_data() 从 http 响应中检索标头。

        当然,这并不是真正的 100%。一些服务器发送不正确的标头。但是,它将处理通过脚本传递图像并且正确的文件扩展名不可用的情况。真正确定的唯一方法是实际检索图像 - 全部或前几个字节,正如 thomasrutter 所建议的那样。

        【讨论】:

        • 我不会将此描述为防弹。浏览器会忽略
        • 是的。我同意。我会改变语言。 :) 不过,我确实认为这是最好的方法,无需检索 url 的实际内容。
        • (取消投票)是的,我认为现在这是一个不错的选择 :)
        • 我喜欢检查内容类型的想法,但我也会考虑您的观点,即内容类型可能并不总是准确的。 $headers = get_headers($url, 1); $type = $headers["Content-Type"];
        【解决方案8】:

        有几种不同的方法。

        • 通过查找文件开头的幻数来嗅探内容。例如,GIF 使用 GIF87 或 GIF89 作为文件的前五个字节(在 ascii 中)。不幸的是,这无法告诉您图像中是否存在错误或图像是否包含恶意内容。以下是各种图像文件的一些神奇数字(请随意使用):

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

          像这样嗅探内容可能最符合您的要求;您只需要阅读并下载文件的前几个字节(经过标题)。

        • 使用 GD 库加载图像,看看它是否加载无错误。这可以告诉您图像是否有效,没有错误。不幸的是,这可能不符合您的要求,因为它需要下载完整的图像。

        • 如果您真的不想对图像发出 HTTP 请求,那么这将排除嗅探和获取 HTTP 标头。但是,您可以尝试通过链接的上下文来确定某物是否是图像。在

        不幸的是,文件可能既是有效图像,又是包含有害内容的 ZIP 文件,这些有害内容可能会被有害网站以 Java 形式执行 - 请参阅 the GIFAR exploit。您几乎可以肯定地通过将图像加载到像 GD 这样的库中并对其执行一些重要的过滤器来防止此漏洞,例如将其软化或锐化少量(即使用卷积过滤器)并将其保存到新文件 无需传输任何元数据。

        试图仅通过内容类型来确定某物是否是图像是非常不可靠的,几乎与检查文件扩展名一样不可靠。当使用

        【讨论】:

        • 感谢您的详细回答,但对于我的应用程序来说,在显示页面之前可能会读取数百张图片,这可能会导致太多开销。
        • 你读过我的第三个点了吗?不确定您是否从
        • 啊 - 刚刚看到您从文本文件中获取图像 URL。可能需要某种轻型 HTTP 请求,例如我的第一个点。请注意,它的开销并不比 HTTP HEAD 方法显着增加。
        • 请注意,读取图像开头不会有更多开销,但会涉及更多编码,并且内容类型应该足够好。反正比我的扩展检查好多了!
        猜你喜欢
        • 2023-03-18
        • 1970-01-01
        • 1970-01-01
        • 2017-10-21
        • 1970-01-01
        • 2011-05-25
        • 2023-03-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多