How to create a ZIP file using PHP and delete it after user downloads it?
Asked Answered
S

9

20

I need to download images from other websites to my server. Create a ZIP file with those images. automatically start download of created ZIP file. once download is complete the ZIP file and images should be deleted from my server.

Instead of automatic download, a download link is also fine. but other logic remains same.

Salience answered 9/4, 2011 at 8:36 Comment(1)
What have your tried so far? Stack Overflow is not here to provide source code for you. You need to try and solve this problem yourself. If you encounter a specific problem/issue, then post a targeted question after first checking to see if another user hasn't already asked the same question.Murmur
W
25

Well, you'll have to first create the zipfile, using the ZipArchive class.

Then, send :

  • The right headers, indicating to the browser it should download something as a zip -- see header() -- there is an example on that manual's page that should help
  • The content of the zip file, using readfile()

And, finally, delete the zip file from your server, using unlink().


Note : as a security precaution, it might be wise to have a PHP script running automatically *(by crontab, typically)*, that would delete the old zip files in your temporary directory.

This just in case your normal PHP script is, sometimes, interrupted, and doesn't delete the temporary file.

Walrath answered 9/4, 2011 at 8:42 Comment(1)
I guess it will crash when user pause the download, but besides that's the best solution I thinkInsnare
M
15
<?php 

Zip('some_directory/','test.zip');

if(file_exists('test.zip')){
    //Set Headers:
    header('Pragma: public');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime('test.zip')) . ' GMT');
    header('Content-Type: application/force-download');
    header('Content-Disposition: inline; filename="test.zip"');
    header('Content-Transfer-Encoding: binary');
    header('Content-Length: ' . filesize('test.zip'));
    header('Connection: close');
    readfile('test.zip');
    exit();
}

if(file_exists('test.zip')){
    unlink('test.zip');

}


function Zip($source, $destination)
{
    if (!extension_loaded('zip') || !file_exists($source)) {
        return false;
    }

    $zip = new ZipArchive();
    if (!$zip->open($destination, ZIPARCHIVE::CREATE)) {
        return false;
    }

    $source = str_replace('\\', '/', realpath($source));

    if (is_dir($source) === true)
    {
        $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);

        foreach ($files as $file)
        {
            $file = str_replace('\\', '/', realpath($file));

            if (is_dir($file) === true)
            {
                $zip->addEmptyDir(str_replace($source . '/', '', $file . '/'));
            }
            else if (is_file($file) === true)
            {
                $zip->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
            }
        }
    }
    else if (is_file($source) === true)
    {
        $zip->addFromString(basename($source), file_get_contents($source));
    }

    return $zip->close();
}

?>
Mia answered 28/5, 2012 at 10:44 Comment(3)
The zip file will never be deleted.Cyclamen
It's not really possible to delete the zip file from inside this code as you don't know when the download will be completed. Really you need to delete the file via a cron job.Albuminate
Can't you read the zip file into a string, and then delete the file? Only for relatively small files of course, but still, it seems like a decent approach.Clause
W
3

Any idea how many zip file downloads get interrupted and need to be continued?

If continued downloads are a small percentage of your downloads, you can delete the zip file immediately; as long as your server is still sending the file to the client, it'll remain on disk.

Once the server closes the file descriptor, the file's reference count will drop to zero, and finally its blocks on disk will be released.

But, you might spent a fair amount of time re-creating zip files if many downloads get interrupted though. Nice cheap optimization if you can get away with it.

Wolfson answered 9/4, 2011 at 8:43 Comment(1)
Thanks, this is good information, really the only information needed to solve the problem. Don't know why not more up votes.Denudation
P
2

Here's how I've been able to do it in the past. This code assumes you've written the files to a path specified by the $path variable. You might have to deal with some permissions issues on your server configuration with using php's exec

 // write the files you want to zip up
file_put_contents($path . "/file", $output);

// zip up the contents
chdir($path);
exec("zip -r {$name} ./");

$filename = "{$name}.zip";

header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.urlencode($filename));
header('Content-Transfer-Encoding: binary');

readfile($filename);
Primulaceous answered 9/4, 2011 at 8:46 Comment(0)
R
2

Other solution: Delete past files before creation new zip file:

    // Delete past zip files script
    $files = glob('*.zip'); //get all file names in array
    $currentTime = time(); // get current time
    foreach($files as $file){ // get file from array
        $lastModifiedTime = filemtime($file); // get file creation time

        // get how old is file in hours:
        $timeDiff = abs($currentTime - $lastModifiedTime)/(60*60);

        //check if file was modified before 1 hour:
        if(is_file($file) && $timeDiff > 1)
            unlink($file); //delete file
    }
Redford answered 16/11, 2017 at 15:19 Comment(1)
Well, what if I myself am downloading a file right now, then create another download, which would delete the file I'm already loading? Or am I missing something here?Judaist
O
1

Enable your php_curl extension; (php.ini),Then use the below code to create the zip. create a folder class and use the code given below:

<?php 
    include("class/create_zip.php");
    $create_zip     =   new create_zip();
    //$url_path,$url_path2 you can use your directory path
            $urls = array(
                 '$url_path/file1.pdf',         
                 '$url_path2/files/files2.pdf'
                 ); // file paths 


            $file_name      =   "vin.zip";   // zip file default name
            $file_folder    =   rand(1,1000000000); // folder with random name
            $create_zip->create_zip($urls,$file_folder,$file_name);  
            $create_zip->delete_directory($file_folder);  //delete random folder 

            if(file_exists($file_name)){
             $temp = file_get_contents($file_name);     
             unlink($file_name); 
            }       

            echo $temp;

    ?>

create a folder class and use the code given below:

<?php

    class create_zip{

        function create_zip($urls,$file_folder,$file_name){

            header('Content-Type: application/octet-stream'); 
            header('Content-Disposition: attachment; filename='.$file_name); 
            header('Content-Transfer-Encoding: binary');

                $mkdir  =   mkdir($file_folder); 

                $zip    = new ZipArchive;
                $zip->open($file_name, ZipArchive::CREATE); 

                foreach ($urls as $url)
                {
                     $path=pathinfo($url);     
                     $path = $file_folder.'/'.$path['basename'];
                     $zip->addFile($path);     
                     $fileopen = fopen($path, 'w');     
                     $init = curl_init($url);     
                     curl_setopt($init, CURLOPT_FILE, $fileopen);     
                     $data = curl_exec($init);     
                     curl_close($init);     
                     fclose($fileopen); 
                } 

                $zip->close();


            }

            function delete_directory($dirname) 
            {
                if (is_dir($dirname))
                $dir_handle = opendir($dirname); 
                if (!$dir_handle)
                return false;
                    while($file = readdir($dir_handle))
                    {
                        if ($file != "." && $file != "..") 
                        {
                            if (!is_dir($dirname."/".$file))             
                            unlink($dirname."/".$file);          
                            else            
                            delete_directory($dirname.'/'.$file);           
                        }    
                    }
                closedir($dir_handle);    
                rmdir($dirname);    
                return true; 
            }



    }

    ?>
Osmious answered 1/7, 2013 at 7:25 Comment(0)
D
1

I went there looking for a similar solution, and after reading the comments found this turnover : before creating your zip file in a dedicated folder (here called 'zip_files', delete all zip you estimate being older than a reasonable time (I took 24h) :

$dossier_zip='zip_files';   
    if(is_dir($dossier_zip))
        {
        $t_zip=$dossier_zip.'/*.zip'; #this allow you to let index.php, .htaccess and other stuffs...
        foreach(glob($t_zip) as $old_zip)
            {
            if(is_file($old_zip) and filemtime($old_zip)<time()-86400)
                {
                unlink($old_zip);
                }
            }

        $zipname=$dossier_zip.'/whatever_you_want_but_dedicated_to_your_user.zip';
        if(is_file($zipname))
            {
            unlink($zipname); #to avoid mixing 2 archives
            }

        $zip=new ZipArchive;
#then do your zip job

By doing so, after 24h you only have the last zips created, user by user. Nothing prevents you for doing a clean by cron task sometimes, but the problem with the cron task is if someone is using the zip archive when the cron is executed it will lead to an error. Here the only possible error is if someone waits 24h to DL the archive.

Disinfection answered 12/11, 2018 at 8:48 Comment(0)
B
0

This code sample and short explanation shows how to delete the zip file after the user has downloaded or cancelled the download.

I am not taking full credit for this answer. Here is a link to the article I copied most of the code from.

Handling zip file download completion and cancellation event

$zip = new ZipArchive();
$zip_file_name = 'your-file.zip';
$zip->open($zip_file_name, ZipArchive::CREATE|ZipArchive::EXCL);

// you can add your file using $zip->addFromString(basename($file_path),  file_get_contents($file_path)); 
// as well as other the $zip->addFile() method.

// when you are done adding your files, simply close the zip.

$zip->close();

// these are the headers we will send to download.
// notice that the Expires header's value is set to true and that of Pragma is set to "no-cache".


header('Content-Type: application/zip');
header('Content-disposition: attachment; filename='.$zip_file_name);
header('Content-Length: ' . filesize($zip_file_name));
header('Pragma: no-cache');
header('Expires: 0');
readfile($zip_file_name);

ob_clean();
ob_flush();

readfile($zip_file_name);

ignore_user_abort(true);
unlink($zip_file_name);

exit();
Buttery answered 8/6, 2023 at 19:13 Comment(0)
P
-1

Firstly, you download images from webiste

then, with the files you have downloaded you creatae zipfile (great tute)

finally you sent this zip file to browser using readfile and headers (see Example 1)

Plumbiferous answered 9/4, 2011 at 8:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.