Search code examples
phplaraveldirectoryzip

Laravel ZIP Directory and sub-directories


I have this code, but it only zips files, is it possible to include all the directories and files inside as well? The zipped file should match the folder structure containing the following: (like a normal operation of zipping a folder on your PC)

- Documents
-- test.pdf
-- file.pdf
- Images
-- test.png
- mainfile.xsl

The code as follows: (Taken from a different stack solution)

    // Create a list of files that should be added to the archive.
    $files = glob(storage_path("app/course/*.*"));

    // Define the name of the archive and create a new ZipArchive instance.
    $archiveFile = storage_path("app/course/files.zip");
    $archive = new ZipArchive();

    // Check if the archive could be created.
    if ($archive->open($archiveFile, ZipArchive::CREATE | ZipArchive::OVERWRITE)) {
        // Loop through all the files and add them to the archive.
        foreach ($files as $file) {
            if ($archive->addFile($file, basename($file))) {
                // Do something here if addFile succeeded, otherwise this statement is unnecessary and can be ignored.
                continue;
            } else {
                throw new Exception("File [`{$file}`] could not be added to the zip file: " . $archive->getStatusString());
            }
        }

        // Close the archive.
        if ($archive->close()) {
            // Archive is now downloadable ...
            return response()->download($archiveFile, basename($archiveFile))->deleteFileAfterSend(true);
        } else {
            throw new Exception("Could not close zip file: " . $archive->getStatusString());
        }
    } else {
        throw new Exception("Zip file could not be created: " . $archive->getStatusString());
    }

An alternative code is acceptable as well, if it achieves the result.


Solution

  • I created such functionality recently (it should even work when there are symlinks in your source directory).

    1. I create the archive file
    2. In the creation process, I add the content fo a directory to the archive
    3. Finally, I close the archive.
        /**
         * @throws RuntimeException If the file cannot be opened
         */
        public function create()
        {
            $filePath = 'app/course/files.zip';
            $zip = new \ZipArchive();
        
            if ($zip->open($filePath, \ZipArchive::CREATE) !== true) {
                throw new \RuntimeException('Cannot open ' . $filePath);
            }
        
            $this->addContent($zip, realpath('app/course'));
            $zip->close();
        }
    
    
        /**
         * This takes symlinks into account.
         *
         * @param ZipArchive $zip
         * @param string     $path
         */
        private function addContent(\ZipArchive $zip, string $path)
        {
            /** @var SplFileInfo[] $files */
            $iterator = new \RecursiveIteratorIterator(
                new \RecursiveDirectoryIterator(
                    $path,
                    \FilesystemIterator::FOLLOW_SYMLINKS
                ),
                \RecursiveIteratorIterator::SELF_FIRST
            );
        
            while ($iterator->valid()) {
                if (!$iterator->isDot()) {
                    $filePath = $iterator->getPathName();
                    $relativePath = substr($filePath, strlen($path) + 1);
        
                    if (!$iterator->isDir()) {
                        $zip->addFile($filePath, $relativePath);
                    } else {
                        if ($relativePath !== false) {
                            $zip->addEmptyDir($relativePath);
                        }
                    }
                }
                $iterator->next();
            }
        }