PHP cURL неправильная длина содержимого, предоставленная источником

Загрузка изображения с помощью cURL

https://cdni.rt.com/deutsch/images/2018.04/article/5ac34e500d0403503d8b4568.jpg

при сохранении этого изображения вручную из браузера на локальный компьютер размер, отображаемый системой, составляет 139 880 байт.

При загрузке с помощью cURL файл кажется поврежденным и не считается допустимым изображением.

его размер при загрузке с использованием cURL составляет 139 845, что меньше размера при загрузке вручную

углубляясь в проблему, обнаружил, что сервер возвращает длину содержимого в заголовках ответа как

content-length: 139845

Эта длина идентична загруженной cURL, поэтому я подозревал, что cURL закрывает передачу после достижения сервером предполагаемой (возможно, неправильной) длины.

Есть ли способ заставить cURL полностью загрузить файл, даже если заголовок длины содержимого неверен

Используемый код:

//curl ini
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER,0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_TIMEOUT,20);
curl_setopt($ch, CURLOPT_REFERER, 'http://www.bing.com/');
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.8) Gecko/2009032609 Firefox/3.0.8');
curl_setopt($ch, CURLOPT_MAXREDIRS, 5); // Good leeway for redirections.
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // Many login forms redirect at least once.
curl_setopt($ch, CURLOPT_COOKIEJAR , "cookie.txt");

//curl get
$x='error';
$url='https://cdni.rt.com/deutsch/images/2018.04/article/5ac34e500d0403503d8b4568.jpg';
curl_setopt($ch, CURLOPT_HTTPGET, 1);
curl_setopt($ch, CURLOPT_URL, trim($url));
$exec=curl_exec($ch);
$x=curl_error($ch);

$fp = fopen('test.jpg','x');
fwrite($fp, $exec);
fclose($fp);

person Atef    schedule 03.04.2018    source источник
comment
Что такое strlen() полученных данных? Подумайте также об использовании флага b при открытии выходного файла!   -  person Ulrich Eckhardt    schedule 03.04.2018
comment
strlen возвращает 139845   -  person Atef    schedule 03.04.2018
comment
Я пробовал с curl и wget из командной строки (передавая плоскость, чтобы игнорировать заголовок длины содержимого), и оба приводят к файлу длиной 139 845 байт. Я начинаю задаваться вопросом, является ли проблема сжатием gzip.   -  person Sean Bright    schedule 03.04.2018
comment
@SeanBright действительно так. используйте флаг --compressed с curl, и это сработает.   -  person hanshenrik    schedule 04.04.2018


Ответы (2)


на сервере есть ошибка реализации Accept-Encoding механизма сжатой передачи. ответ ВСЕГДА сжат gzip, но не сообщит клиенту, что он сжат gzip, если у клиента нет заголовка Accept-Encoding: gzip в запросе. когда сервер не сообщает клиенту, что он сжат с помощью gzip, клиент не будет распаковывать его с помощью gzip перед сохранением, поэтому ваша загрузка повреждена. скажите curl предложить сжатие gzip, установив CURLOPT_ENCODING,

curl_setopt($ch,CURLOPT_ENCODING,'gzip');

, тогда сервер сообщит curl, что он сжат с помощью gzip, и curl распаковает его для вас, прежде чем передать его PHP.

вам, вероятно, следует сообщить об этом администратору сервера, это серьезная ошибка на его веб-сервере, приводящая к повреждению загрузок.

person hanshenrik    schedule 04.04.2018

libcurl имеет опцию для этого, которая называется CURLOPT_IGNORE_CONTENT_LENGTH, к сожалению, она изначально не поддерживается в php, но вы все равно можете обмануть php, чтобы установить эту опцию, используя правильное магическое число (которое, по крайней мере, в моей системе 136),

if(!defined('CURLOPT_IGNORE_CONTENT_LENGTH')){
    define('CURLOPT_IGNORE_CONTENT_LENGTH',136);
}
if(!curl_setopt($ch,CURLOPT_IGNORE_CONTENT_LENGTH,1)){
    throw new \RuntimeException('failed to set CURLOPT_IGNORE_CONTENT_LENGTH! - '.curl_errno($ch).': '.curl_error($ch));
}

вы можете найти правильный номер для вашей системы, скомпилировав и запустив следующий код C++:

#include <iostream>
#include <curl/curl.h>
int main(){
std::cout << CURLOPT_IGNORE_CONTENT_LENGTH << std::endl;
}
  • но это, вероятно, 136. наконец, protip, file_get_contents полностью игнорирует заголовок длины содержимого и просто продолжает загрузку, пока сервер не закроет соединение (что потенциально намного медленнее, чем curl) - также вам, вероятно, следует связаться с оператором сервера и позволить ему знаете, что-то не так с его сервером.
person hanshenrik    schedule 03.04.2018
comment
Спасибо за ответ на моем локальном компьютере CURLOPT_IGNORE_CONTENT_LENGTH определен, а curl_setopt($ch,CURLOPT_IGNORE_CONTENT_LENGTH,1) возвращает true, но изображение по-прежнему загружается неправильно - person Atef; 04.04.2018
comment
file_get_contents также возвращает тот же результат - person Atef; 04.04.2018