From 7b70bc269794ea8cca19636e541d9486f044ea0d Mon Sep 17 00:00:00 2001 From: Zach Borboa Date: Mon, 14 Oct 2019 00:08:41 -0700 Subject: [PATCH] Fix #602: Unify download implementation of MultiCurl::addDownload with Curl::download --- src/Curl/Curl.php | 2 +- src/Curl/MultiCurl.php | 28 +++++++++++++++++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/Curl/Curl.php b/src/Curl/Curl.php index ffb7e6a406..837efde09e 100644 --- a/src/Curl/Curl.php +++ b/src/Curl/Curl.php @@ -39,7 +39,7 @@ class Curl public $errorCallback = null; public $completeCallback = null; public $fileHandle = null; - private $downloadFileName = null; + public $downloadFileName = null; public $attempts = 0; public $retries = 0; diff --git a/src/Curl/MultiCurl.php b/src/Curl/MultiCurl.php index 5deddb2a8a..7a3a754aed 100644 --- a/src/Curl/MultiCurl.php +++ b/src/Curl/MultiCurl.php @@ -85,15 +85,33 @@ public function addDownload($url, $mixed_filename) // Use tmpfile() or php://temp to avoid "Too many open files" error. if (is_callable($mixed_filename)) { - $callback = $mixed_filename; - $curl->downloadCompleteCallback = $callback; + $curl->downloadCompleteCallback = $mixed_filename; + $curl->downloadFileName = null; $curl->fileHandle = tmpfile(); } else { $filename = $mixed_filename; - $curl->downloadCompleteCallback = function ($instance, $fh) use ($filename) { - file_put_contents($filename, stream_get_contents($fh)); - }; + + // Use a temporary file when downloading. Not using a temporary file can cause an error when an existing + // file has already fully completed downloading and a new download is started with the same destination save + // path. The download request will include header "Range: bytes=$filesize-" which is syntactically valid, + // but unsatisfiable. + $download_filename = $filename . '.pccdownload'; + + $mode = 'wb'; + // Attempt to resume download only when a temporary download file exists and is not empty. + if (is_file($download_filename) && $filesize = filesize($download_filename)) { + $mode = 'ab'; + $first_byte_position = $filesize; + $range = $first_byte_position . '-'; + $curl->setOpt(CURLOPT_RANGE, $range); + } + $curl->downloadFileName = $download_filename; $curl->fileHandle = fopen('php://temp', 'wb'); + + // Move the downloaded temporary file to the destination save path. + $curl->downloadCompleteCallback = function ($instance, $fh) use ($download_filename) { + file_put_contents($download_filename, stream_get_contents($fh)); + }; } $curl->setOpt(CURLOPT_FILE, $curl->fileHandle);