Длина содержимого доступна в Curl, Wget, но не в Python Запросы - PullRequest
0 голосов
/ 17 февраля 2020

У меня есть 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 я пытаюсь реализовать те же логи c, проверяя заголовок Content-length с использованием библиотеки запросов :

        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 никогда не присутствует, то есть получает здесь значение None , Я знаю, что это можно обойти, передав значение по умолчанию в вызов get(), но в целях отладки этот пример, следовательно, вызывает исключение:

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

Я могу подтвердить вручную, что * Заголовок 1027 * отсутствует:

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'}

Этот лог c отлично работает, хотя и для других URL, т.е. я получаю заголовок Content-length.

При использовании requests.head(url) ( опуская stream=True), я получаю те же заголовки, за исключением Transfer-Encoding.

Я понимаю, что серверу не нужно отправлять заголовок Content-length. Однако, wget и curl явно получают этот заголовок. Чем они отличаются от моей Python реализации?

1 Ответ

0 голосов
/ 18 февраля 2020

Не совсем ответ на вопрос об отсутствующем заголовке Content-length, но решение основной проблемы:

Вместо проверки размера локального файла в зависимости от длины содержимого удаленного, я закончил проверьте заголовок Last-modified и сравните его с mtime локального файла. Это также более безопасно в (маловероятном) случае, когда удаленный файл обновляется, но при этом имеет точно такой же размер.

...