简体   繁体   中英

How to zip a folder and download it using php?

I have folder named "data". This "data" folder contains a file "filecontent.txt" and another folder named "Files". The "Files" folder contains a "info.txt" file. So it is a folder inside folder structure.

I have to zip this folder "data"(using php) along with the file and folder inside it, and download the zipped file.

I have tried the examples available at http://www.php.net/manual/en/zip.examples.php These examples did not work. My PHP version is 5.2.10

Please help.

I have written this code.

<?php
$zip = new ZipArchive;
if ($zip->open('check/test2.zip',ZIPARCHIVE::CREATE) === TRUE) {
    if($zip->addEmptyDir('newDirectory')) {
        echo 'Created a new directory';
    } else {
        echo 'Could not create directory';
    }
    $zipfilename="test2.zip";
    $zipname="check/test2.zip";

    header('Content-Type: application/zip');
    header('Content-disposition: attachment; filename=check/test1.zip');    //header('Content-Length: ' . filesize( $zipfilename));
    readfile($zipname);  //$zip->close(); } else { echo failed';
}
?>

file downloaded but could not unzip

You need to recursively add files in the directory. Something like this (untested):

function createZipFromDir($dir, $zip_file) {
    $zip = new ZipArchive;
    if (true !== $zip->open($zip_file, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)) {
        return false;
    }
    zipDir($dir, $zip);
    return $zip;
}

function zipDir($dir, $zip, $relative_path = DIRECTORY_SEPARATOR) {
    $dir = rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
    if ($handle = opendir($dir)) {
        while (false !== ($file = readdir($handle))) {
            if (file === '.' || $file === '..') {
                continue;
            }
            if (is_file($dir . $file)) {
                $zip->addFile($dir . $file, $file);
            } elseif (is_dir($dir . $file)) {
                zipDir($dir . $file, $zip, $relative_path . $file);
            }
        }
    }
    closedir($handle);
}

Then call $zip = createZipFromDir('/tmp/dir', 'files.zip');

For even more win I'd recommend reading up on the SPL DirectoryIterator here

========= The only solution for me ! ! !==========

Puts all subfolders and sub-files with their structure:

<?php
$the_folder = 'path/foldername';
$zip_file_name = 'archived_name.zip';


$download_file= true;
//$delete_file_after_download= true; doesnt work!!


class FlxZipArchive extends ZipArchive {
    /** Add a Dir with Files and Subdirs to the archive;;;;; @param string $location Real Location;;;;  @param string $name Name in Archive;;; @author Nicolas Heimann;;;; @access private  **/

    public function addDir($location, $name) {
        $this->addEmptyDir($name);

        $this->addDirDo($location, $name);
     } // EO addDir;

    /**  Add Files & Dirs to archive;;;; @param string $location Real Location;  @param string $name Name in Archive;;;;;; @author Nicolas Heimann
     * @access private   **/
    private function addDirDo($location, $name) {
        $name .= '/';
        $location .= '/';

        // Read all Files in Dir
        $dir = opendir ($location);
        while ($file = readdir($dir))
        {
            if ($file == '.' || $file == '..') continue;
            // Rekursiv, If dir: FlxZipArchive::addDir(), else ::File();
            $do = (filetype( $location . $file) == 'dir') ? 'addDir' : 'addFile';
            $this->$do($location . $file, $name . $file);
        }
    } // EO addDirDo();
}

$za = new FlxZipArchive;
$res = $za->open($zip_file_name, ZipArchive::CREATE);
if($res === TRUE) 
{
    $za->addDir($the_folder, basename($the_folder));
    $za->close();
}
else  { echo 'Could not create a zip archive';}

if ($download_file)
{
    ob_get_clean();
    header("Pragma: public");
    header("Expires: 0");
    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
    header("Cache-Control: private", false);
    header("Content-Type: application/zip");
    header("Content-Disposition: attachment; filename=" . basename($zip_file_name) . ";" );
    header("Content-Transfer-Encoding: binary");
    header("Content-Length: " . filesize($zip_file_name));
    readfile($zip_file_name);

    //deletes file when its done...
    //if ($delete_file_after_download) 
    //{ unlink($zip_file_name); }
}
?>

I had to do the same thing a few days ago and here is what I did.

1) Retrieve File/Folder structure and fill an array of items. Each item is either a file or a folder, if it's a folder, retrieve its content as items the same way.

2) Parse that array and generate the zip file.

Put my code below, you will of course have to adapt it depending on how your application was made.

// Get files
$items['items'] = $this->getFilesStructureinFolder($folderId);

$archiveName = $baseDir . 'temp_' . time(). '.zip';

if (!extension_loaded('zip')) {
    dl('zip.so');
}

//all files added now
$zip = new ZipArchive();
$zip->open($archiveName, ZipArchive::OVERWRITE);

$this->fillZipRecursive($zip, $items);

$zip->close();

//outputs file
if (!file_exists($archiveName)) {
    error_log('File doesn\'t exist.');
    echo 'Folder is empty';
    return;
}


header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private", false);
header("Content-Type: application/zip");
header("Content-Disposition: attachment; filename=" . basename($archiveName) . ";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . filesize($archiveName));
readfile($archiveName);

//deletes file when its done...
unlink($archiveName);

Methods used to fill & parse:

/**
 * 
 * Gets all the files recursively within a folder and keeps the structure.
 * 
 * @param   int     $folderId   The id of the folder from which we start the search
 * @return  array   $tree       The data files/folders data structure within the given folder id
 */
public function getFilesStructureinFolder($folderId) {
    $result = array();

    $query = $this->db->query('SELECT * FROM xx WHERE deleted = 0 AND status = 1 AND parent_folder_id = ? ORDER BY name ASC', $folderId);

    $folders = $query->result();

    foreach($folders as $folder) {
        $folderItem = array();
        $folderItem['type']     = 'folder';
        $folderItem['obj']      = $folder;  
        $folderItem['items']    = $this->getFilesStructureinFolder($folder->id);
        $result[]               = $folderItem;
    }

    $query = $this->db->query('SELECT * FROM xx WHERE deleted = 0 AND xx = ? AND status = 1 ORDER BY name ASC', $folderId);

    $files = $query->result();

    foreach ($files as $file) {
        $fileItem = array();
        $fileItem['type']   = 'file';
        $fileItem['obj']    = $file;    
        $result[]           = $fileItem;
    }

    return $result;
}

/**
 * Fills zip file recursively
 * 
 * @param ZipArchive    $zip        The zip archive we are filling
 * @param Array         $items      The array representing the file/folder structure
 * @param String        $zipPath    Local path within the zip
 * 
 */
public function fillZipRecursive($zip, $items, $zipPath = '') {
    $baseDir = $this->CI->config->item('xxx');

    foreach ($items['items'] as $item) {

        //Item is a file
        if ($item['type'] == 'file') {
            $file = $item['obj'];
            $fileName = $baseDir . '/' . $file->fs_folder_id . '/' . $file->file_name;

            if (trim($file->file_name) == '' || !file_exists($fileName))
                continue;

            $zip->addFile($fileName, $zipPath.''.$file->file_name);
        }

        //Item is a folder
        else if ($item['type'] == 'folder') {
            $folder     = $item['obj'];

            $zip->addEmptyDir($zipPath.''.$folder->name);

            //Folder probably has items in it!
            if (!empty($item['items']))
                $this->fillZipRecursive($zip, $item, $zipPath.'/'.$folder->name.'/');
        }
    }
} 

See the linked duplicates. Another often overlooked and particular lazy option would be:

exec("zip -r data.zip data/");
header("Content-Type: application/zip");
readfile("data.zip");    // must be a writeable location though

Use the TbsZip class to create a new zip Archive. TbsZip is simple, it uses no temporary files, no zip EXE, it has no dependency, and has a Download feature that flushes the archive as a download file.

You just have to loop under the folder tree and add all files in the archive, and then flush it.

Code example:

$zip = new clsTbsZip(); // instantiate the class
$zip->CreateNew(); // create a virtual new zip archive
foreach (...) { // your loop to scann the folder tree
  ...
  // add the file in the archive
  $zip->FileAdd($FileInnerName, $LocalFilePath, TBSZIP_FILE);
}
// flush the result as an HTTP download
$zip->Flush(TBSZIP_DOWNLOAD, 'my_archive.zip');

Files added in the archive will be compressed sequentially during the Flush() method. So your archive can contain numerous sub-files, this won't increase the PHP memory.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM