curl_multi_exec:一些下载的图像丢失了一些数据/流不完整
Posted
技术标签:
【中文标题】curl_multi_exec:一些下载的图像丢失了一些数据/流不完整【英文标题】:curl_multi_exec: some images downloaded are missing some data / stream incomplete 【发布时间】:2018-12-23 11:48:02 【问题描述】:我已经实现了一个 php 函数,它使用 PHP curl_multi_init()
方法检查并下载大量图像 (> 1'000) - 使用数组传递给它。
在重做了几次之后,因为我得到了诸如 0 字节文件之类的东西。我现在有一个解决方案,可以下载所有图像 - 但下载的所有其他图像文件都不完整。
在我看来,好像我使用 file_put_contents()
“太早了”,意思是在使用 curl_multi_exec()
完全接收到某些图像数据之前。
不幸的是,我没有找到任何类似的问题,也没有针对我的案例找到任何 Google 搜索结果,我需要使用 curl_multi_exec,但不想使用 curl-opt-header“CURLOPT_FILE
”检索和保存图像。
希望有人能够帮助我了解我缺少什么以及为什么我会在本地保存一些损坏的图像。
以下是检索到的损坏图像的一些示例:
这是我传递给我的 Multi-CURL-Function 的示例数组:
$curl_httpresources = [
[ 'http://www.gravatar.com/avatar/example?d=mm&r=x&s=427'
,'/srv/www/data/images/1_unsplash.jpg' ],
[ 'http://www.gravatar.com/avatar/example?d=identicon&r=x&s=427'
,'/srv/www/data/images/2_unsplash.jpg' ],
[ 'http://www.gravatar.com/avatar/example?d=monsterid&r=x&s=427'
,'/srv/www/data/images/3_unsplash.jpg' ],
[ 'http://www.gravatar.com/avatar/example?d=wavatar&r=x&s=427'
,'/srv/www/data/images/4_unsplash.jpg' ],
[ 'http://www.gravatar.com/avatar/example?d=retro&r=x&s=427'
,'/srv/www/data/images/5_unsplash.jpg' ],
[ 'http://www.gravatar.com/avatar/example?d=mm&r=x&s=427'
,'/srv/www/data/images/6_unsplash.jpg' ],
[ 'http://www.gravatar.com/avatar/example?d=identicon&r=x&s=427'
,'/srv/www/data/images/7_unsplash.jpg' ],
[ 'http://www.gravatar.com/avatar/example?d=monsterid&r=x&s=427'
,'/srv/www/data/images/8_unsplash.jpg' ],
[ 'http://www.gravatar.com/avatar/example?d=wavatar&r=x&s=427'
,'/srv/www/data/images/9_unsplash.jpg' ],
[ 'http://www.gravatar.com/avatar/example?d=retro&r=x&s=427'
,'/srv/www/data/images/10_unsplash.jpg' ],
];
我的多 cURL PHP 函数
现在对于我当前正在使用的功能 - 除了一些部分下载的文件外,还有一种“有效”的功能 - 这是代码:
function cURLfetch(array $resources)
/** Disable PHP timelimit, because this could take a while... */
set_time_limit(0);
/** Validate the $resources Array (not empty, null, or alike) */
$resources_num = count($resources);
if ( empty($resources) || $resources_num <= 0 ) return false;
/** Callback-Function for writing data to file */
$callback = function($resource, $filepath)
file_put_contents($filepath, $resource);
/** For Debug only: output <img>-Tag with saved $resource */
printf('<img src="%s"><br>', str_replace('/srv/www', '', $filepath));
;
/**
* Initialize CURL process for handling multiple parallel requests
*/
$curl_instance = curl_multi_init();
$curl_multi_exec_active = null;
$curl_request_options = [
CURLOPT_USERAGENT => 'PHP-Script/1.0 (+https://website.com/)',
CURLOPT_TIMEOUT => 10,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_VERBOSE => false,
CURLOPT_RETURNTRANSFER => true,
];
/**
* Looping through all $resources
* $resources[$i][0] = HTTP resource
* $resources[$i][1] = Target Filepath
*/
for ($i = 0; $i < $resources_num; $i++)
$curl_requests[$i] = curl_init($resources[$i][0]);
curl_setopt_array($curl_requests[$i], $curl_request_options);
curl_multi_add_handle($curl_instance, $curl_requests[$i]);
do
try
$curl_execute = curl_multi_exec($curl_instance, $curl_multi_exec_active);
catch (Exception $e)
error_log($e->getMessage());
while ($curl_execute == CURLM_CALL_MULTI_PERFORM);
/** Wait until data arrives on all sockets */
$h = 0; // initialise a counter
while ($curl_multi_exec_active && $curl_execute == CURLM_OK)
if (curl_multi_select($curl_instance) != -1)
do
$curl_data = curl_multi_exec($curl_instance, $curl_multi_exec_active);
$curl_done = curl_multi_info_read($curl_instance);
/** Check if there is data... */
if ($curl_done['handle'] !== NULL)
/** Continue ONLY if HTTP statuscode was OK (200) */
$info = curl_getinfo($curl_done['handle']);
if ($info['http_code'] == 200)
if (!empty(curl_multi_getcontent($curl_requests[$h])))
/** Curl request successful. Process data using the callback function. */
$callback(curl_multi_getcontent($curl_requests[$h]), $resources[$h][1]);
$h++; // count up
while ($curl_data == CURLM_CALL_MULTI_PERFORM);
/** Close all $curl_requests */
foreach($curl_requests as $request)
curl_multi_remove_handle($curl_instance, $request);
curl_multi_close($curl_instance);
return true;
/** Start fetching images from an Array */
cURLfetch($curl_httpresources);
非常感谢您的帮助,非常感谢!
【问题讨论】:
只是一个猜测 - 您可能对服务器的打击太大,因此有时它会断开连接(不会发出错误)。循环和同时下载不超过 10 个图像。 (然后尝试增加...) 感谢@Paolo 的猜测。有趣的事情 - 让我觉得反对这个理论 - 是,通常第一张图像被影响为不完整,并且它已经发生,同时只请求总共 10 张图像(参见初始问题中的示例数组)...... 【参考方案1】:我最终只在经典循环中使用常规 cURL 请求,查询所有 >1'000 个图像并下载带有“HTTP 200 OK”响应的图像。我最初担心服务器可能会由于潜在的错误识别 DDoS 而切断连接没有效果,为什么这种方法对我的情况很有效。
这是我正在使用的常规 cURL 请求的最终函数:
function cURLfetchUrl($url, $save_as_file)
/** Validate $url & $save_as_file (not empty, null, or alike) */
if ( empty($url) || is_numeric($url) ) return false;
if ( empty($save_as_file) || is_numeric($save_as_file) ) return false;
/** Disable PHP timelimit, because this could take a while... */
set_time_limit(0);
try
/**
* Set cURL options to be passed to a single request
*/
$curl_request_options = [
CURLOPT_USERAGENT => 'PHP-Script/1.0 (+https://website.com/)',
CURLOPT_TIMEOUT => 5,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_RETURNTRANSFER => true,
];
/** Initialize & execute cURL-Request */
$curl_instance = curl_init($url);
curl_setopt_array($curl_instance, $curl_request_options);
$curl_data = curl_exec($curl_instance);
$curl_done = curl_getinfo($curl_instance);
/** cURL request successful */
if ($curl_done['http_code'] == 200)
/** Open a new file handle, write the file & close the file handle */
if (file_put_contents($save_as_file, $curl_data) !== false)
// logging if file_put_contents was OK
else
// logging if file_put_contents FAILED
/** Close the $curl_instance */
curl_close($curl_instance);
return true;
catch (Exception $e)
error_log($e->getMessage());
return false;
并执行它:
$curl_httpresources = [
[ 'http://www.gravatar.com/avatar/example?d=mm&r=x&s=427'
,'/srv/www/data/images/1_unsplash.jpg' ],
[ 'http://www.gravatar.com/avatar/example?d=identicon&r=x&s=427'
,'/srv/www/data/images/2_unsplash.jpg' ],
[ 'http://www.gravatar.com/avatar/example?d=monsterid&r=x&s=427'
,'/srv/www/data/images/3_unsplash.jpg' ],
[ 'http://www.gravatar.com/avatar/example?d=wavatar&r=x&s=427'
,'/srv/www/data/images/4_unsplash.jpg' ],
[ 'http://www.gravatar.com/avatar/example?d=retro&r=x&s=427'
,'/srv/www/data/images/5_unsplash.jpg' ],
[ 'http://www.gravatar.com/avatar/example?d=mm&r=x&s=427'
,'/srv/www/data/images/6_unsplash.jpg' ],
[ 'http://www.gravatar.com/avatar/example?d=identicon&r=x&s=427'
,'/srv/www/data/images/7_unsplash.jpg' ],
[ 'http://www.gravatar.com/avatar/example?d=monsterid&r=x&s=427'
,'/srv/www/data/images/8_unsplash.jpg' ],
[ 'http://www.gravatar.com/avatar/example?d=wavatar&r=x&s=427'
,'/srv/www/data/images/9_unsplash.jpg' ],
[ 'http://www.gravatar.com/avatar/example?d=retro&r=x&s=427'
,'/srv/www/data/images/10_unsplash.jpg' ],
];
/** cURL all request from the $curl_httpresources Array */
if (count($curl_httpresources) > 0)
foreach ($curl_httpresources as $resource)
cURLfetchUrl($resource[0], $resource[1]);
不过,如果有人知道如何使用 curl_multi 正确检索文件数据流,那就太好了,因为我对最初问题的回答只是显示了一种不同的方法——而不是解决最初的方法。
【讨论】:
以上是关于curl_multi_exec:一些下载的图像丢失了一些数据/流不完整的主要内容,如果未能解决你的问题,请参考以下文章
应用重新运行后,通过 WriteToFile 命令写入的图像丢失