How to Create ZIP Archive with Files And Download it in Laravel

If you need your users to be able to download multiple files at once, it’s better to create one archive and let them download it. Here’s how to do it in Laravel.

In fact, it’s less about Laravel and more about PHP, we will be using ZipArchive class that existed since PHP 5.2. To use that, make sure your php.ini has enabled extension called ext-zip.


Task 1. Archive user’s invoice from storage/invoices/aaa001.pdf

Here’s the code:

$zip_file = 'invoices.zip'; // Name of our archive to download

// Initializing PHP class
$zip = new \ZipArchive();
$zip->open($zip_file, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);

$invoice_file = 'invoices/aaa001.pdf';

// Adding file: second parameter is what will the path inside of the archive
// So it will create another folder called "storage/" inside ZIP, and put the file there.
$zip->addFile(storage_path($invoice_file), $invoice_file);
$zip->close();

// We return the file immediately after download
return response()->download($zip_file);

That’s it, nothing too difficult, right?


Task 2. Archive all files in a folder storage/invoices

Nothing changes from Laravel side, we will just add some more plain PHP code for iterating the files.

$zip_file = 'invoices.zip';
$zip = new \ZipArchive();
$zip->open($zip_file, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);

$path = storage_path('invoices');
$files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path));
foreach ($files as $name => $file)
{
    // We're skipping all subfolders
    if (!$file->isDir()) {
        $filePath     = $file->getRealPath();

        // extracting filename with substr/strlen
        $relativePath = 'invoices/' . substr($filePath, strlen($path) + 1);

        $zip->addFile($filePath, $relativePath);
    }
}
$zip->close();
return response()->download($zip_file);

We’re done here. You see, you don’t need any Laravel packages to achieve this.

Saying that, if you use Spatie Media Library, they have a special class for it, called MediaStream.

Like our articles?
Check out our Laravel online courses!

9 COMMENTS

  1. Thanks for this, I’ve found myself at Laravel Daily on numerous occasions either solving a problem or learning something new. This is just the most recent and you’re well over due a little thanks from me.

  2. Hello Povilas,

    “$zip->close()” is causing this error:
    “ZipArchive::close(): Failure to create temporary file: Permission denied”

    I also searched it online but couldn’t find a solution. Could you help with it please?
    I am on Ubuntu 18.04, Nginx, PHP7.3 w/ Laravel 5.8.
    Thanks.

  3. A few tips to avoid bad surprises with PHP’s ZipArchive, it is not very well documented at php.net:
    – There is a number of files, not exactly known to me, where an error occurs. In this case the ZIP archive has to be closed and reopened: To solve the problem you have to carry a counter with you and call ->close() and ->open() if the number is exceeded. I use in a derivative of ZipArchive the sequence $this->close(); and $this->open($archiveFilename, self::CREATE | self::OVERWRITE); This limit seems to be about 200 files. To be on the safe side, I assume 100 files.
    – Maybe the following problem also exists on other platforms, I noticed it on Windows Server 2012 R2. It occurs rarely, but still too often: If you don’t get true as a result of ->close() (despite the above described measure), then there is – hard to believe, but it is indeed so – a file with the ZIP target filename, with a file extension of any combination of characters from the range [0-9a-f]. If you rename this file to ….zip, it turns out to be a valid ZIP file that contains everything it should contain. In fact I have not encountered a single exception yet.

  4. Hello I have a problem,
    the file is not creating
    Laravel 5.8
    php 7.3
    I have zip enabled in php ini
    Also write permissions

    I dont know what is happening, but the file is not creating and doesnt show any error

LEAVE A REPLY

Please enter your comment!
Please enter your name here