How to zip a folder and download it using php?

前端 未结 5 2117
伪装坚强ぢ
伪装坚强ぢ 2020-12-15 15:01

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.

相关标签:
5条回答
  • 2020-12-15 15:21

    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

    0 讨论(0)
  • 2020-12-15 15:30

    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
    
    0 讨论(0)
  • 2020-12-15 15:30

    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.

    0 讨论(0)
  • 2020-12-15 15:36

    ========= 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); }
    }
    ?>
    
    0 讨论(0)
  • 2020-12-15 15:45

    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.'/');
            }
        }
    } 
    
    0 讨论(0)
提交回复
热议问题