forked from Public/pics
Adds the ability to export an album using streaming tar.
This commit is contained in:
parent
768f5ee529
commit
20db3561cf
122
controllers/Download.php
Normal file
122
controllers/Download.php
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
<?php
|
||||||
|
/*****************************************************************************
|
||||||
|
* Download.php
|
||||||
|
* Contains the code to download an album.
|
||||||
|
*
|
||||||
|
* Kabuki CMS (C) 2013-2019, Aaron van Geffen
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
class Download extends HTMLController
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
// Ensure we're logged in at this point.
|
||||||
|
$user = Registry::get('user');
|
||||||
|
if (!$user->isLoggedIn())
|
||||||
|
throw new NotAllowedException();
|
||||||
|
|
||||||
|
if(!isset($_GET['tag']))
|
||||||
|
throw new UnexpectedValueException('Must specify an album to download');
|
||||||
|
|
||||||
|
$tag = (int)$_GET['tag'];
|
||||||
|
$album = Tag::fromId($tag);
|
||||||
|
|
||||||
|
if($album->kind !== 'Album')
|
||||||
|
throw new UnexpectedValueException('Specified tag does not correspond to an album');
|
||||||
|
|
||||||
|
//Yes TOCTOU but it does not need to be perfect.
|
||||||
|
$lock_file = join('/', array(sys_get_temp_dir(), 'pics-export.lock'));
|
||||||
|
if(!file_exists($lock_file))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$fp = fopen($lock_file, 'x');
|
||||||
|
|
||||||
|
if(!$fp)
|
||||||
|
throw new UnexpectedValueException('Could not open lock-file');
|
||||||
|
|
||||||
|
$this->exportAlbum($album);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
fclose($fp);
|
||||||
|
unlink($lock_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new UnexpectedValueException('Another export is busy, please try again later');
|
||||||
|
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function exportAlbum($album)
|
||||||
|
{
|
||||||
|
$files = array();
|
||||||
|
|
||||||
|
$album_ids = array_merge(array($album->id_tag), $this->getChildAlbumIds($album->id_tag));
|
||||||
|
foreach($album_ids as $album_id)
|
||||||
|
{
|
||||||
|
$iterator = AssetIterator::getByOptions(
|
||||||
|
array(
|
||||||
|
'id_tag' => $album_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
while($asset = $iterator->Next())
|
||||||
|
{
|
||||||
|
$files[] = join(DIRECTORY_SEPARATOR, array('.', $asset->getSubdir(), $asset->getFilename()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$descriptorspec = array(
|
||||||
|
0 => array('pipe', 'r'),
|
||||||
|
1 => array('pipe', 'w'),
|
||||||
|
);
|
||||||
|
|
||||||
|
$command = 'tar --null -cf - -T -';
|
||||||
|
|
||||||
|
$proc = proc_open($command, $descriptorspec, $pipes, ASSETSDIR);
|
||||||
|
|
||||||
|
if(!$proc)
|
||||||
|
throw new UnexpectedValueException('Could not execute TAR command');
|
||||||
|
|
||||||
|
if(!$pipes[0])
|
||||||
|
throw new UnexpectedValueException('Could not open pipe for STDIN');
|
||||||
|
|
||||||
|
if(!$pipes[1])
|
||||||
|
throw new UnexpectedValueException('Could not open pipe for STDOUT');
|
||||||
|
|
||||||
|
$album_name = $album->tag;
|
||||||
|
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
header('Content-Description: File Download');
|
||||||
|
header('Content-disposition: attachment; filename="' . $album_name . '.tar"');
|
||||||
|
header('Content-Type: application/octet-stream');
|
||||||
|
header('Content-Transfer-Encoding: binary');
|
||||||
|
|
||||||
|
foreach($files as $file)
|
||||||
|
fwrite($pipes[0], $file . "\0");
|
||||||
|
|
||||||
|
fclose($pipes[0]);
|
||||||
|
|
||||||
|
while($chunk = stream_get_contents($pipes[1], 4096))
|
||||||
|
echo $chunk;
|
||||||
|
|
||||||
|
fclose($pipes[1]);
|
||||||
|
|
||||||
|
proc_close($proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getChildAlbumIds($parent_id)
|
||||||
|
{
|
||||||
|
$ids = array();
|
||||||
|
|
||||||
|
$albums = Tag::getAlbums($parent_id, 0, PHP_INT_MAX, 'object');
|
||||||
|
foreach($albums as $album)
|
||||||
|
{
|
||||||
|
$ids[] = $album->id_tag;
|
||||||
|
$ids = array_merge($ids, $this->getChildAlbumIds($album->id_tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ids;
|
||||||
|
}
|
||||||
|
}
|
@ -61,7 +61,13 @@ class ViewPhotoAlbum extends HTMLController
|
|||||||
// Can we do fancy things here?
|
// Can we do fancy things here?
|
||||||
// !!! TODO: permission system?
|
// !!! TODO: permission system?
|
||||||
$buttons = [];
|
$buttons = [];
|
||||||
|
|
||||||
if (Registry::get('user')->isLoggedIn())
|
if (Registry::get('user')->isLoggedIn())
|
||||||
|
$buttons[] = [
|
||||||
|
'url' => BASEURL . '/download/?tag=' . $id_tag,
|
||||||
|
'caption' => 'Download this album',
|
||||||
|
];
|
||||||
|
|
||||||
$buttons[] = [
|
$buttons[] = [
|
||||||
'url' => BASEURL . '/uploadmedia/?tag=' . $id_tag,
|
'url' => BASEURL . '/uploadmedia/?tag=' . $id_tag,
|
||||||
'caption' => 'Upload new photos here',
|
'caption' => 'Upload new photos here',
|
||||||
|
@ -28,6 +28,7 @@ class Dispatcher
|
|||||||
'suggest' => 'ProvideAutoSuggest',
|
'suggest' => 'ProvideAutoSuggest',
|
||||||
'timeline' => 'ViewTimeline',
|
'timeline' => 'ViewTimeline',
|
||||||
'uploadmedia' => 'UploadMedia',
|
'uploadmedia' => 'UploadMedia',
|
||||||
|
'download' => 'Download',
|
||||||
];
|
];
|
||||||
|
|
||||||
// Work around PHP's FPM not always providing PATH_INFO.
|
// Work around PHP's FPM not always providing PATH_INFO.
|
||||||
|
Loading…
Reference in New Issue
Block a user