无需下载文件的远程文件大小
Posted
技术标签:
【中文标题】无需下载文件的远程文件大小【英文标题】:Remote file size without downloading file 【发布时间】:2011-02-05 20:28:49 【问题描述】:有没有办法在不下载文件的情况下获取远程文件http://my_url/my_file.txt 的大小?
【问题讨论】:
【参考方案1】:发现了一些关于这个here:
这是获取遥控器大小的最佳方法(我发现) 文件。请注意,HEAD 请求不会获得请求的实际正文, 他们只是检索标题。所以向资源发出 HEAD 请求 即 100MB 将花费与 HEAD 请求相同的时间 1KB 的资源。
<?php
/**
* Returns the size of a file without downloading it, or -1 if the file
* size could not be determined.
*
* @param $url - The location of the remote file to download. Cannot
* be null or empty.
*
* @return The size of the file referenced by $url, or -1 if the size
* could not be determined.
*/
function curl_get_file_size( $url )
// Assume failure.
$result = -1;
$curl = curl_init( $url );
// Issue a HEAD request and follow any redirects.
curl_setopt( $curl, CURLOPT_NOBODY, true );
curl_setopt( $curl, CURLOPT_HEADER, true );
curl_setopt( $curl, CURLOPT_RETURNTRANSFER, true );
curl_setopt( $curl, CURLOPT_FOLLOWLOCATION, true );
curl_setopt( $curl, CURLOPT_USERAGENT, get_user_agent_string() );
$data = curl_exec( $curl );
curl_close( $curl );
if( $data )
$content_length = "unknown";
$status = "unknown";
if( preg_match( "/^HTTP\/1\.[01] (\d\d\d)/", $data, $matches ) )
$status = (int)$matches[1];
if( preg_match( "/Content-Length: (\d+)/", $data, $matches ) )
$content_length = (int)$matches[1];
// http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
if( $status == 200 || ($status > 300 && $status <= 308) )
$result = $content_length;
return $result;
?>
用法:
$file_size = curl_get_file_size( "http://***.com/questions/2602612/php-remote-file-size-without-downloading-file" );
【讨论】:
但请记住,可以在没有 Content-length 的情况下进行响应。 像@macki 建议的那样使用curl_getinfo
不是更好吗?
这对我不起作用,因为 get_user_agent_string()
没有定义。删除整条线使整个工作正常。
如果服务器不支持 HEAD 则返回 405
与@Rapti 一样,我收到了get_user_agent_string()
的错误消息,这可能是代码中遗漏的本地函数。它在行注释掉时起作用,但可能代替函数 use$_SERVER['HTTP_USER_AGENT']
【参考方案2】:
试试这个代码
function retrieve_remote_file_size($url)
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_HEADER, TRUE);
curl_setopt($ch, CURLOPT_NOBODY, TRUE);
$data = curl_exec($ch);
$size = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
curl_close($ch);
return $size;
【讨论】:
如果这对您不起作用,您可能需要添加curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
。
不适用于我的图像。我确实将 CURLOPT_FOLLOWLOCATION
设置为 true。
@Abenil 添加此参数。 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
@Davinder Kumar:非常感谢,添加您的代码使上述代码有效。
欢迎您! @TrungLeNguyenNhat【参考方案3】:
正如多次提到的,要走的路是从响应头的Content-Length
字段中检索信息。
但是,你应该注意
您正在探测的服务器不一定实现 HEAD 方法(!) 当 PHP 有get_headers()
时,绝对不需要使用 fopen
或类似方法手动制作 HEAD 请求(甚至可能不支持),甚至不需要调用 curl 库(请记住:K.I.S.S. )
get_headers()
的使用遵循K.I.S.S. principle并且即使您正在探测的服务器不支持 HEAD 请求也有效。
所以,这是我的版本(噱头:返回人类可读的格式化大小;-)):
要点:https://gist.github.com/eyecatchup/f26300ffd7e50a92bc4d(curl 和 get_headers 版本) get_headers()-版本:
<?php
/**
* Get the file size of any remote resource (using get_headers()),
* either in bytes or - default - as human-readable formatted string.
*
* @author Stephan Schmitz <eyecatchup@gmail.com>
* @license MIT <http://eyecatchup.mit-license.org/>
* @url <https://gist.github.com/eyecatchup/f26300ffd7e50a92bc4d>
*
* @param string $url Takes the remote object's URL.
* @param boolean $formatSize Whether to return size in bytes or formatted.
* @param boolean $useHead Whether to use HEAD requests. If false, uses GET.
* @return string Returns human-readable formatted size
* or size in bytes (default: formatted).
*/
function getRemoteFilesize($url, $formatSize = true, $useHead = true)
if (false !== $useHead)
stream_context_set_default(array('http' => array('method' => 'HEAD')));
$head = array_change_key_case(get_headers($url, 1));
// content-length of download (in bytes), read from Content-Length: field
$clen = isset($head['content-length']) ? $head['content-length'] : 0;
// cannot retrieve file size, return "-1"
if (!$clen)
return -1;
if (!$formatSize)
return $clen; // return size in bytes
$size = $clen;
switch ($clen)
case $clen < 1024:
$size = $clen .' B'; break;
case $clen < 1048576:
$size = round($clen / 1024, 2) .' KiB'; break;
case $clen < 1073741824:
$size = round($clen / 1048576, 2) . ' MiB'; break;
case $clen < 1099511627776:
$size = round($clen / 1073741824, 2) . ' GiB'; break;
return $size; // return formatted size
用法:
$url = 'http://download.tuxfamily.org/notepadplus/6.6.9/npp.6.6.9.Installer.exe';
echo getRemoteFilesize($url); // echoes "7.51 MiB"
附加说明: Content-Length 标头是可选的。因此,作为一个通用解决方案它不是防弹的!
【讨论】:
这应该是公认的答案。没错,Content-Length
是可选的,但它是无需下载即可获得文件大小的唯一方法 - 而get_headers
是获得content-length
的最佳方式。
请注意,这将在此 PHP 进程的所有后续 HTTP 请求中将请求方法的首选项更改为 HEAD。使用 stream_context_create
创建单独的上下文以用于调用 get_headers
(7.1+)。
只是添加,如果您的 URL 或 DOCUMENT 文件名中有空格,这将返回 -1【参考方案4】:
Php 函数 get_headers()
可以让我检查 content-length 为
$headers = get_headers('http://example.com/image.jpg', 1);
$filesize = $headers['Content-Length'];
更多详情:PHP Function get_headers()
【讨论】:
对我来说(使用 nginx)标题是 Content-Length【参考方案5】:当然。发出仅标头请求并查找 Content-Length
标头。
【讨论】:
【参考方案6】:单行最佳解决方案:
echo array_change_key_case(get_headers("http://.../file.txt",1))['content-length'];
php太美味了
function urlsize($url):int
return array_change_key_case(get_headers($url,1))['content-length'];
echo urlsize("http://.../file.txt");
【讨论】:
【参考方案7】:我不确定,但您不能为此使用 get_headers 函数吗?
$url = 'http://example.com/dir/file.txt';
$headers = get_headers($url, true);
if ( isset($headers['Content-Length']) )
$size = 'file size:' . $headers['Content-Length'];
else
$size = 'file size: unknown';
echo $size;
【讨论】:
在这个例子中,$url 上的目标服务器可以利用 get_headers 来保持连接打开,直到 PHP 进程超时(通过非常缓慢地返回标头,而不是慢到让连接失效)。由于总 PHP 进程可能受到 FPM 的限制,当多个“用户”同时访问您的 get_headers 脚本时,这可能会导致一种缓慢的 loris 攻击。【参考方案8】:最简单高效的实现方式:
function remote_filesize($url, $fallback_to_download = false)
static $regex = '/^Content-Length: *+\K\d++$/im';
if (!$fp = @fopen($url, 'rb'))
return false;
if (isset($http_response_header) && preg_match($regex, implode("\n", $http_response_header), $matches))
return (int)$matches[0];
if (!$fallback_to_download)
return false;
return strlen(stream_get_contents($fp));
【讨论】:
OP 表示“不下载文件”。此方法将文件从远程服务器加载到内存中(例如:下载)。即使服务器之间有快速连接,这也很容易超时或在大文件上花费太长时间。注意:您永远不会关闭不在全局范围内的 $fp 此功能不会尽可能长时间地下载正文;如果它包含Content-Length
标头。并且明确的$fp
关闭是不必要的;它会在过期时自动释放。 php.net/manual/en/language.types.resource.php
您可以使用nc -l localhost 8080
轻松确认以上内容
实际上大多数*close
函数在现代PHP中都不是必需的。它们来自两个历史原因:实现限制和模仿 C 语言。
标头不可靠,后备下载不符合 OP。最后,如果您打开一个文件,只需将其关闭即可。垃圾收集器不是懒惰的开发人员节省一行代码的借口。【参考方案9】:
由于这个问题已经被标记为“php”和“curl”,我假设你知道如何在 PHP 中使用 Curl。
如果您设置curl_setopt(CURLOPT_NOBODY, TRUE)
,那么您将发出 HEAD 请求,并且可能会检查响应的“Content-Length”标头,这将只是标头。
【讨论】:
【参考方案10】:试试下面的函数来获取远程文件大小
function remote_file_size($url)
$head = "";
$url_p = parse_url($url);
$host = $url_p["host"];
if(!preg_match("/[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*/",$host))
$ip=gethostbyname($host);
if(!preg_match("/[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*/",$ip))
return -1;
if(isset($url_p["port"]))
$port = intval($url_p["port"]);
else
$port = 80;
if(!$port) $port=80;
$path = $url_p["path"];
$fp = fsockopen($host, $port, $errno, $errstr, 20);
if(!$fp)
return false;
else
fputs($fp, "HEAD " . $url . " HTTP/1.1\r\n");
fputs($fp, "HOST: " . $host . "\r\n");
fputs($fp, "User-Agent: http://www.example.com/my_application\r\n");
fputs($fp, "Connection: close\r\n\r\n");
$headers = "";
while (!feof($fp))
$headers .= fgets ($fp, 128);
fclose ($fp);
$return = -2;
$arr_headers = explode("\n", $headers);
foreach($arr_headers as $header)
$s1 = "HTTP/1.1";
$s2 = "Content-Length: ";
$s3 = "Location: ";
if(substr(strtolower ($header), 0, strlen($s1)) == strtolower($s1)) $status = substr($header, strlen($s1));
if(substr(strtolower ($header), 0, strlen($s2)) == strtolower($s2)) $size = substr($header, strlen($s2));
if(substr(strtolower ($header), 0, strlen($s3)) == strtolower($s3)) $newurl = substr($header, strlen($s3));
if(intval($size) > 0)
$return=intval($size);
else
$return=$status;
if (intval($status)==302 && strlen($newurl) > 0)
$return = remote_file_size($newurl);
return $return;
【讨论】:
这是唯一一个在 Ubuntu Linux apache 服务器上为我工作的。我确实必须在函数开始时初始化 $size 和 $status,否则按原样工作。【参考方案11】:这是另一种适用于不支持HEAD
请求的服务器的方法。
它使用 cURL 来请求带有 HTTP 范围标头的内容,要求文件的第一个字节。
如果服务器支持范围请求(大多数媒体服务器支持),那么它将接收到资源大小的响应。
如果服务器没有响应一个字节范围,它会寻找一个 content-length 头来确定长度。
如果在范围或内容长度标头中找到大小,则中止传输。如果未找到大小并且函数开始读取响应正文,则中止传输。
如果HEAD
请求导致405
方法不支持响应,这可能是一种补充方法。
/**
* Try to determine the size of a remote file by making an HTTP request for
* a byte range, or look for the content-length header in the response.
* The function aborts the transfer as soon as the size is found, or if no
* length headers are returned, it aborts the transfer.
*
* @return int|null null if size could not be determined, or length of content
*/
function getRemoteFileSize($url)
$ch = curl_init($url);
$headers = array(
'Range: bytes=0-1',
'Connection: close',
);
$in_headers = true;
$size = null;
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/46.0.2450.0 Iron/46.0.2450.0');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_VERBOSE, 0); // set to 1 to debug
curl_setopt($ch, CURLOPT_STDERR, fopen('php://output', 'r'));
curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($curl, $line) use (&$in_headers, &$size)
$length = strlen($line);
if (trim($line) == '')
$in_headers = false;
list($header, $content) = explode(':', $line, 2);
$header = strtolower(trim($header));
if ($header == 'content-range')
// found a content-range header
list($rng, $s) = explode('/', $content, 2);
$size = (int)$s;
return 0; // aborts transfer
else if ($header == 'content-length' && 206 != curl_getinfo($curl, CURLINFO_HTTP_CODE))
// found content-length header and this is not a 206 Partial Content response (range response)
$size = (int)$content;
return 0;
else
// continue
return $length;
);
curl_setopt($ch, CURLOPT_WRITEFUNCTION, function($curl, $data) use ($in_headers)
if (!$in_headers)
// shouldn't be here unless we couldn't determine file size
// abort transfer
return 0;
// write function is also called when reading headers
return strlen($data);
);
$result = curl_exec($ch);
$info = curl_getinfo($ch);
return $size;
用法:
$size = getRemoteFileSize('http://example.com/video.mp4');
if ($size === null)
echo "Could not determine file size from headers.";
else
echo "File size is $size bytes.";
【讨论】:
您的回答对我很有帮助。总是返回答案。即使Content-Length
不可用。
您好,感谢您的关注和评论。我真的很高兴你发现它有帮助!
这在我禁用 selinux 后对我有用。在远程图像、PDF 和 mp4 上进行了测试。 mp4 给出了结果,但“22”不是正确的文件大小。【参考方案12】:
这里的大多数答案要么使用 CURL,要么基于阅读标题。但在某些特定情况下,您可以使用更简单的解决方案。考虑filesize()
's docs on PHP.net 上的注释。你会发现一个提示:“从 PHP 5.0.0 开始,这个函数也可以与一些 URL 包装器一起使用。请参阅 Supported Protocols and Wrappers 以确定哪些包装器支持 stat() 系列功能”。
所以,如果您的服务器和 PHP 解析器配置正确,您可以简单地使用 filesize()
函数,将完整 URL 提供给它,指向您想要获取的远程文件,然后让 PHP 完成所有魔法.
【讨论】:
【参考方案13】:试试这个:我用了,效果不错。
function getRemoteFilesize($url)
$file_headers = @get_headers($url, 1);
if($size =getSize($file_headers))
return $size;
elseif($file_headers[0] == "HTTP/1.1 302 Found")
if (isset($file_headers["Location"]))
$url = $file_headers["Location"][0];
if (strpos($url, "/_as/") !== false)
$url = substr($url, 0, strpos($url, "/_as/"));
$file_headers = @get_headers($url, 1);
return getSize($file_headers);
return false;
function getSize($file_headers)
if (!$file_headers || $file_headers[0] == "HTTP/1.1 404 Not Found" || $file_headers[0] == "HTTP/1.0 404 Not Found")
return false;
elseif ($file_headers[0] == "HTTP/1.0 200 OK" || $file_headers[0] == "HTTP/1.1 200 OK")
$clen=(isset($file_headers['Content-Length']))?$file_headers['Content-Length']:false;
$size = $clen;
if($clen)
switch ($clen)
case $clen < 1024:
$size = $clen . ' B';
break;
case $clen < 1048576:
$size = round($clen / 1024, 2) . ' KiB';
break;
case $clen < 1073741824:
$size = round($clen / 1048576, 2) . ' MiB';
break;
case $clen < 1099511627776:
$size = round($clen / 1073741824, 2) . ' GiB';
break;
return $size;
return false;
现在,像这样测试:
echo getRemoteFilesize('http://mandasoy.com/wp-content/themes/spacious/images/plain.png').PHP_EOL;
echo getRemoteFilesize('http://bookfi.net/dl/201893/e96818').PHP_EOL;
echo getRemoteFilesize('https://***.com/questions/14679268/downloading-files-as-attachment-filesize-incorrect').PHP_EOL;
结果:
24.82 KB
912 KB
101.85 KB
【讨论】:
【参考方案14】:为了覆盖HTTP/2请求,这里提供的函数https://***.com/a/2602624/2380767需要稍作改动:
<?php
/**
* Returns the size of a file without downloading it, or -1 if the file
* size could not be determined.
*
* @param $url - The location of the remote file to download. Cannot
* be null or empty.
*
* @return The size of the file referenced by $url, or -1 if the size
* could not be determined.
*/
function curl_get_file_size( $url )
// Assume failure.
$result = -1;
$curl = curl_init( $url );
// Issue a HEAD request and follow any redirects.
curl_setopt( $curl, CURLOPT_NOBODY, true );
curl_setopt( $curl, CURLOPT_HEADER, true );
curl_setopt( $curl, CURLOPT_RETURNTRANSFER, true );
curl_setopt( $curl, CURLOPT_FOLLOWLOCATION, true );
curl_setopt( $curl, CURLOPT_USERAGENT, get_user_agent_string() );
$data = curl_exec( $curl );
curl_close( $curl );
if( $data )
$content_length = "unknown";
$status = "unknown";
if( preg_match( "/^HTTP\/1\.[01] (\d\d\d)/", $data, $matches ) )
$status = (int)$matches[1];
elseif( preg_match( "/^HTTP\/2 (\d\d\d)/", $data, $matches ) )
$status = (int)$matches[1];
if( preg_match( "/Content-Length: (\d+)/", $data, $matches ) )
$content_length = (int)$matches[1];
elseif( preg_match( "/content-length: (\d+)/", $data, $matches ) )
$content_length = (int)$matches[1];
// http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
if( $status == 200 || ($status > 300 && $status <= 308) )
$result = $content_length;
return $result;
?>
【讨论】:
【参考方案15】:如果你使用 laravel 7
use Illuminate\Support\Facades\Http;
Http::head($url)->header('Content-Length');
【讨论】:
以上是关于无需下载文件的远程文件大小的主要内容,如果未能解决你的问题,请参考以下文章