diff --git a/controllers/Download.php b/controllers/Download.php index 58ec832..f9b81ef 100644 --- a/controllers/Download.php +++ b/controllers/Download.php @@ -34,7 +34,7 @@ class Download $files = []; $album_ids = array_merge([$album->id_tag], $this->getChildAlbumIds($album->id_tag)); - foreach($album_ids as $album_id) + foreach ($album_ids as $album_id) { $iterator = AssetIterator::getByOptions(['id_tag' => $album_id]); while ($asset = $iterator->next()) @@ -42,8 +42,8 @@ class Download } $descriptorspec = [ - 0 => ['pipe', 'r'], - 1 => ['pipe', 'w'], + 0 => ['pipe', 'r'], // STDIN + 1 => ['pipe', 'w'], // STDOUT ]; $command = 'tar -cf - -C ' . escapeshellarg(ASSETSDIR) . ' --null -T -'; @@ -59,22 +59,33 @@ class Download if(!$pipes[1]) throw new UnexpectedValueException('Could not open pipe for STDOUT'); - $album_name = $album->tag; + // STDOUT should not block. + stream_set_blocking($pipes[1], 0); header('Pragma: no-cache'); header('Content-Description: File Download'); - header('Content-disposition: attachment; filename="' . $album_name . '.tar"'); + header('Content-disposition: attachment; filename="' . $album->tag . '.tar"'); header('Content-Type: application/octet-stream'); header('Content-Transfer-Encoding: binary'); - foreach($files as $file) + // Write filenames to include to STDIN, separated by null bytes. + foreach ($files as $file) fwrite($pipes[0], $file . "\0"); + // Close STDIN pipe to start archiving. fclose($pipes[0]); - while($chunk = stream_get_contents($pipes[1], 4096)) - echo $chunk; + do + { + // Read STDOUT as `tar` is doing its work. + echo stream_get_contents($pipes[1], 4096); + // Are we still running? + $status = proc_get_status($proc); + } + while (!empty($status) && $status['running']); + + // Close STDOUT pipe and clean up process. fclose($pipes[1]); proc_close($proc);