0

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

Maestro J
  • 1
  • 1

1 Answers1

3

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'.
}
Kenney
  • 9,003
  • 15
  • 21
  • Honestly I dont know the first thing about php so what you are saying is like a different language to me not meaning to be disrespectful or anything. So the code you pasted do I just add it as a php file. Also the link/button would be in articles that I create in joomla so how do i put these links in the articles pointing to that php file. – Maestro J Nov 05 '15 at 22:46
  • ok so I tried it went to mysite/batchdownload.php?directory=dir1. All I got was a 0 byte file for download with "_your.zip_". i should add that I do not want anyone to download the files maybe they can be encrypted or something. Here is the code I added. Sorry couldnt post it as it said too many characters in the comment box. http://drive.google.com/open?id=0B1pK8Pv1aiBuVjNwR3ZwRjdzQkE – Maestro J Nov 06 '15 at 01:58
  • I'm sorry, I can't open that. Several reasons it might have failed: the directory you specified in the script at the line with `'dir1' => '....',` doesn't exist, or, your server doesn't allow executing shell commands from PHP (If this script doesn't produce any output then that's the case: ` – Kenney Nov 06 '15 at 15:02
  • Hey Kenney. Thanks for the reply. I am in the process of using a different host. (Using Godaddy) shared hosting atm so not sure if they allow for executing shell commands. Do you know if they do? Ive got a solution for not allowing everyone to download using an extension. Just need to get the script to work now. Can i try to send you the php file using another link. Appreciate the help. – Maestro J Nov 11 '15 at 20:00
  • This is a mediafire lin hope im not breaking any rules. http://www.mediafire.com/view/2x6b2nfx31kjuae/batchdownload.php – Maestro J Nov 11 '15 at 20:02
  • I added a complete (tested) script with ZipArchiver - that should work anywhere. – Kenney Nov 11 '15 at 22:25
  • Great it works. I just realised one thing tho. I have hundreds of directories I will allow for download. For eg each article will have a link/button with the option to batch download a number of files from a specific directory/folder using the (mysite/batchdownload.php?directory=dir1) , which means i'd have to enter each of the 'dir1' => '....', for each directory/folder. Is there a more suitable way to do that? Thanks – Maestro J Nov 12 '15 at 15:01
  • Sure. I made it that way so visitors wouldn't be able to download just any directory (such as configuration files with database passwords etc..). From your example it looks like your directories are all in the web root. If you move them to a `media/` directory, you could pass the relative path (f.e. `mysite/batchdownload.php?directory=Riddims/1998 Riddims/Sleng Teng Riddim 1998/`), and replace the get_my_directory function. Have to go now but i'll post an update later. – Kenney Nov 12 '15 at 15:10
  • Hey Kenny just tried the new script. Got this error message (Parse error: syntax error, unexpected '=', expecting ')' in /home/xxxxx/public_html/download.php on line 6). My directory layout is as follows. media/Riddims/1998 Riddims/Sleng Teng Riddim 1998. I used (mysite/download.php?directory=Riddims/1998 Riddims/Sleng Teng Riddim 1998/)... Any idea why I got the error message. Thanks – Maestro J Nov 12 '15 at 21:36
  • Ah yes, you're probably running an old PHP version (< 5.4). I think you can change that in cPanel somewhere. Better set it to 5.4 or newer. Here's an updated [pastebin](http://pastebin.com/upJXtYks) that should work, though. – Kenney Nov 12 '15 at 21:50
  • Hey Kenney sorry to bother u again. I tried the script and it works fine. Thanks fo that. Only problem I have now is that anyone can download other files in that "media" folder. The extension I use, I was hoping it would stop that but it doesnt. Is there a way to sort of encrypt or hide those links (mysite/batchdownload.php?directory=Riddims/1998 Riddims/Sleng Teng Riddim 1998/). So when the user hovers it doesnt show in the status bar or if the user tries to view the source code? Thanks – Maestro J Nov 18 '15 at 20:33
  • No, there isn't. What you could do instead, is not show the links if the user doesn't have permission. Maybe you know of a way to only show certain pages in Joomla if a user is logged in, or has a certain role or permission? That would hide the links, but they would still be accessible for anyone. If you want to manage authorization using Joomla, I can't help you - I don't know. But, there is another way: see [this so post](http://stackoverflow.com/questions/5229656/password-protecting-a-directory-and-all-of-its-subfolders-using-htaccess). – Kenney Nov 18 '15 at 21:28
  • Thanks for response. I do have an extension that restricts access to the articles but once a registered/subscribed user has access to those he can then access them even after his membership runs out. Was hoping there was another way to get the zips working and protect the directories. – Maestro J Nov 19 '15 at 12:38
  • Final thing. I realise that the zip file that is created, if it isnt downloaded, for eg the user cancels the download it doesnt get deleted from the server (I have to do it manually). Any way of getting it to delete it like say after a while or something? Thanks – Maestro J Nov 19 '15 at 17:15
  • Yes; I updated the bottom snippet by registering a shutdown function to delete the file. Now the file will be deleted when the script terminates. – Kenney Nov 19 '15 at 17:19
  • which one is it? is it this link (http://pastebin.com/iAjcUq3T) cause code looks the same as previous! – Maestro J Nov 19 '15 at 19:13
  • The part between `$zipfile = make_zip( $rp );` (line 13 in your pastebin) and `function make_zip( $dir )` (line 20) has changed. – Kenney Nov 19 '15 at 19:39
  • got that error again.. syntax error, unexpected '=', expecting ')' line 6 – Maestro J Nov 19 '15 at 22:27
  • There aren't any in my code. Can't you fix this yourself? – Kenney Nov 19 '15 at 22:31
  • Im a rookie when it comes to php and at the moment Im in the process of working on other aspects of the site so I cant really learn php right now. The error i got was the same one from above. I believe u changed something in it before n it worked. – Maestro J Nov 19 '15 at 23:07