ASPit - Totally ASP JSit - Totally JavaScript
Search PHPit

Use this textbox to search for articles on PHPit. Seperate keywords with a space.

Advertisements

Creating ZIP and TAR archives on the fly with PHP

(Page 3 out of 5)

Fixing the relative paths

So we have to find a solution for that. The easiest solution is to simply copy all the files and directory to our temporary directory, so the relative paths still work. Copying a file is easy, since PHP comes with an inbuilt copy function logically called copy(). We have to add the following to our script:

// Copy files & directories so relative paths still work
foreach ($list as $item) {
        if (is_file($item)) {
                // Copy file
                copy ($item, $dir . $item);
        } elseif (is_dir ($item)) {
                // TODO: we'll do this in a minute
        } else {
                // Invalid file, just ignore
                continue;
        }
}

If the above code is placed above the chdir() call, all the files will be included in the zip file, and work properly. But directories aren't yet included because we haven't copied those yet.

Unfortunately PHP doesn't come with a copy_directory() or something similar, and we have to write our own. A quick Google search will probably turn up something very quickly, but here's one I wrote myself:

function move_directory($source, $dest, $copy=false) {
        // Standardize paths
        $source = str_replace('\\\\', '/', $source);
        $source = str_replace('\\', '/', $source);

        $dest = str_replace('\\\\', '/', $dest);
        $dest = str_replace('\\', '/', $dest);

        // Remove trailing slashes
        $source = rtrim($source, '/');
        $dest = rtrim($dest, '/');

        // Add trailing slashes
        $source .= '/';
        $dest .= '/';

        // Source doesn't exist?
        if (file_exists($source) == false) { return false; }

        // Try to create destination
        @mkdir($dest);

        // Destination doesn't exist?
        if (file_exists($dest) == false) { return false; }

        // Copy all files
        $d = dir($source);

        while (false !== ($entry = $d->read())) {
                if ($entry == '.' OR $entry == '..') continue;
                $file = $d->path . $entry;

                // Is a sub dir?
                if (is_dir($file)) {
                        // Try to create sub dir
                        $subdir = $dest . $entry . '/';
                        $result = @mkdir($subdir);
                        if ($result == false) { continue; }

                        // Move files in sub directory
                        move_directory($file, $subdir, $copy);
                        continue;
                }

                // Copy file
                copy($file, $dest . $entry);

                // Remove old file?
                if ($copy == false) {
                        // Moving the directory, so remove original
                        @unlink($file);
                }
        }       

        if ($copy == false) {
                @rmdir($source);
        }

        return true;
}

I won't really bother explaining the function, but it basically copies all the files in the directory, and all the sub-directories (and does the same for all the sub-directories, and so forth).

If we incorporate this function into our script, the part that copies the files and directories now looks like this:

// Copy files & directories so relative paths still work
foreach ($list as $item) {
        if (is_file($item)) {
                // Copy file
                copy ($item, $dir . $item);
        } elseif (is_dir ($item)) {
                // Copy directory
                move_directory ($item, $dir . $item, true);
        } else {
                // Invalid file, just ignore
                continue;
        }
}

And if we put everything together our script looks like this:


include ('pear/archive_zip.php');

// Create instance of Archive_Zip class, and pass the name of our zipfile
$zipfile = New Archive_Zip('myzipfile.zip');

// Create a list of files and directories
$list = array('example.txt', 'test');

// Get temporary directory
if (!empty($_ENV['TMP'])) {
        $tempdir = $_ENV['TMP'];
} elseif (!empty($_ENV['TMPDIR'])) {
        $tempdir = $_ENV['TMPDIR'];
} elseif (!empty($_ENV['TEMP'])) {
        $tempdir = $_ENV['TEMP'];
} else {
        $tempdir = dirname(tempnam('', 'na'));
}

if (empty($tempdir)) { die ('No temporary directory'); }

// Make sure trailing slash is there
$tempdir = rtrim($tempdir, '/');
$tempdir .= '/';

// Make sure temporary directory is writable
if (is_writable($tempdir) == false) {
        die ('Temporary directory isn\'t writable');
}

// Create temp name     for our own directory
$dir = tempnam($tempdir, 'temp');

// Make sure another file or directory doesn't already exist with this name
@unlink($dir);
@rmdir($dir);

// Create directory
mkdir($dir);
$dir .= '/';

// Copy files & directories so relative paths still work
foreach ($list as $item) {
        if (is_file($item)) {
                // Copy file
                copy ($item, $dir . $item);
        } elseif (is_dir ($item)) {
                // Copy directory
                move_directory ($item, $dir . $item, true);
        } else {
                // Invalid file, just ignore
                continue;
        }
}

// Change current working directory, so that the zip file gets created in the temp dir
chdir($dir);

// Create the zip file
$zipfile->create($list);

echo 'Zip file created in ' . $dir;

function move_directory($source, $dest, $copy=false) {
        // Standardize paths
        $source = str_replace('\\\\', '/', $source);
        $source = str_replace('\\', '/', $source);

        $dest = str_replace('\\\\', '/', $dest);
        $dest = str_replace('\\', '/', $dest);

        // Remove trailing slashes
        $source = rtrim($source, '/');
        $dest = rtrim($dest, '/');

        // Add trailing slashes
        $source .= '/';
        $dest .= '/';

        // Source doesn't exist?
        if (file_exists($source) == false) { return false; }

        // Try to create destination
        @mkdir($dest);

        // Destination doesn't exist?
        if (file_exists($dest) == false) { return false; }

        // Copy all files
        $d = dir($source);

        while (false !== ($entry = $d->read())) {
                if ($entry == '.' OR $entry == '..') continue;
                $file = $d->path . $entry;

                // Is a sub dir?
                if (is_dir($file)) {
                        // Try to create sub dir
                        $subdir = $dest . $entry . '/';
                        $result = @mkdir($subdir);
                        if ($result == false) { continue; }

                        // Move files in sub directory
                        move_directory($file, $subdir, $copy);
                        continue;
                }

                // Copy file
                copy($file, $dest . $entry);

                // Remove old file?
                if ($copy == false) {
                        // Moving the directory, so remove original
                        @unlink($file);
                }
        }       

        if ($copy == false) {
                @rmdir($source);
        }

        return true;
}

?>

And that's pretty much all we need to do.

« Previous: Using the Temporary Directory
Next: Creating a re-usable function »



7 Responses to “Creating ZIP and TAR archives on the fly with PHP”

  1. Il blog sul php » Creare un archivio ZIP o TAR al volo con php Says:

    […] Link: http://phpit.net/article/creating-zip-tar-archives-dynamically-php/  […]

  2. Creare un archivio ZIP o TAR al volo con php - sastgroup.com Says:

    […] Link: http://phpit.net/article/creating-zip-tar-archives-dynamically-php/  Posted by Administrator on May 18th, 2006 Filed in tutorials, php, links, php […]

  3. Giao’s Journal » Blog Archive : Creating ZIP and TAR archives on the fly with PHP Says:

    […] http://phpit.net/article/creating-zip-tar-archives-dynamically-php/ […]

  4. PHP-Master02 Says:

    Hi,
    This was very interesting and gave me lots of thoughs to create new things,,
    thanks alot

  5. Andrew Says:

    Hi, Thanks a lot for the detailed tutorial… I have one problem, although: When I executed your script, it simply downloads the actual “example.txt” file (zipped up) instead of the array of files. I’m very new to php so apologies for my ignorance in advance but is there something I’m missing?

  6. Andrew Says:

    Hi, this is Andrew again and I am still having the same problem almost 3 weeks later. If I implement the script exactly “As is”, it runs perfectly fine and I download the zip file but it only has 1 file in it… the “example.txt” file. Any ideas? I can’t find any other zip file tutorials…

  7. Fab Says:

    Just tried it and it worked fine, very good article. First it didn’t put any file in the zipfile altough i filed my filelist array correctly, but later I saw it was due to a mistake in filepaths (I forgot a directory in the path).
    Andrew ->Did you check that your filepaths are OK?

Leave a Reply

About the author
Dennis Pallett is the main contributor to PHPit. He owns several websites, including ASPit and Chill2Music. He is currently still studying.
Article Index
  1. Introduction & Creating the Archives
  2. Using the Temporary Directory
  3. Fixing the relative paths
  4. Creating a re-usable function
  5. Streaming files & Conclusion
Bookmark Article
Download Article
PDF
Download this article as a PDF file