Search code examples
phplaraveldownloadhttp-headersphp-ziparchive

Laravel Zip Archive not downloading in browser


I am trying to zip a bunch of PDFs together and download in the browser, at the moment the PDF files are zipped and downloaded to the folder the PDFs are stored in, not via the users browser and into their download folder, I have a similar (and much simpler) function which downloads a single PDF so feel like I'm missing something fairly obvious here..

$id is a comma seperated list of filenames, this is then split into an array for looping through and adding to zip file. this bit works, thinking it may be a header issue or with the response.

Any help much appreciated.

public function downloadMultiple($id) {

    $id_array = explode(',', $id);

    $public_dir = storage_path();
    $zipFileName = time().'.zip';

    $zip = new ZipArchive;

    if ($zip->open($public_dir . '/' . $zipFileName, ZipArchive::CREATE) === TRUE) {

        foreach($id_array as $file) {

            $file_path = storage_path($file).".pdf";

            if (file_exists($file_path)) {
                $zip->addFile($file_path,$file.".pdf");
            }
        }

        if ($zip->close()) {
            $filetopath = $public_dir.'/'.$zipFileName;

            $headers = [
                'Cache-control: maxage=1',
                'Pragma: no-cache',
                'Expires: 0',
                'Content-Type : application/octet-stream',
                'Content-Transfer-Encoding: binary',
                'Content-Type: application/force-download',
                'Content-Disposition: attachment; filename='.time().'.zip',
                "Content-length: " . filesize($filetopath)
            ];

            if (file_exists($filetopath)) {
                $response = response()->download($filetopath, $zipFileName, $headers);
                //$response->deleteFileAfterSend(true);
            } else {
                return ['status'=>'zip file does not exist'];
            }

        } else {
            return ['status'=>'zip file could not close'];
        }

    } else {
        return ['status'=>'Could not create new zip'];
    }
}

Update: Definitely gets to the return and does create the file, it just doesn't seem to download for the user, the below is what is brought back in the inspector so clearly something not working as expected

enter image description here

may be worth while mentioning the code which is sent to the controller

let xhr = new XMLHttpRequest(),  self = this;
             xhr.open('GET', window.location.origin+'/download-multiple/' + this.selected);
             xhr.onload = function () {
             };
             xhr.send();

Solution

  • Assuming you're getting to that part of the controller method, I believe the problem is that you're not returning your response:

    if (file_exists($filetopath)) {
        // $response = response()->download($filetopath, $zipFileName, $headers);
        // $response->deleteFileAfterSend(true);
        return response()->download($filetopath, $zipFileName, $headers)->deleteFileAfterSend(true);
    } else {
        return ['status'=>'zip file does not exist'];
    }
    

    EDIT: The problem is you're trying to load the file via AJAX, which you can't do the way that you're trying to do it (see here for examples on how to do it). Change your javascript to:

    let xhr = new XMLHttpRequest(),  self = this;
    window.location = window.location.origin+'/download-multiple/' + this.selected