Maestro J
Maestro J

Reputation: 1

Using ziparchive or other php script to allow user to download all files from a folder in zip

So id like to offer my users the option to download all files from specific folders/directories from my site (lets call it mass download).

What id like to do is when the user clicks a link/button the script will create a temporary zip file of all the files in the specific folder and the user will be able to download it. (I will need different instances of the link/button on different pages to download files from other folders that I choose if that makes sense). The zip file will be deleted after sometime. Say after download is complete or something. I think ziparchive can do this but I dont know where to start or how to implement it. Its a joomla site and I can't find any extensions that can do this.

I dont know the first thing about php so I am hoping some one will be willing to help me get this working if thats possible. Thanks

Upvotes: 0

Views: 983

Answers (1)

Kenney
Kenney

Reputation: 9093

If your server allows execution of shell commands from PHP, and zip is installed, you could generate a zip on the fly with passthru( "zip - directory" ). The - says to write to stdout, which saves you from having to deal with temporary file cleanup.

Here's an outline of such a script:

<?php
if ( ! $dir = get_my_directory() )
    die("Illegal call.");

header( 'Content-Type: application/zip' );
header( 'Content-Disposition: attachment; filename=\"your.zip\"' );
passthru( 'zip -r - ' . escapeshellarg( $dir ) );

/**
 * @return false/null or the directory to zip.
 */
function get_my_directory() {
    ....
    return ....;
}

However you implement get_my_directory(), make sure that it isn't possible for anyone to specify any path on your server!

Also, do not generate any output (no echo/print or warnings), because then either the headers won't be set, or the zip binary data will be corrupt.

Other than that, there are code samples and documentation on PHP's ZipArchive page.

UPDATE

(@ OP: I'm not really sure what you're doing implementing PHP solutions if you don't know any PHP. But, let's assume that you want to learn. )

Lets say that you have 3 public directories you would like to offer for download, and that anyone can download them. You would implement as follows:

function get_my_directory() {
    // list of the directories you want anyone to be able to download.
    // These are key-value pairs, so we can use the key in our URLs
    // without revealing the real directories.
    $my_directories = array(
       'dir1' => 'path/to/dir1/',
       'dir2' => 'path/to/dir2/',
       'dir3' => 'path/to/dir3/'
    );

    // check if the 'directory' HTTP GET parameter is given:
    if ( ! isset( $_GET['directory'] ) )
        return null;               // it's not set: return nothing
    else
        $dir = $_GET['directory']; // it's set: save it so we don't have
                                   // to type $_GET['directory'] all the time.

    // validate the directory: only pre-approved directories can be downloaded
    if ( ! in_array( $dir, array_keys( $my_directories ) ) )
       return null;                    // we don't know about this directory
    else
       return $my_directories[ $dir ]; // the directory: is 'safe'.
}

And yes, you paste the first and second code sample in one .php file (be sure to replace the first get_my_directory function with the second one), somewhere on your server where it is accessible.

If you call the file 'download-archive.php', and place it in the DocumentRoot, you would access it as http://your-site/download-archive.php?directory=dir1 etc.

Here are some references:

Update 2

Here's a complete script using ZipArchive. It only adds files in the directory; no subdirectories.

<?php
if ( ! $dir = get_my_directory() )
    die("Illegal call.");

$zipfile = make_zip( $dir );
register_shutdown_function( function() use ($zipfile) {
    unlink( $zipfile ); // delete the temporary zip file
} );

header( "Content-Type: application/zip" );
header( "Content-Disposition: attachment; filename=\"$zipfile\"" );
readfile( $zipfile );

function make_zip( $dir )
{
    $zip = new ZipArchive();
    $zipname = 'tmp_'.basename( $dir ).'.zip';  // construct filename
    if ($zip->open($zipname, ZIPARCHIVE::CREATE) !== true)
        die("Could not create archive");


    // open directory and add files in the directory
    if ( !( $handle = opendir( $dir ) ) )
        die("Could not open directory");

    $dir = rtrim( $dir, '/' );      // strip trailing /
    while ($filename = readdir($handle)) 
        if ( is_file( $f = "$dir/$filename" ) )
            if ( ! $zip->addFile( $f, $filename ) )
                die("Error adding file $f to zip as $filename");

    closedir($handle);

    $zip->close();

    return $zipname;
}


/**
 * @return false/null or the directory to zip.
 */
function get_my_directory() {
    // list of the directories you want anyone to be able to download.
    // These are key-value pairs, so we can use the key in our URLs
    // without revealing the real directories.
    $my_directories = array(
       'dir1' => 'path/to/dir1/',
       'dir2' => 'path/to/dir2/',
       'dir3' => 'path/to/dir3/'
    );

    // check if the 'directory' HTTP GET parameter is given:
    if ( ! isset( $_GET['directory'] ) )
        return null;               // it's not set: return nothing
    else
        $dir = $_GET['directory']; // it's set: save it so we don't have
                                   // to type $_GET['directory'] all the time.

    // validate the directory: only pre-approved directories can be downloaded
    if ( ! in_array( $dir, array_keys( $my_directories ) ) )
       return null;                    // we don't know about this directory
    else
       return $my_directories[ $dir ]; // the directory: is 'safe'.
}

Upvotes: 3

Related Questions