没有不值得去解决的问题,也没有不值得去学习的技术!

Detect Remote File Size in PHP Over HTTP Without Downloading It Using fopen

The final code implementation is as follows

1. During the implementation of channel publishing, files to be uploaded to a channel may be too large, resulting in upload failures. For example, Douyin currently limits file size to 128 MB. See Figure 1.

During the implementation of channel publishing, files to be uploaded to a channel may be too large, resulting in upload failures. For example, Douyin currently limits file size to 128 MB
Figure 1

2. However, the current implementation logic first downloads the file over HTTP (absolute_url: the absolute URL of the source resource file) to obtain the file’s relative_path (the relative path of the resource file for channel publishing), and only then can it retrieve the file size. This implementation stage is asynchronous. See Figure 2.

However, the current implementation logic first downloads the file over HTTP (absolute_url: the absolute URL of the source resource file) to obtain the file's relative_path (the relative path of the resource file for channel publishing), and only then can it retrieve the file size. This implementation stage is asynchronous.
Figure 2

3. We now want to optimize the user experience by detecting the file size based on the HTTP protocol when calling the API (i.e., obtaining the file size without downloading the file).

4. Refer to the filesystem function filesize — Gets file size: https://www.php.net/manual/zh/function.filesize.php . The simplest and most efficient implemention for getting remote filesize. See Figure 3.

Refer to the filesystem function filesize — Gets file size: https://www.php.net/manual/zh/function.filesize.php . The simplest and most efficient implemention for getting remote filesize.
Figure 3

5. When executing fopen(), you can use the variable $http_response_header. It contains an array of response headers. Print the variable $http_response_header. See Figure 4.

When executing fopen(), you can use the variable $http_response_header. It contains an array of response headers. Print the variable $http_response_header
Figure 4
PHP
<?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://media.shuijingwanwq.com/2021/07/秦时明月第一集荧惑守心.mp4';
echo remote_filesize($url) . "\n";
$endTime = time();
echo '耗费时间:' . ($endTime - $startTime) . '秒';
?>
Plaintext
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
Time consumed: 0 seconds

6. If the variable $http_response_header does not exist, use the function stream_get_contents — Reads the remainder of a stream into a string. File size: 1.27 MB. Testing the time consumed: 2 seconds, which is unacceptable. Decided to return false when the variable $http_response_header does not exist. See Figure 5.

If the variable $http_response_header does not exist, use the function stream_get_contents — Reads the remainder of a stream into a string. File size: 1.27 MB. Testing the time consumed: 2 seconds, which is unacceptable. Decided to return false when the variable $http_response_header does not exist
Figure 5
PHP
<?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://media.shuijingwanwq.com/2021/07/兔子、牛、还有一只什么哟?认不出来了.mp4';
echo remote_filesize($url) . "\n";
$endTime = time();
echo '耗费时间:' . ($endTime - $startTime) . '秒';
?>
Plaintext
1334721
Time consumed: 2 seconds

7. The final code implementation is as follows. See Figure 6.

The final code implementation is as follows
Figure 6
PHP
<?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://media.shuijingwanwq.com/2021/07/ChinaJoy 2015 上海 梦幻西游.mp4';
echo remote_filesize($url) . "\n";
$endTime = time();
echo '耗费时间:' . ($endTime - $startTime) . '秒';
?>
 

8. When calling the API, detecting the file size based on the HTTP protocol (i.e., obtaining the file size without downloading the file) ultimately achieves the expected goal. See Figure 7.

When calling the API, detecting the file size based on the HTTP protocol (i.e., obtaining the file size without downloading the file) ultimately achieves the expected goal
Figure 7

关于作者 · 持续学习与技术实践

我是一名拥有 15 年以上开发经验的技术从业者,自 2013 年起持续运营个人技术博客,记录工作与个人项目中遇到的真实问题,以及解决问题过程中的探索、实践和思考。

除了技术写作,我也在持续开发和维护自己的独立项目,探索新技术和新工具在实际场景中的应用。我始终相信,没有不值得去解决的问题,也没有不值得去学习的技术。

如果这篇文章对你有所帮助,欢迎浏览博客中的其他文章,也欢迎交流技术经验与想法。

关于我  ·  GitHub  ·  邮件联系