如何使用 PHP 检查远程文件是不是存在?
Posted
技术标签:
【中文标题】如何使用 PHP 检查远程文件是不是存在?【英文标题】:How can one check to see if a remote file exists using PHP?如何使用 PHP 检查远程文件是否存在? 【发布时间】:2010-11-02 04:32:53 【问题描述】:我能找到的最好的,if
fclose
fopen
类型的东西,使页面加载非常缓慢。
基本上我要做的是:我有一个网站列表,我想在它们旁边显示他们的网站图标。但是,如果网站没有,我想用另一张图片替换它,而不是显示损坏的图片。
【问题讨论】:
我认为您可以使用 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_contents
Docs 使用上下文选项来实现。以下代码将重定向考虑在内,并将返回最终目的地的状态代码 (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 检查文件是不是存在于远程服务器中