如何使用 PHP 检查远程文件是不是存在?

Posted

技术标签:

【中文标题】如何使用 PHP 检查远程文件是不是存在?【英文标题】: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: How to check if image file exists?的可能重复 【参考方案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://***.com/favicon.ico');
if ($exists) 
    echo 'file exists';
 else 
    echo 'file does not exist';   

【讨论】:

remoteFileExists('***.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=***.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,而且速度更快***.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。

【讨论】:

相关:PHP: get_headers set temporary stream_context 相关:What is the best way to check if a URL exists in PHP?(2010 年 12 月 14 日) 有关 PHP 的 $http_response_header 的更多信息,请参阅 php.net/manual/en/reserved.variables.httpresponseheader.php。 第二个变体对我有用,与默认的 file_get_contents 调用(没有自定义 stream_context)相比,它快了 50%,即请求从 3.4 秒到 1.7 秒。 @ErikČerpnjak:如果没有“自定义”stream_context,它是默认的。您可以从默认上下文中获取选项,并查看它们与您的自定义上下文有何不同。这应该让您了解为什么时间不同。 - php.net/stream-context-get-default 和 php.net/stream-context-get-options【参考方案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/***/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"  />')['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/***/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) ...

【讨论】:

以上是关于如何使用 PHP 检查远程文件是不是存在?的主要内容,如果未能解决你的问题,请参考以下文章

如何检查远程文件是不是存在于代理后面

如何使用 phpseclib 通过 SFTP 检查上传的文件是不是存在

如何仅使用 (*.pdf) 之类的扩展名检查 php 中是不是存在文件

Android 使用其 URL 检查文件是不是存在于远程服务器中

如何使用 XPath 检查 <Success /> 节点是不是存在

cmd dos 检查远程FTP文件夹是不是存在