Content-length 在 Curl、Wget 中可用,但在 Python 请求中不可用

Content-length available in Curl, Wget, but not in Python Requests

我有一个 URL 指向一个二进制文件,我需要在检查其大小后下载该文件,因为只有在本地文件大小与远程文件大小不同时才应(重新)执行下载.

这是它与 wget(匿名主机名和 IP)一起工作的方式:

$ wget <URL>
--2020-02-17 11:09:18--  <URL>
Resolving <URL> (<host>)... <IP>
Connecting to <host> (<host>)|<ip>|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 31581872 (30M) [application/x-gzip]
Saving to: ‘[...]’

这也适用于 --continue 标志以恢复下载,包括如果文件之前已完全下载则跳过。

我可以用 curl 做同样的事情,content-length 也存在:

$ curl -I <url>
HTTP/2 200 
date: Mon, 17 Feb 2020 13:11:55 GMT
server: Apache/2.4.25 (Debian)
strict-transport-security: max-age=15768000
last-modified: Fri, 14 Feb 2020 15:42:29 GMT
etag: "[...]"
accept-ranges: bytes
content-length: 31581872
vary: Accept-Encoding
content-type: application/x-gzip

在 Python 中,我尝试通过使用 requests library:

检查 Content-length header 来实现相同的逻辑
        with requests.get(url, stream=True) as response:
            total_size = int(response.headers.get("Content-length"))

            if not response.ok:
                logger.error(
                    f"Error {response.status_code} when downloading file from {url}"
                )
            elif os.path.exists(file) and os.stat(file).st_size == total_size:
                logger.info(f"File '{file}' already exists, skipping download.")
            else:
                [...] # download file

事实证明 Content-length header 从未出现过,即在这里得到一个 None 值。我知道这应该通过将默认值传递给 get() 调用来解决,但是为了调试的目的,这个示例因此触发了一个异常:

TypeError: int() argument must be a string, a bytes-like object or a number, not 'NoneType' 

我可以手动确认 Content-length header 不存在:

requests.get(url, stream=True).headers
{'Date': '[...]', 'Server': '[...]', 'Strict-Transport-Security': '[...]', 'Upgrade': '[...]', 'Connection': 'Upgrade, Keep-Alive', 'Last-Modified': '[...]', 'ETag': ''[...]'', 'Accept-Ranges': 'bytes', 'Vary': 'Accept-Encoding', 'Content-Encoding': 'gzip', 'Keep-Alive': 'timeout=15, max=100', 'Transfer-Encoding': 'chunked', 'Content-Type': 'application/x-gzip'}

这个逻辑对于其他 URL 来说工作正常,即我确实得到了 Content-length header.

当使用 requests.head(url)(省略 stream=True)时,我得到相同的 headers 除了 Transfer-Encoding

我了解服务器不必发送 Content-length header。 然而,wgetcurl 显然确实得到了 header。它们与我的 Python 实施有何不同?

并不是关于缺失 Content-length header 问题的真正答案,而是对潜在问题的解决方案:

我没有检查本地文件大小与远程内容长度的关系,而是检查了 Last-modified header 并将其与本地文件的 mtime 进行了比较.这在(不太可能)更新远程文件但仍具有完全相同大小的情况下也更安全。