快速远程 PHP 技术检测图像 404

Posted

技术标签:

【中文标题】快速远程 PHP 技术检测图像 404【英文标题】:Fast Remote PHP Technique To Detect Image 404 【发布时间】:2010-04-18 01:42:12 【问题描述】:

在包含图像之前检测远程图像是否不存在时,哪种 php 脚本技术运行速度最快?我的意思是,我不想下载远程图像的所有字节——只要检测它是否存在就足够了。

在这个主题上,但只是略有偏差,我想下载足够的字节来确定 JPEG 的宽度和高度信息。

在我正在研究的这个系统设计中,速度非常重要。

【问题讨论】:

有人在 Twitter 上暗示我可以用 fsockopen 和 fread 来做到这一点,8 个字节用于 PNG 信息,16 个字节用于 JPG 信息。只需要知道那些图像结构。 使用Curl或socket向服务器发送请求头并检查响应码,200 = OK, 404 = Not found. 您想一次为一个文件/网址执行此操作,还是有多个网址?你想检查内容的“神奇”字节,甚至从数据中获取一些信息,即单独的 http 标头不会做? 我从fileformat.info/format/png/corion.htm 读到,我认为我可以通过套接字读取(fsockopen/fread)从 PNG 中删除 28 个字节,然后接下来的 8 个字节被拆分为 4 个字节的宽度和 4 个字节为高度。我得测试一下。 @VolkerK 多个 URL —— 几个小时,在只有有限 CPU 能力的服务器上。 【参考方案1】:

我也修改了@Volomike 的代码以获取宽度。给你...

函数 get_image_dim($sURL) // 请注意,对于 jpeg,您可能需要将 300 更改为更大的值, // 因为一些高度/宽度信息在标题中更远 尝试 $hSock = @ fopen($sURL, 'rb'); 如果($hSock) 而(!feof($hSock)) $vData = fread($hSock, 300); 休息; fclose($hSock); if (strpos(' ' . $vData, 'JFIF')>0) $vData = substr($vData, 0, 300); $asResult = unpack('H*',$vData); $sBytes = $asResult[1]; $宽度 = 0; $高度 = 0; $hex_width = ''; $hex_height = ''; if (strstr($sBytes, 'ffc2')) $hex_height = substr($sBytes, strpos($sBytes, 'ffc2') + 10, 4); $hex_width = substr($sBytes, strpos($sBytes, 'ffc2') + 14, 4); 别的 $hex_height = substr($sBytes, strpos($sBytes, 'ffc0') + 10, 4); $hex_width = substr($sBytes, strpos($sBytes, 'ffc0') + 14, 4); $width = hexdec($hex_width); $height = hexdec($hex_height); 返回数组('width' => $width, 'height' => $height); elseif (strpos(' ' . $vData, 'GIF')>0) $vData = substr($vData, 0, 300); $asResult = unpack('h*',$vData); $sBytes = $asResult[1]; $sBytesH = substr($sBytes, 16, 4); $height = hexdec(strrev($sBytesH)); $sBytesW = substr($sBytes, 12, 4); $width = hexdec(strrev($sBytesW)); 返回数组('width' => $width, 'height' => $height); elseif (strpos(' ' . $vData, 'PNG')>0) $vDataH = substr($vData, 22, 4); $asResult = unpack('n',$vDataH); $height = $asResult[1]; $vDataW = substr($vData, 18, 4); $asResult = unpack('n',$vDataW); $width = $asResult[1]; 返回数组('width' => $width, 'height' => $height); 捕捉(异常 $e) 返回错误;

所以,使用它我们有...

// JPEG $url = 'http://upload.wikimedia.org/wikipedia/commons/thumb/c/ce/Quality_comparison_jpg_vs_saveforweb.jpg/250px-Quality_comparison_jpg_vs_saveforweb.jpg'; // PNG //$url = 'http://upload.wikimedia.org/wikipedia/commons/thumb/4/47/PNG_transparency_demonstration_1.png/280px-PNG_transparency_demonstration_1.png'; // 动图 //$url = 'http://upload.wikimedia.org/wikipedia/commons/e/e2/Sunflower_as_gif_small.gif'; $dim = get_image_dim($url); print_r($dim);

【讨论】:

【参考方案2】:

运行一个 cURL 来执行 HEAD 请求,该请求包含完整的 GET

我没有对此进行测试,但希望你能明白:

<?php
$url = 'http://www.example.com/image.gif';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_NOBODY, true); // this is what sets it as HEAD request
curl_exec($ch);

if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == '200')  // 200 = OK
    // image exists ..


curl_close($ch);
?>

有关 cURL 的更多信息,请参阅 cURL docuentation。

【讨论】:

这太棒了。这是一个开始。但是,如果我能理解 PNG 和 JPG 图像标题,我也许可以用 fsockopen/fread 一口气解决我的两个问题,而不需要这种 curl 技术。但是,为def +1。向我展示一些我不知道的有趣的东西。【参考方案3】:

您应该能够在不加载其全部内容的情况下确定 JPEG 的尺寸。对于基线 JPEG,即非逐行扫描 JPEG,以字节为单位进行扫描,直到遇到 0xFFC0。跳过接下来的三个字节。接下来的两个字节表示高度。它们后面是另外两个指示宽度的字节。

例如,在“FF C0 00 11 08 01 DE 02 D0”中,01DE表示高度为478,02D0表示宽度为720。

【讨论】:

【参考方案4】:

我会发送一个包含RANGE header 的 GET 请求,以尽可能限制实际数据传输(远程服务器可能不接受 RANGE 请求,但仍然值得一试)。无论您是使用套接字(直接)还是使用 curl 来发出请求,都可能没有太大区别。但是......你永远不会知道没有基准。对于 curl,请查看 http://docs.php.net/function.curl-setopt 的“CURLOPT_RANGE”选项

它可能不适合您的个人资料(“几个小时,在只有少量 CPU 能力的服务器上。”)但您可能想尝试一次处理多个 url,即有多个活动连接并且只处理那些不会阻塞读取操作的。如果限制因素主要/仅是 cpu 功率...忘记这部分。 sockets:看看stream_select 卷曲:见curl_multi_exec()

如果 curl 模块不可用,您还可以将 http url 包装器与 stream_context_create() 结合使用来发送包含 RANGE 标头的请求。

看起来您已经想好在收到数据后如何处理数据。

【讨论】:

【参考方案5】:

我认为以下例程将仅检索 JPG、GIF 和 PNG 的图像高度,或者在 404 或其他图像类型上返回 === FALSE 条件。该例程还使用最少的服务器资源执行此操作,因为即使添加了字节限制,file_get_contents() 路由似乎也实际下载了文件,getimagesize() 下载文件也是如此。与此相比,您可以看到性能下降。

这个例程的工作方式是它只从文件中下载 300 个字节。不幸的是,与 GIF 或 PNG 不同,JPEG 在文件中将其高度值推得很远,因此我不得不以字节为单位读取该文件。然后,使用这些字节,它会在该标头中扫描 JFIF、PNG 或 GIF,让我们知道它是哪种文件类型。一旦我们有了它,我们就在每个上使用独特的例程来解析标题。请注意,JPEG 必须首先使用带有 H* 的 unpack(),然后扫描 ffc2 或 ffc0 并进行处理。然而,GIF 必须先 unpack() 和 h* (差别很大)。

这个函数是我经过反复试验创建的,可能是错误的。我在几张图像上运行它,它似乎工作得很好。如果您发现其中有问题,请考虑让我知道。

无论如何,这个系统会让我确定图像高度并丢弃图像,如果太高再找到另一个。在我找到的任何随机图像上,我在 html 的 IMG 标记中设置了宽度,它会自动调整高度的大小——但只有当图像低于某个高度时才会看起来不错。此外,它还会执行 404 检查以查看另一台服务器返回给我的图像是否不是不再存在或禁止跨站点链接的图像。而且由于我手动将图像设置为固定宽度,因此我不在乎读取图像宽度。您可以调整此函数,通常只需向前看几个小字节即可找到您想要的图像宽度。

function getImageHeight($sURL) 
  try 
    $hSock = @ fopen($sURL, 'rb');
    if ($hSock) 
      while(!feof($hSock)) 
        $vData = fread($hSock, 300);
        break;
      
      fclose($hSock);
      if (strpos(' ' . $vData, 'JFIF')>0) 
        $vData = substr($vData, 0, 300);
        $asResult = unpack('H*',$vData);
        $sBytes = $asResult[1];
        if (strstr($sBytes, 'ffc2')) 
          $sBytes = substr($sBytes, strpos($sBytes, 'ffc2') + 10, 4);
         else 
          $sBytes = substr($sBytes, strpos($sBytes, 'ffc0') + 10, 4);
         
        return hexdec($sBytes);
       elseif (strpos(' ' . $vData, 'GIF')>0) 
        $vData = substr($vData, 0, 300);
        $asResult = unpack('h*',$vData);
        $sBytes = $asResult[1];
        $sBytes = substr($sBytes, 16, 4);
        $sBytes = strrev($sBytes);
        return hexdec($sBytes);
       elseif (strpos(' ' . $vData, 'PNG')>0) 
        $vData = substr($vData, 22, 4);
        $asResult = unpack('n',$vData);
        $nHeight = $asResult[1];
        return $nHeight;
      
    
   catch (Exception $e) 
  return FALSE;

【讨论】:

【参考方案6】:

在本地存储图像。这是非常简单且有保证的解决方案。

【讨论】:

在这种情况下,我的服务器 CPU 和磁盘空间有限。基本上需要数百个博客才能拥有可以工作的远程图像。 贪婪是致命的罪孽之一,@Volomike 如果你买不起图像存储 - 只是不要去数百个博客。做一个。 图片来自公共领域资源,如 Wikipedia、Flickr 和其他 CreativeCommons 资源。我正在应客户的要求在他们过载的服务器上为他们做这件事,我们正试图减少这些博客的瓶颈。 其实你们共同创造另一个瓶颈,呵呵。好吧,贪婪的音符传给了他。 我想我可以创建一个脚本来获取图像 URL点击网站的广告,以支付惩罚?

以上是关于快速远程 PHP 技术检测图像 404的主要内容,如果未能解决你的问题,请参考以下文章

在远程服务器上配置 codeigniter(404 未找到)

c# 远程服务器返回错误: (404) 未找到。

远程服务器错误404,怎么解决,求高人。

如何获取远程存储图像的文件大小? (php)

php 远程代码执行漏洞 怎么修复

如何检测远程服务器的 IP?