Fixes album export by reducing memory usage of the scripts. #18
2
app.php
@ -27,6 +27,6 @@ set_error_handler('ErrorHandler::handleError');
|
|||||||
ini_set("display_errors", DEBUG ? "On" : "Off");
|
ini_set("display_errors", DEBUG ? "On" : "Off");
|
||||||
|
|
||||||
// The real magic starts here!
|
// The real magic starts here!
|
||||||
ob_start();
|
ob_start(null, 32768);
|
||||||
|
|||||||
Dispatcher::dispatch();
|
Dispatcher::dispatch();
|
||||||
ob_end_flush();
|
ob_end_flush();
|
||||||
|
@ -53,20 +53,6 @@ class Download extends HTMLController
|
|||||||
{
|
{
|
||||||
$files = array();
|
$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()));
|
|
||||||
minnozz
commented
Is this change still useful? It seems like an array with only paths ( Is this change still useful? It seems like an array with only paths (`$files`) and not the file contents should not get so large as to hit a memory limit.
Roflin
commented
In the old situation the export would fail on the first chunk leaving behind a .tar of ~0kb, This means that the list of files of an album of around 270 pictures already consumed a considerable amount of memory. So I would still like to keep this more "streaming" approach as a optimization. In the old situation the export would fail on the first chunk leaving behind a .tar of ~0kb, This means that the list of files of an album of around 270 pictures already consumed a considerable amount of memory. So I would still like to keep this more "streaming" approach as a optimization.
minnozz
commented
I don't think that's possible, because if the memory limit was hit while building the array of file names, there would be no tar file at all, since While I don't think the streaming approach is wrong, this feels like a premature optimization (and certainly one that should not be necessary). I don't think that's possible, because if the memory limit was hit while building the array of file names, there would be no tar file at all, since `tar` is invoked after `$files` has been built.
While I don't think the streaming approach is wrong, this feels like a premature optimization (and certainly one that should not be necessary).
|
|||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$descriptorspec = array(
|
$descriptorspec = array(
|
||||||
minnozz
commented
Roflin
commented
I'm currently using ther tar's -T functionality to read a list of files in a file seperated by a "\0" byte. and I skipping the file all together by using - as the argument to -T and feeding it the files on STDIN. After a quick look at the passthru i don't think such a thing is possible. and I think i will run out of "command line" space to execute the tar command with passthru. I'm currently using ther tar's -T functionality to read a list of files in a file seperated by a "\0" byte. and I skipping the file all together by using - as the argument to -T and feeding it the files on STDIN. After a quick look at the passthru i don't think such a thing is possible. and I think i will run out of "command line" space to execute the tar command with passthru.
Aaron
commented
Agreed that I do not think the proposed solution to use Agreed that `passthru` is probably the better solution, especially if you're not expecting to handle anything on `STDOUT` or `STDERR`.
I do not think the proposed solution to use `STDIN` to manually feed filenames will provide much in the way of performance benefit. If anything, I think `tar` can enumerate files more quickly than a PHP script.
|
|||||||
0 => array('pipe', 'r'),
|
0 => array('pipe', 'r'),
|
||||||
1 => array('pipe', 'w'),
|
1 => array('pipe', 'w'),
|
||||||
@ -93,8 +79,19 @@ class Download extends HTMLController
|
|||||||
header('Content-Type: application/octet-stream');
|
header('Content-Type: application/octet-stream');
|
||||||
header('Content-Transfer-Encoding: binary');
|
header('Content-Transfer-Encoding: binary');
|
||||||
|
|
||||||
foreach($files as $file)
|
$album_ids = array_merge(array($album->id_tag), $this->getChildAlbumIds($album->id_tag));
|
||||||
fwrite($pipes[0], $file . "\0");
|
foreach($album_ids as $album_id)
|
||||||
|
{
|
||||||
|
$iterator = AssetIterator::getByOptions(
|
||||||
|
array(
|
||||||
Aaron
commented
This is an aside, but I'd like to stick to new-style (bracketed) arrays, that is This is an aside, but I'd like to stick to new-style (bracketed) arrays, that is `[]` instead of `array()`.
|
|||||||
|
'id_tag' => $album_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
while($asset = $iterator->Next())
|
||||||
|
{
|
||||||
|
fwrite($pipes[0], join(DIRECTORY_SEPARATOR, array('.', $asset->getSubdir(), $asset->getFilename())) . "\0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fclose($pipes[0]);
|
fclose($pipes[0]);
|
||||||
|
|
||||||
|
I haven't looked at the rest of the framework, but you might want to consider disabling the output buffer before calling
tar
. This code could then check whether an output buffer is still active withob_get_level
.@aaron what are your thoughts on this? I'm not familiar enough to comment on this.
I agree with @minnozz; much better to disable it locally (i.e. in the download controller) with
ob_end_flush
. In general, output buffering is quite useful.