Created
December 7, 2017 20:40
-
-
Save jszobody/e10feecb4b8b7ceae2588850ee75ff47 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Services; | |
use App\Models\ZipFile; | |
use Illuminate\Support\Collection; | |
use ZipStream\Exception\FileNotFoundException; | |
use ZipStream\ZipStream as ZipStreamBase; | |
class ZipStream extends ZipStreamBase | |
{ | |
/** | |
* @var array | |
*/ | |
protected $fileList = []; | |
/** | |
* @var int | |
*/ | |
protected $fileSizes = 0; | |
/** | |
* @var int | |
*/ | |
protected $filepathLengths = 0; | |
/** | |
* @var int | |
*/ | |
protected $bytesSent = 0; | |
/** | |
* @var | |
*/ | |
protected $cacheStream; | |
/** | |
* @param null $name | |
* @param array $opt | |
*/ | |
public function __construct($name = null, $opt = []) | |
{ | |
// We want all files treated like large files, which uses 'store' and no compression | |
$opt['large_file_size'] = 1; | |
parent::__construct($name, $opt); | |
} | |
/** | |
* @param $name | |
* | |
* @return $this | |
*/ | |
public function setName($name) | |
{ | |
$this->output_name = $name; | |
$this->need_headers = $name || $this->opt[self::OPTION_SEND_HTTP_HEADERS]; | |
return $this; | |
} | |
/** | |
* Hand me a writable output stream to this method, and we'll save the zip data as we stream it out. | |
* | |
* @param $stream | |
*/ | |
public function cacheZip($stream) | |
{ | |
$this->cacheStream = $stream; | |
} | |
/** | |
* @param String $str | |
*/ | |
protected function send($str) { | |
$this->bytesSent += strlen($str); | |
if(is_resource($this->cacheStream)) { | |
fwrite($this->cacheStream, $str); | |
} | |
parent::send($str); | |
} | |
/** | |
* @param Collection $fileList | |
* | |
* @return $this | |
*/ | |
public function queue(Collection $fileList) | |
{ | |
$fileList->each(function ($file) { | |
$this->queueFile($file); | |
}); | |
return $this; | |
} | |
/** | |
* @param ZipFile $file | |
*/ | |
public function queueFile(ZipFile $file) | |
{ | |
if(array_key_exists($file->getAbsolutePath(), $this->fileList)) { | |
return; | |
} | |
$this->fileList[$file->getAbsolutePath()] = $file; | |
$this->fileSizes += max(2, $file->getFilesize()); | |
$this->filepathLengths += strlen($file->getZipPath()); | |
} | |
/** | |
* @param string $cachePath | |
* | |
* @return $this | |
*/ | |
public function process($cachePath = null) | |
{ | |
if(is_null($cachePath) || !is_writable(dirname($cachePath))) { | |
$this->buildAndSend(); | |
return $this; | |
} | |
$this->cacheStream = fopen($cachePath, 'w'); | |
$this->buildAndSend(); | |
fclose($this->cacheStream); | |
return $this; | |
} | |
/** | |
* | |
*/ | |
protected function buildAndSend() | |
{ | |
$this->sendContentLengthHeader(); | |
foreach($this->fileList AS $file) { | |
$this->processFile($file); | |
} | |
$this->finish(); | |
} | |
/** | |
* @param $file | |
* | |
* @throws FileNotFoundException | |
* @throws \ZipStream\Exception\FileNotReadableException | |
*/ | |
protected function processFile(ZipFile $file) | |
{ | |
if(starts_with($file->getAbsolutePath(), "s3://")) { | |
$this->addFileFromStream($file->getZipPath(), $file->getHandle()); | |
fclose($file->getHandle()); | |
} else { | |
$this->addFileFromPath($file->getZipPath(), $file->getAbsolutePath()); | |
} | |
} | |
/** | |
* @return int | |
*/ | |
public function getBytesSent() | |
{ | |
return $this->bytesSent; | |
} | |
/** | |
* We can do this! | |
*/ | |
protected function sendContentLengthHeader() | |
{ | |
header("Content-Length: " . $this->calculateZipSize()); | |
} | |
/** | |
* See http://stackoverflow.com/a/19380600/660694 | |
* | |
* @return int | |
*/ | |
public function calculateZipSize() | |
{ | |
return count($this->fileList) * (30 + 46) + (2 * $this->filepathLengths) + $this->fileSizes + 22; | |
} | |
/** | |
* @param callable $errorCallback | |
* | |
* @return $this | |
*/ | |
public function sanityCheckFileSize(callable $errorCallback) | |
{ | |
if($this->calculateZipSize() != $this->getBytesSent()) { | |
$errorCallback($this->calculateZipSize(), $this->getBytesSent()); | |
} | |
return $this; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Very insightful! Thank you very much!