【问题标题】:How can one check to see if a remote file exists using PHP?如何使用 PHP 检查远程文件是否存在?
【发布时间】:2010-11-02 04:32:53
【问题描述】:

我能找到的最好的,iffclosefopen 类型的东西,使页面加载非常缓慢。

基本上我要做的是:我有一个网站列表,我想在它们旁边显示他们的网站图标。但是,如果网站没有,我想用另一张图片替换它,而不是显示损坏的图片。

【问题讨论】:

  • 我认为您可以使用 CURL 并检查其返回码。但如果是速度问题,那就离线做缓存。
  • 是的,但我仍然建议使用离线脚本(从 cron 运行)来解析网站列表,检查他们是否有网站图标并为前端缓存该数据。如果你不/不能使用 cron,至少缓存你检查的每个新 URL 的结果。
  • 要在浏览器中用占位符图像替换损坏的图像,请考虑使用图像的onerror 的客户端解决方案,例如a solution using jQuery

标签: php file networking testing


【解决方案1】:

您可以通过 CURLOPT_NOBODY 指示 curl 使用 HTTP HEAD 方法。

或多或少

$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 ]。然后,您可以比较时间戳并重新加载网站图标。

【讨论】:

  • 请注意:retcode 在所有 400 个代码上都有错误,因此验证将是 >= 而不仅仅是 >
  • 如果您不提供用户代理字符串,某些网站会阻止访问,因此我建议您按照本指南在 CURLOPT_NOBODY 之外添加 CURLOPT_USERAGENT:davidwalsh.name/set-user-agent-php-curl-spoof
  • @Lyth 3XX 重新编码不是错误,而是重定向。这些应该手动处理或使用 CURLOPT_FOLLOWLOCATION。
  • 使用 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);还要确保相同的代码适用于以 HTTPS 开头的 URL!
【解决方案2】:

正如 Pies 所说,您可以使用 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';   
}

【讨论】:

  • remoteFileExists('stackoverflow.com/') 这也将返回 true,但它只是一个链接。此函数不检查链接内容类型是文件。
【解决方案3】:

CoolGoose 的解决方案很好,但对于大文件来说更快(因为它只尝试读取 1 个字节):

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

【讨论】:

  • +1。这个解决方案相对于 CURL 有什么缺点吗?
  • 你可以直接使用fopen——如果请求返回码是404,fopen返回false。
  • 这真的很慢,对我不起作用(这意味着如果文件路径不正确,它仍然会显示损坏的图像)
  • 如果服务器在图像或文件不存在时进行重定向,则此方法不起作用。当网站使用 mod_rewrite 或其他某种“规则”如何处理请求时,就会发生这种情况。
【解决方案4】:

这不是对您最初问题的回答,而是一种更好的方式来做您想做的事情:

而不是实际尝试直接获取网站的 favicon(考虑到它可能是 /favicon.png、/favicon.ico、/favicon.gif 或什至 /path/to/favicon.png,这是一种皇家痛苦),使用谷歌:

<img src="http://www.google.com/s2/favicons?domain=[domain]">

完成。

【讨论】:

  • 语法有点混乱。所以这里有一个例子:google.com/s2/favicons?domain=stackoverflow.com">
【解决方案5】:

投票最多答案的完整功能:

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
}

【讨论】:

  • 哦!最近几天我一直不在,但本月初几乎是 24/7。谢谢你告诉我!
  • 如果服务器没有响应任何 HTTP 代码(或者 cUrl 没有捕获它),这将不起作用。这经常发生在我身上。例如。如果是图像。
  • 如果 url 被重定向到另一个 URL 或 https 版本怎么办?在这种情况下,这个 curl 代码将无法完成这项工作。最好的方法是获取标题信息并搜索不区分大小写的字符串“200 ok”。
  • @Infoconic 您可以添加curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);。我已经更新了处理 302 重定向的答案。
【解决方案6】:

如果您正在处理图像,请使用 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 前面使用@ 来抑制错误来处理它,但对这种hack 感到内疚。
  • 就我而言,这是最好的方法,因为只要图像/文件不存在,我就会被重定向。我认为使用 @ 抑制错误是不行的,但在这种情况下它是必要的。
  • 我发现我们也可以使用exif_imagetype,而且速度更快stackoverflow.com/a/38295345/1250044
【解决方案7】:
if (false === file_get_contents("http://example.com/path/to/image")) {
    $image = $default_image;
}

应该工作;)

【讨论】:

  • 函数前加@
【解决方案8】:

这可以通过获取 HTTP 状态代码(404 = 未找到)来完成,这可以通过 file_get_contentsDocs 使用上下文选项来实现。以下代码将重定向考虑在内,并将返回最终目的地的状态代码 (Demo):

$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";

如果你不想跟随重定向,你可以这样做(Demo):

$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

【讨论】:

【解决方案9】:

如果出于安全原因将 allow_url_fopen 设置设置为关闭,PHP 的内置函数可能无法用于检查 URL。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 开头。

【讨论】:

    【解决方案10】:

    要检查图像是否存在,exif_imagetype 应该优先于 getimagesize,因为它更快。

    要禁止 E_NOTICE,只需添加错误控制运算符 (@)。

    if (@exif_imagetype($filename)) {
      // Image exist
    }
    

    作为奖励,使用来自exif_imagetype 的返回值 (IMAGETYPE_XXX),我们还可以使用image_type_to_mime_type / image_type_to_extension 获得 mime 类型或文件扩展名。

    【讨论】:

      【解决方案11】:

      一个激进的解决方案是将网站图标显示为默认图标上方的 div 中的背景图像。这样一来,所有开销都将放在客户端上,同时仍然不显示损坏的图像(在所有浏览器 AFAIK 中都会忽略丢失的背景图像)。

      【讨论】:

      • +1 如果您没有检查多个位置的网站图标(favicon.ico、favicon.gif、favicon.png),这似乎是最好的解决方案
      【解决方案12】:
      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!!!";
          }
      

      【讨论】:

        【解决方案13】:

        您可以使用以下内容:

        $file = 'http://mysite.co.za/images/favicon.ico';
        $file_exists = (@fopen($file, "r")) ? true : false;
        

        在尝试检查 URL 上是否存在图像时为我工作

        【讨论】:

          【解决方案14】:

          这对我来说可以检查 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';
                  }
          

          【讨论】:

            【解决方案15】:

            你可以使用:

            $url=getimagesize(“http://www.flickr.com/photos/27505599@N07/2564389539/”);
            
            if(!is_array($url))
            {
               $default_image =”…/directoryFolder/junal.jpg”;
            }
            

            【讨论】:

              【解决方案16】:

              您应该发出 HEAD 请求,而不是 GET 请求,因为您根本不需要 URI 内容。正如 Pies 上面所说,您应该检查状态代码(在 200-299 范围内,您可以选择遵循 3xx 重定向)。

              答案问题包含许多可能有用的代码示例:PHP / Curl: HEAD Request takes a long time on some sites

              【讨论】:

                【解决方案17】:

                还有一个更复杂的选择。您可以使用 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/(原博客暂时下架)

                【讨论】:

                  【解决方案18】:

                  这里所有使用 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。

                  【讨论】:

                    【解决方案19】:

                    如果文件不是外部托管的,您可以将远程 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 才能使用此功能

                    【讨论】:

                      【解决方案20】:

                      如果你使用的是 Laravel 框架或 guzzle 包,还有一个更简单的使用 guzzle 客户端的方法,它也可以在链接重定向时工作:

                      $client = new \GuzzleHttp\Client(['allow_redirects' => ['track_redirects' => true]]);
                      try {
                          $response = $client->request('GET', 'your/url');
                          if ($response->getStatusCode() != 200) {
                              // not exists
                          }
                      } catch (\GuzzleHttp\Exception\GuzzleException $e) {
                          // not exists
                      }
                      

                      文档中的更多内容:https://docs.guzzlephp.org/en/latest/faq.html#how-can-i-track-redirected-requests

                      【讨论】:

                        【解决方案21】:

                        不知道当文件远程不存在时这个是否更快,is_file(),但你可以试一试。

                        $favIcon = 'default FavIcon';
                        if(is_file($remotePath)) {
                           $favIcon = file_get_contents($remotePath);
                        }
                        

                        【讨论】:

                        • 来自文档:“从 PHP 5.0.0 开始,此函数也可以与一些 URL 包装器一起使用。请参阅支持的协议和包装器以确定哪些包装器支持 stat() 系列功能。”
                        • 你的意思是如果你注册一个流包装器这可以工作吗?编辑您的问题以显示一个有效的示例,我将删除我的反对票(如果可以的话,请支持您)。但目前,我用远程文件从 php cli 测试了 is_file,结果为假。
                        • 无工作示例:var_dump(is_file('http://cdn.sstatic.net/stackoverflow/img/sprites.png')); bool(false)
                        【解决方案22】:

                        如果您使用的是 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

                        【讨论】:

                          【解决方案23】:

                          您可以使用文件系统: 使用 Symfony\组件\文件系统\文件系统; 使用 Symfony\Component\Filesystem\Exception\IOExceptionInterface;

                          并检查 $fileSystem = 新文件系统(); if ($fileSystem->exists('path_to_file')==true) {...

                          【讨论】:

                            猜你喜欢
                            • 1970-01-01
                            • 2011-05-07
                            • 1970-01-01
                            • 1970-01-01
                            • 2011-07-24
                            • 2010-11-15
                            • 1970-01-01
                            • 1970-01-01
                            相关资源
                            最近更新 更多