[PHP] ZipArchive, Too many open files

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Joshua
  • Registratie: Juli 2005
  • Laatst online: 16-09 15:47
Ik ben bezig een scriptje te schrijven waarmee je een .zip archive kunt maken.
Hiervoor maak ik gebruik van de classe ZipArchive en DirectoryIterator (beide zitten standaard in PHP).
Alles gaat goed totdat ik probeer een subdirectory toe te voegen, dan krijg ik de fout dat er te veel files open zijn.
Fatal error: Uncaught exception 'RuntimeException' with message 'DirectoryIterator::__construct(/home/joshua/projects/php/testtest/test/): failed to open dir: Too many open files' in /home/joshua/projects/php/test/backup2.class.php:23
Stack trace:
#0 /home/joshua/projects/php/test/backup2.class.php(23): DirectoryIterator->__construct('/home/joshua/pr...')
#1 /home/joshua/projects/php/test/backup2.class.php(15): backup->scanFiles('/home/joshua/pr...')
#2 /home/joshua/projects/php/test/backup2.class.php(30): backup->createZip()
#3 /home/joshua/projects/php/test/backup2.class.php(15): backup->scanFiles('/home/joshua/pr...')
#4 /home/joshua/projects/php/test/backup2.class.php(30): backup->createZip()
#5 /home/joshua/projects/php/test/backup2.class.php(15): backup->scanFiles('/home/joshua/pr...')
#6 /home/joshua/projects/php/test/backup2.class.php(30): backup->createZip()
#7 /home/joshua/projects/php/test/backup2.class.php(15): backup->scanFiles('/home/joshua/pr...')
#8 /home/joshua/projects/php/test/backup2.class.php(3 in /home/joshua/projects/php/test/backup2.class.php on line 23
Heb ondertussen al ondekt dat het een bekende "bug" is bij PHP.

Het betreft hier de volgende stuk code:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?
class backup extends ZipArchive {
   public $zipFile;
   public $location;

   public function __construct($zipFile,$location) {
      $this->zipFile = $zipFile;    //Name of the zip file, don't forget the extension
      $this->location = $location;    //Location of the directory we need to create an archive from.
      self::createZip($zipFile);    //Create the zip file.
   }

   public function createZip() {
      if (self::open($this->zipFile,ZIPARCHIVE::CREATE)) {
         //It's able to create the zip file.
         self::scanFiles($this->location);
      }
      else {
         //It's unable to create the zip file.
      }
   }

   public function scanFiles($location) {
      $dir = new DirectoryIterator($this->location);

      while ($dir->valid()) {
         if ($dir->isDir() && !$dir->isDot()) {
            self::addEmptyDir($dir->current());
            self::scanFiles($this->location. "" .$location);
         }
         elseif (!$dir->isDot()) {
            self::addFile($location. "" .$dir->current(),$dir->current());
         }
         $dir->next();
      }
      self::close();
   }
}
?>


Mijn vraag is nu dus eigenlijk:
Hoe kan ik dit het beste oplossen? Of zijn er makkelijkere manieren om een zip archive te maken met behulp van PHP.

Ik gebruik trouwens PHP versie: 5.2.6RC-3 (gentoo build)

Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 22:43
Waar heb je gevonden dat dit een bekende bug is? Wellicht staat daar ook een work-around, of meer informatie die relevant is voor het probleem.

Verder lijkt het erop dat je álle bestanden in een bepaalde directory wilt inpakken. Kun je dan niet gewoon een command line tool aanroepen en het resultaat naar de browser sturen, bv. met passthru?

Acties:
  • 0 Henk 'm!

  • Joshua
  • Registratie: Juli 2005
  • Laatst online: 16-09 15:47
Soultaker schreef op woensdag 09 april 2008 @ 13:59:
Waar heb je gevonden dat dit een bekende bug is? Wellicht staat daar ook een work-around, of meer informatie die relevant is voor het probleem.

Verder lijkt het erop dat je álle bestanden in een bepaalde directory wilt inpakken. Kun je dan niet gewoon een command line tool aanroepen en het resultaat naar de browser sturen, bv. met passthru?
Had beetje verkeerd gelezen bij de bugs van PHP het is geen bug. Mijn excuses.

Heb zelf ook al gekeken naar een extern programma, maar dat is eigenlijk toch niet echt wat ik wil.

Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 22:43
Is het mogelijk om de limiet op het aantal open files op te voeren? (Kun je met ulimit -n eens kijken hoe hoog die nu is?)

Acties:
  • 0 Henk 'm!

  • Joshua
  • Registratie: Juli 2005
  • Laatst online: 16-09 15:47
Soultaker schreef op woensdag 09 april 2008 @ 14:19:
Is het mogelijk om de limiet op het aantal open files op te voeren? (Kun je met ulimit -n eens kijken hoe hoog die nu is?)
De huidige limiet is 1024, dat terwijl er maar 28 files in de opgegeven dir. zitten.
Het probleem zit hem dan waarschijnlijk ook in mijn code, al kan ik zo 1, 2, 3 niet vinden waar en hoe ik dit moet oplossen.

Acties:
  • 0 Henk 'm!

  • OnTracK
  • Registratie: Oktober 2002
  • Laatst online: 20:37
PHP:
1
2
self::createZip($zipFile);
self::scanFiles($this->location)

Doe je door middel van het self keyword niet een static call op die functies? Terwijl je daar wel een dir meegeeft met je $this->location. Die $this is helemaal niet bekend en dan dus null. Ik gok dat je DirectoryIterator met een null argument de root neemt, en daardoor teveel files opent?

Volgens mij wil je dus deze ook binnen het huidige object aanroepen, als volgt:
PHP:
1
2
$this->createZip($zipFile);
$this->scanFiles($this->location)





Verder "werkt" de check of self::open correct is gegaan niet, hij zal altijd zeggen dat je zip correct is aangemaakt. In de manual staat het volgende:
Returns a resource handle for later use with zip_read() and zip_close() or returns the number of error if filename does not exist or in case of other error.
Beide zullen naar true evalueren, je zult dus iets in de volgende richting moeten zoeken: (de tweede staat zo in de manual, maar is in mijn ogen ook niet erg netjes)

PHP:
1
2
if (is_resource($this->open($this->zipFile, ZIPARCHIVE::CREATE)))
if ($this->open($this->zipFile, ZIPARCHIVE::CREATE) === true)

[ Voor 54% gewijzigd door OnTracK op 09-04-2008 21:28 ]

Not everybody wins, and certainly not everybody wins all the time.
But once you get into your boat, push off and tie into your shoes.
Then you have indeed won far more than those who have never tried.


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 22:43
Je kan sowieso eens beginnen met echoën welke directories je precies bezoek in scanFiles en welke files probeert toe te voegen met addFiles...

Acties:
  • 0 Henk 'm!

  • Joshua
  • Registratie: Juli 2005
  • Laatst online: 16-09 15:47
Bedankt voor jullie hulp, de foutmelding met betrekking tot het openen van teveel files is opgelost.

Het enige probleem waar ik nog mee worstel zijn de subdirectories.
Mijn code herkent ze wel, voegt deze ook toe door middel van addEmptyDir en daarna voert hij netjes de scan uit op deze subdirectory.
Als mijn scan start in de map /test dan herkent hij bijvoorbeeld de directory /a in /test (/test/a) maar zo gauw als er weer een subdirectory in /a zit bijvoorbeeld /b (/test/a/b) gaat het fout en zegt hij dat hij de locatie niet kan vinden. Opzich logisch aangezien hij zoekt in $this->location.$dir->current().

Mijn vraag is dan ook hoe ik dit het beste kan aanpakken? Ik moet dus op een of andere manier achterhalen of de directory die ik scan een subdirectory is vanaf waar ik begin te scannen.

Mijn aangepaste code tot nu toe:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?
class backup extends ZipArchive {
    public $zipFile;
    public $location;

    public function __construct($zipFile,$location) {
        $this->zipFile = $zipFile;  //Name of the zip archive, don't forget the extension.
        $this->location = $location;    //Location of the directory we need to create an archive from.
        self::createZip();      //Create the zip archive.
    }
    
    public function createZip() {
        if (self::open($this->zipFile, ZIPARCHIVE::CREATE) === true) {
            //It's able to create the zip archive.
            self::scanFiles($this->location);
        }
        else {
            //Failed to create zip archive.
        }
    }
    
    public function scanFiles($location) {
        $dir = new DirectoryIterator($location);
        
        while ($dir->valid()) {
            if ($dir->isDir() && !$dir->isDot()) {
                echo($dir->current(). "\n");
                //self::addEmptyDir($dir->current());
                self::scanFiles($this->location.$dir->current());
            }
            elseif (!$dir->isDot()) {
                echo($dir->current(). "\n");
                //self::addFile($this->location.$dir->current(),$dir->current());
            }
            $dir->next();
        }
    }
}
?>

Acties:
  • 0 Henk 'm!

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 21:47

Creepy

Tactical Espionage Splatterer

Je geeft je scanfiles al een $location mee als parameter, waarom dan nog die $this->location gebruiken?

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


Acties:
  • 0 Henk 'm!

  • Joshua
  • Registratie: Juli 2005
  • Laatst online: 16-09 15:47
Creepy schreef op donderdag 10 april 2008 @ 09:36:
Je geeft je scanfiles al een $location mee als parameter, waarom dan nog die $this->location gebruiken?
Omdat als ik de functie __construct aanroep hij de opgegeven locatie van waar de ZipArchive gemaakt wordt opslaat in $this->location.
Hierna roep ik scanFiles($this->location) aan (deze accepteer dus de parameter als $locatie. Als ik dan een directory tegenkom dan roep ik weer de functie scanFiles aan, ditmaal om dus deze directory te scannen.
Omdat als je een new DirectoryIterator() maakt je de fullpath moet meegeven moet ik dus het oude path + subdir megeven wat dus is: $this->location.$dir->current().
Maar als ik nu in deze subdir weer een subdir tegenkomt gaat het fout omdat $this->location.$dir->current() niet meer gelijk is aan de path die hij moet scannen.
Ik kan $this->location dan wel vervangen door de nieuwe fullpath naar deze subdirectory maar dan gaat het fout bij de volgende files die hij tegenkomt aangezien DirectoryIterator() mappen en files door elkaar heen doet.

Hoop dat ik het een beetje duidelijk uitgelegd heb waarom ik dus $this->location gebruik.



EDIT:
Heb het werkend gekregen, een vriend van me kwam met het ideen om getPathname te gebruiken (waarom ik daar zelf niet opgekomen ben weet ik eigenlijk niet 8)7).

Voor de liefhebbers hieronder de werkende code:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?
class backup extends ZipArchive {
    public $zipFile;
    public $location;

    public function __construct($zipFile,$location) {
        $this->zipFile = $zipFile;                                  //Name of the zip archive, don't forget the extension.
        $this->location = $location;                                    //Location of the directory we need to create an archive from.
        self::createZip();                                      //Create the zip archive.
    }
    
    public function createZip() {
        if (self::open($this->zipFile, ZIPARCHIVE::CREATE) === true) {
                                                        //It's able to create the zip archive.
            self::scanFiles($this->location);                           //We need to scan for files and directories.
        }
        else {
                                                        //Failed to create zip archive.         
        }
    }
    
    public function scanFiles($location) {
        $dir = new DirectoryIterator($location);    
        
        while ($dir->valid()) {
            if ($dir->isDir() && !$dir->isDot()) {                          //If it's an directory and not a . or .. we can do something with it.
                self::addEmptyDir($dir->current());                     //We need to add a empty directory to the zip file.
                self::scanFiles($dir->getPathname($dir->current()));                //Now we need to scan that directory.
            }
            elseif (!$dir->isDot()) {                               //If the file is not a . or .. we can do something with it.
                $file = str_replace($this->location,"",$dir->getPathname($dir->current())); //We need to replace $this->location with nothing to prevent that we add a fullpath to our ZipArchive
                self::addFile($dir->getPathname($dir->current()),$file);            //Now we need to add the file.
            }
            $dir->next();                                       //Next please ;-)
        }
    }
}
?>

[ Voor 43% gewijzigd door Joshua op 10-04-2008 10:37 ]

Pagina: 1