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");
|
||||
|
||||
// The real magic starts here!
|
||||
ob_start();
|
||||
ob_start(null, 32768);
|
||||
|
||||
Dispatcher::dispatch();
|
||||
ob_end_flush();
|
||||
|
@ -53,20 +53,6 @@ class Download extends HTMLController
|
||||
{
|
||||
$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(
|
||||
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'),
|
||||
1 => array('pipe', 'w'),
|
||||
@ -93,8 +79,19 @@ class Download extends HTMLController
|
||||
header('Content-Type: application/octet-stream');
|
||||
header('Content-Transfer-Encoding: binary');
|
||||
|
||||
foreach($files as $file)
|
||||
fwrite($pipes[0], $file . "\0");
|
||||
$album_ids = array_merge(array($album->id_tag), $this->getChildAlbumIds($album->id_tag));
|
||||
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]);
|
||||
|
||||
|
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.