在 PHP 中基于 HTTP 协议检测文件的大小(即不下载文件的情况下,获取远程文件的大小),使用文件系统函数 fopen — 打开文件或者 URL

1、在实现渠道发布的过程中,存在待上传至渠道的文件过大的情况,进而导致上传失败。比如说抖音,暂时限制为 128 MB。如图1

图1

2、但是现阶段的实现逻辑是,先基于文件的 HTTP (absolute_url:来源的资源文件的绝对URL) 下载文件后,得到文件的 relative_path:渠道发布的资源文件的相对路径,然后才能够获取到文件的大小。而这个实现阶段是异步的。如图2

图2

3、现在希望优化用户的体验,能够在调用接口时,基于 HTTP 协议检测文件的大小(即不下载文件的情况下,获取文件的大小)。

4、参考 文件系统函数 filesize — 取得文件大小:https://www.php.net/manual/zh/function.filesize.php 。The simplest and most efficient implemention for getting remote filesize。获取远程文件大小的最简单和最有效的实现。如图3

图3

5、执行 fopen() 时可以使用变量 $http_response_header。 其中包含响应标头的数组。打印变量 $http_response_header。如图4

图4

<?php
function remote_filesize($url) {
    static $regex = '/^Content-Length: *+\K\d++$/im';
 // 将 $url 指定的名字资源绑定到一个流上,只读方式打开,将文件指针指向文件头
    if (!$fp = @fopen($url, 'rb')) {
        return false;
    }
 // 执行 fopen() 时可以使用变量 $http_response_header。 其中包含响应标头的数组。
    if (
        isset($http_response_header) &&
        preg_match($regex, implode("\n", $http_response_header), $matches)
    ) {
  print_r($http_response_header);
        return (int)$matches[0];
    }
 echo 1;
    return strlen(stream_get_contents($fp));
}

$startTime = time();
$url = 'https://www.shuijingwanwq.com/wp-content/uploads/2021/07/秦时明月第一集荧惑守心.mp4';
echo remote_filesize($url) . "\n";
$endTime = time();
echo '耗费时间:' . ($endTime - $startTime) . '秒';
?>

Array
(
    [0] => HTTP/1.1 200 OK
    [1] => Server: nginx
    [2] => Date: Mon, 09 Aug 2021 05:46:41 GMT
    [3] => Content-Type: video/mp4
    [4] => Content-Length: 143162935
    [5] => Last-Modified: Tue, 30 Oct 2018 09:52:18 GMT
    [6] => Connection: close
    [7] => ETag: "5bd829d2-8887e37"
    [8] => Expires: Wed, 08 Sep 2021 05:46:41 GMT
    [9] => Cache-Control: max-age=2592000
    [10] => Strict-Transport-Security: max-age=15768000
    [11] => Accept-Ranges: bytes
)
143162935
耗费时间:0秒

6、如果变量 $http_response_header 不存在,则 使用函数 stream_get_contents — 读取资源流到一个字符串。文件大小:1.27 MB,测试一下耗费时间:2秒,发现无法接受。决定当变量 $http_response_header 不存在时,返回 false。如图5

图5

<?php
function remote_filesize($url) {
    static $regex = '/^Content-Length: *+\K\d++$/im';
 // 将 $url 指定的名字资源绑定到一个流上,只读方式打开,将文件指针指向文件头
    if (!$fp = @fopen($url, 'rb')) {
        return false;
    }
 // 执行 fopen() 时可以使用变量 $http_response_header。 其中包含响应标头的数组。
 /*
    if (
        isset($http_response_header) &&
        preg_match($regex, implode("\n", $http_response_header), $matches)
    ) {
  print_r($http_response_header);
        return (int)$matches[0];
    }
 */ echo 1;
    return strlen(stream_get_contents($fp));
}

$startTime = time();
$url = 'https://www.shuijingwanwq.com/wp-content/uploads/2021/07/兔子、牛、还有一只什么哟?认不出来了.mp4';
echo remote_filesize($url) . "\n";
$endTime = time();
echo '耗费时间:' . ($endTime - $startTime) . '秒';
?>

1334721
耗费时间:2秒

7、最终代码实现如下。如图6

图6

<?php
function remote_filesize($url) {
    static $regex = '/^Content-Length: *+\K\d++$/im';
 // 将 $url 指定的名字资源绑定到一个流上,只读方式打开,将文件指针指向文件头
    if (!$fp = @fopen($url, 'rb')) {
        return 0;
    }
 // 执行 fopen() 时可以使用变量 $http_response_header。其中包含响应标头的数组。
    if (
        isset($http_response_header) &&
        preg_match($regex, implode("\n", $http_response_header), $matches)
    ) {
  print_r($http_response_header);
        return (int)$matches[0];
    }
 return 0;
}

$startTime = time();
$url = 'https://www.shuijingwanwq.com/wp-content/uploads/2021/07/ChinaJoy 2015 上海 梦幻西游.mp4';
echo remote_filesize($url) . "\n";
$endTime = time();
echo '耗费时间:' . ($endTime - $startTime) . '秒';
?>

8、在调用接口时,基于 HTTP 协议检测文件的大小(即不下载文件的情况下,获取文件的大小),最终实现预期目标。如图7

图7

永夜

View Comments