PHP – Безопасный способ загрузки больших файлов?

Информация

Существует множество способов загрузки файлов в PHP, file_get_contents + file_put_contents , fopen , readfile и cURL.

Вопрос?

  • При наличии большого файла, скажем, 500 МБ с другого сервера / домена, что такое «правильный» способ загрузить его в безопасное место? Если соединение не удается, он должен найти позицию и продолжить ИЛИ загрузить файл снова, если он содержит ошибки.
  • Он будет использоваться на веб-сайте, а не в оболочке php.exe.

Что я догадался до сих пор

  • Я читал о решениях AJAX с индикаторами выполнения, но то, что я действительно ищу, – это PHP-решение.
  • Мне не нужно буферизовать файл в строку, например file_get_contents. Это, вероятно, также использует память.
  • Я также читал о проблемах памяти. Может быть предпочтительным решение, которое не использует большую память.

концепция

Это то, что я хочу, если результат ложный.

function download_url( $url, $filename ) { // Code $success['success'] = false; $success['message'] = 'File not found'; return $success; } 

Самый простой способ скопировать большие файлы можно продемонстрировать здесь. Сохраните большие файлы с php stdin, но не показывает, как копировать файлы с помощью диапазона http

 $url = "http://REMOTE_FILE"; $local = __DIR__ . "/test.dat"; try { $download = new Downloader($url); $download->start($local); // Start Download Process } catch (Exception $e) { printf("Copied %d bytes\n", $pos = $download->getPos()); } 

Когда происходит Исключение, вы можете возобновить загрузку файла для последней точки

 $download->setPos($pos); 

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

 class Downloader { private $url; private $length = 8192; private $pos = 0; private $timeout = 60; public function __construct($url) { $this->url = $url; } public function setLength($length) { $this->length = $length; } public function setTimeout($timeout) { $this->timeout = $timeout; } public function setPos($pos) { $this->pos = $pos; } public function getPos() { return $this->pos; } public function start($local) { $part = $this->getPart("0-1"); // Check partial Support if ($part && strlen($part) === 2) { // Split data with curl $this->runPartial($local); } else { // Use stream copy $this->runNormal($local); } } private function runNormal($local) { $in = fopen($this->url, "r"); $out = fopen($local, 'w'); $pos = ftell($in); while(($pos = ftell($in)) <= $this->pos) { $n = ($pos + $this->length) > $this->length ? $this->length : $this->pos; fread($in, $n); } $this->pos = stream_copy_to_stream($in, $out); return $this->pos; } private function runPartial($local) { $i = $this->pos; $fp = fopen($local, 'w'); fseek($fp, $this->pos); while(true) { $data = $this->getPart(sprintf("%d-%d", $i, ($i + $this->length))); $i += strlen($data); fwrite($fp, $data); $this->pos = $i; if ($data === - 1) throw new Exception("File Corupted"); if (! $data) break; } fclose($fp); } private function getPart($range) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $this->url); curl_setopt($ch, CURLOPT_RANGE, $range); curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout); $result = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); // Request not Satisfiable if ($code == 416) return false; // Check 206 Partial Content if ($code != 206) return - 1; return $result; } } 

Вы хотите загрузить удаленный файл в куски. Этот ответ имеет отличный пример:

Как загрузить большой файл с использованием PHP (использование низкой памяти)