在通过HTTP下载数据之前确定文件大小

时间:2009-09-25 08:10:55

标签: php http curl

这可能吗?我目前正在使用cURL库在PHP中编码,但这相当适用于整个HTTP。

最明显的方式听起来像向数据URL抛出HEAD请求并读取其Content-Length标头,但问题是包括apache 2.0在内的某些服务器不会针对HEAD请求发送Content-Length,因为它不是强制性的,无法保证即使在GET请求中,所有服务器都会回复此类信息。

我正在使服务器下载由用户输入指定的网页并将其存储在服务器上,但我不想让它下载任何请求只是为了找到文件太大而不能在下载到所有内容后丢弃来自恶意请求的带宽。 所以我想知道实际传输数据之前内容的大小,并且可靠。

恶意网络服务器发送错误内容长度的案例和那些轻微的奇怪场合与我无关,如果它适用于所有其他一般情况。

到目前为止,我脑海中最糟糕的想法是实际上只是使用GET请求下载内容,如果超过传输过程中指定的大小限制就断开连接,但这听起来像是一个非常丑陋的解决方案作为HTTP。

有没有人有更好的想法?

2 个答案:

答案 0 :(得分:3)

不,服务器不必告诉您他们即将为您服务的资源的大小,因为他们可能不具备自己的知识。所以不,没有通用的方法,但是,无论何时提供,都可以尝试查找Content-length标题。

答案 1 :(得分:0)

我偶然发现了你的问题,寻找同样的答案。由于还没有真正的答案,我已经为自己破解了一个实现。当然,提到的所有注意事项仍然适用,是的,它确实使用了您的“丑陋”变体 - 但如果信息存在,它是实际获取数据的唯一方法。

/**
 * Returns the size reported by the server, for the given URL, in bytes.
 *
 * Note this information may not be accurate, or may even be plain wrong.
 *
 * Also note, the return value is explicitly NOT converted to an integer, as
 * the remote file might be bigger than 2^31, which may mess up the number if
 * you are on a 32bit machine.
 *
 * @throws        InvalidArgumentException on unknown URL scheme
 * @throws        Exception when unable to connect
 * @param         string $url
 * @returns       int
 */
function getURLDownloadSize($url) {
    $parts = parse_url($url);

    if(isset($parts['port'])) {
        $port = $parts['port'];
    }
    else {
        $port = 80;
    }
    if($parts['scheme'] != 'http') {
        throw new \InvalidArgumentException('Scheme not supported');
    }

    $sock = fsockopen($parts['host'], $port, $errno, $errstr, 3);
    if(!$sock) {
        throw new \Exception(
            sprintf(
                'Unable to connect to host: %s',
                $errstr
            )
        );
    }
    stream_set_timeout($sock, 5);

    fwrite($sock, sprintf("GET %s HTTP/1.1\r\n", $parts['path']));
    fwrite($sock, sprintf("Host: %s\r\n",        $parts['host']));
    fwrite($sock,         "Connection: close\r\n"              );
    fwrite($sock,         "\r\n"                               );

    $data = fread($sock, 1024*20);
    fclose($sock);

    $matchresult = array();
    if (preg_match('/Content-Length:\s+(\d+)/', $data, $matchresult)) {
        return $matchresult[1];
    }
    return 0;
}