mirror of
https://github.com/php-curl-class/php-curl-class.git
synced 2026-08-31 04:34:27 +00:00
Merge pull request #783 from zachborboa/master
Graduate Curl::fastDownload()
This commit is contained in:
@@ -199,7 +199,6 @@ More examples are available under [/examples](https://github.com/php-curl-class/
|
||||
Curl::__construct($base_url = null, $options = [])
|
||||
Curl::__destruct()
|
||||
Curl::__get($name)
|
||||
Curl::_fastDownload($url, $filename, $connections = 4)
|
||||
Curl::attemptRetry()
|
||||
Curl::beforeSend($callback)
|
||||
Curl::buildPostData($data)
|
||||
@@ -213,6 +212,7 @@ Curl::download($url, $mixed_filename)
|
||||
Curl::error($callback)
|
||||
Curl::exec($ch = null)
|
||||
Curl::execDone()
|
||||
Curl::fastDownload($url, $filename, $connections = 4)
|
||||
Curl::get($url, $data = [])
|
||||
Curl::getAttempts()
|
||||
Curl::getBeforeSendCallback()
|
||||
|
||||
+40
-34
@@ -357,17 +357,17 @@ class Curl extends BaseCurl
|
||||
/**
|
||||
* Fast download
|
||||
*
|
||||
* @access private
|
||||
* @access public
|
||||
* @param $url
|
||||
* @param $filename
|
||||
* @param $connections
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function _fastDownload($url, $filename, $connections = 4)
|
||||
public function fastDownload($url, $filename, $connections = 4)
|
||||
{
|
||||
// First we need to retrive the 'Content-Length' header.
|
||||
// Use GET because not all hosts support HEAD requests.
|
||||
// Retrieve content length from the "Content-Length" header and use an
|
||||
// HTTP GET request because not all hosts support HEAD requests.
|
||||
$this->setOpts([
|
||||
CURLOPT_CUSTOMREQUEST => 'GET',
|
||||
CURLOPT_NOBODY => true,
|
||||
@@ -380,24 +380,23 @@ class Curl extends BaseCurl
|
||||
$content_length = isset($this->responseHeaders['Content-Length']) ?
|
||||
$this->responseHeaders['Content-Length'] : null;
|
||||
|
||||
// If content length header is missing, use the normal download.
|
||||
// Use a regular download when content length could not be determined.
|
||||
if (!$content_length) {
|
||||
return $this->download($url, $filename);
|
||||
}
|
||||
|
||||
// Try to divide chunk_size equally.
|
||||
$chunkSize = ceil($content_length / $connections);
|
||||
// Divide chunk_size across the number of connections.
|
||||
$chunk_size = ceil($content_length / $connections);
|
||||
|
||||
// First bytes.
|
||||
$offset = 0;
|
||||
$nextChunk = $chunkSize;
|
||||
$next_chunk = $chunk_size;
|
||||
|
||||
// We need this later.
|
||||
$file_parts = [];
|
||||
// Keep track of file name parts.
|
||||
$part_file_names = [];
|
||||
|
||||
$multi_curl = new MultiCurl();
|
||||
$multi_curl->setConcurrency($connections);
|
||||
|
||||
$multi_curl->error(function ($instance) {
|
||||
return false;
|
||||
});
|
||||
@@ -405,52 +404,59 @@ class Curl extends BaseCurl
|
||||
for ($i = 1; $i <= $connections; $i++) {
|
||||
// If last chunk then no need to supply it.
|
||||
// Range starts with 0, so subtract 1.
|
||||
$nextChunk = $i === $connections ? '' : $nextChunk - 1;
|
||||
$next_chunk = $i === $connections ? '' : $next_chunk - 1;
|
||||
|
||||
// Create part file.
|
||||
$fpath = "$filename.part$i";
|
||||
if (is_file($fpath)) {
|
||||
unlink($fpath);
|
||||
$part_file_name = $filename . '.part' . $i;
|
||||
|
||||
// Save the file name of this part.
|
||||
$part_file_names[] = $part_file_name;
|
||||
|
||||
// Remove any existing file part.
|
||||
if (is_file($part_file_name)) {
|
||||
unlink($part_file_name);
|
||||
}
|
||||
$fp = fopen($fpath, 'w');
|
||||
|
||||
// Track all fileparts names; we need this later.
|
||||
$file_parts[] = $fpath;
|
||||
// Create file part.
|
||||
$file_handle = fopen($part_file_name, 'w');
|
||||
|
||||
$curl = new Curl();
|
||||
$curl->setOpt(CURLOPT_ENCODING, '');
|
||||
$curl->setRange("$offset-$nextChunk");
|
||||
$curl->setFile($fp);
|
||||
$curl->setRange($offset . '-' . $next_chunk);
|
||||
$curl->setFile($file_handle);
|
||||
$curl->disableTimeout(); // otherwise download may fail.
|
||||
$curl->setUrl($url);
|
||||
|
||||
$curl->complete(function () use ($fp) {
|
||||
fclose($fp);
|
||||
$curl->complete(function () use ($file_handle) {
|
||||
fclose($file_handle);
|
||||
});
|
||||
|
||||
$multi_curl->addCurl($curl);
|
||||
|
||||
if ($i !== $connections) {
|
||||
$offset = $nextChunk + 1; // Add 1 to match offset.
|
||||
$nextChunk = $nextChunk + $chunkSize;
|
||||
$offset = $next_chunk + 1; // Add 1 to match offset.
|
||||
$next_chunk = $next_chunk + $chunk_size;
|
||||
}
|
||||
}
|
||||
|
||||
// let the magic begin.
|
||||
// Start the simultaneous downloads for each of the ranges in parallel.
|
||||
$multi_curl->start();
|
||||
|
||||
// Concatenate chunks to single.
|
||||
// Remove existing download file name at destination.
|
||||
if (is_file($filename)) {
|
||||
unlink($filename);
|
||||
}
|
||||
$mainfp = fopen($filename, 'w');
|
||||
foreach ($file_parts as $part) {
|
||||
$fp = fopen($part, 'r');
|
||||
stream_copy_to_stream($fp, $mainfp);
|
||||
fclose($fp);
|
||||
unlink($part);
|
||||
|
||||
// Combine downloaded chunks into a single file.
|
||||
$main_file_handle = fopen($filename, 'w');
|
||||
|
||||
foreach ($part_file_names as $part_file_name) {
|
||||
$file_handle = fopen($part_file_name, 'r');
|
||||
stream_copy_to_stream($file_handle, $main_file_handle);
|
||||
fclose($file_handle);
|
||||
unlink($part_file_name);
|
||||
}
|
||||
fclose($mainfp);
|
||||
|
||||
fclose($main_file_handle);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -13,9 +13,6 @@
|
||||
|
||||
<!-- TODO: Remove the following exclusion when ArrayUtil functions not in camel caps format like ArrayUtil::is_array_assoc() are removed. -->
|
||||
<exclude name="PSR1.Methods.CamelCapsMethodName.NotCamelCaps" />
|
||||
|
||||
<!-- TODO: Remove the following exclusion when Curl::_fastDownload() is renamed to Curl::fastDownload(). -->
|
||||
<exclude name="PSR2.Methods.MethodDeclaration.Underscore" />
|
||||
</rule>
|
||||
|
||||
<!-- Disable "No PHP code was found in this file" for symlinks. -->
|
||||
|
||||
Reference in New Issue
Block a user