3

I need to download a zip file from a website as I am going to require to coalesce multiple files in a single download (up to 100 individual files-ish).

When attempting to create the zip file, it downloads as intended, the file name appears in the format "YYYY.MM.DD - HH.MM.SS" also as intended. My issue occurs when attempting to open the zip file in windows 7 (or winzip) - I get the error message below. This happens repeatedly from multiple attempts.

I assume that I have made an error while coding the creation or download of the zip file, rather than the zip file format itself being an issue as I can open different zip files - can anyone see the mistake I have probably made? (code included below error image)

I have attempted to use Download multiple files as a zip-file using php as a reference.

//backup file name based on current date and time
$filename = date("Y.m.j - H.i.s");
//name of zip file used when downloading
$zipname = 'temp.zip';
//create new zip file
$zip = new ZipArchive;
$zip->open($zipname, ZipArchive::CREATE);
//yes, I know the zip file is empty currently - I've cut the code from here for
//now as the zip file doesn't function with / without it currently
$zip->close();

//download file from temporary file on server as '$filename.zip'
header('Content-Type: application/zip');
header('Content-disposition: attachment; filename='.$filename.'.zip');
header('Content-Length: ' . filesize($zipname));
readfile($zipname);
5
  • Have you ever looked at the ZIP file you got with a HEX editor? If not, please do so and tell us what the first FOUR bytes are. Commented Nov 13, 2013 at 20:15
  • Please show also adding of sample content. Your code looks ok so far
    – hek2mgl
    Commented Nov 13, 2013 at 20:16
  • I suspect one of the following two reasons. a) Some PHP error occurs and is printed to stdout, so it is included in the ZIP file (which, of course, becomes corrupted, as there shouldn't be random characters from PHP errors inside it :D). b) EIther PHP or the webserver adds another layer of GZIP ontop of it, because of mod_deflate or similar. The headers don't respect this however, therefore the ZIP file is not "un-gzip'd" by the browser, and therefore corrupted. Commented Nov 13, 2013 at 20:21
  • 4
    I spent a long time searching for a similar problem with image files. My solution was to add ob_clean(); immediately before starting to output the new headers.
    – user1864610
    Commented Nov 13, 2013 at 20:46
  • @Mike W: You're a special kind of awesome. I wish I knew this earlier. At one point, after upgrading the server to Ubuntu 13.10, this became an issue for me. Using ob_clean(), the final zip file becomes about 10kb bigger and it works agian. I have no idea why. But I can forget about it now.
    – Redsandro
    Commented Jan 13, 2014 at 10:32

6 Answers 6

7

Try to open the zip file with a text editor. This way you can check if were is a php error in your code (during the compression step).

1
  • Well done, all the answers do not work unless you solve this step first! Commented May 17 at 17:12
3

Check that the web server user has write permission to the folder where you're creating the ZIP file. Notwithstanding the documentation, ZipArchive::open() will fail silently and return true (i.e. success) if it cannot create the ZIP file. Further, ZipArchive::addFile() will seemingly add as many files as you wish to this non-existent archive, also without reporting an error. The first point at which an error appears is when ZipArchive::close() returns `false'. No error messages appear in the error logs, either.

Readfile() will report an error to the logs and fail, so the result is a zero-length ZIP file on your local hard disk.

The reason seems to be that the ZipArchive class is only assembling a list of files in memory until it's closed, at which point it assembles all the files into the Zip file. If this can't be done then ZipArchive::close() returns false.

Note: if the zip file is empty, it might not be created at all! Your download will proceed, but readfile() will fail and you'll get a zero-length ZIP file downloaded.

What to do?

Add a little error checking to your code to report some of this:

$zip = new ZipArchive;
$zip->open($zipname, ZipArchive::CREATE);

// Add your files here

if ($zip->close() === false) {
   exit("Error creating ZIP file");
};


//download file from temporary file on server as '$filename.zip'
if (file_exists($zipname)) {

    header('Content-Type: application/zip');
    header('Content-disposition: attachment; filename='.$filename.'.zip');
    header('Content-Length: ' . filesize($zipname));
    readfile($zipname);
} else {
    exit("Could not find Zip file to download");
}
1
  • For some reason all the content within the zip file wasn't being included. Due to this a 0 byte size zip file was being downloaded as you suggested. Adding the error-checking you suggested made this clear the zip wasn't actually existing on the server prior to the download! Thank you very much for the fix! I knew it would be something silly I've done! :')
    – Justice
    Commented Nov 13, 2013 at 23:54
2

I had this issue, but this solution fixed it for me

Add ob_clean(); just before your new output headers.

I am using an older version of Silverstripe and my zip files were constantly being corrupted even though the data was visibly in there.

The solution of this obscure comment above to use ob_clean(); was helpful, and I wanted to pull this out as an Answer to this question.

From PHP docs:
ob_clean(); discards the contents of the output buffer.

ob_clean(); does not destroy the output buffer like ob_end_clean() does.

1

try to add ob_end_clean(); before headers

    <?php
$zip = new ZipArchive();
$zip_name = $name.".zip"; // Zip name
$tmpFile=APPPATH.'../'.$zip_name;
$zip->open($zip_name,  ZipArchive::CREATE);
foreach ($images as $files) {
  if(!empty($files->cloudinary_img_url)){
  $zip->addFromString(basename($files->car_images),  file_get_contents(getImagesDropbox($files->cloudinary_img_url)));  
 
  }
  else{
   echo"file does not exist";
  }
}  
$zip->close();
 ob_end_clean();
header('Content-Type: application/zip');
header('Content-disposition: attachment; filename="'.$zip_name.'"');
header('Content-Length: ' . filesize($tmpFile));
readfile($tmpFile);
unlink($tmpFile);
  ?>
0

In my case this worked

put this at the beginning of the php file

ob_start();

then, after the headers and immediately before the readfile put this

while (ob_get_level()) {
    ob_end_clean();
}

wrapping up

ob_start();//this is new
//backup file name based on current date and time
$filename = date("Y.m.j - H.i.s");
//name of zip file used when downloading
$zipname = 'temp.zip';
//create new zip file
$zip = new ZipArchive;
$zip->open($zipname, ZipArchive::CREATE);
//yes, I know the zip file is empty currently - I've cut the code from here for
//now as the zip file doesn't function with / without it currently
$zip->close();

//download file from temporary file on server as '$filename.zip'
header('Content-Type: application/zip');
header('Content-disposition: attachment; filename='.$filename.'.zip');
header('Content-Length: ' . filesize($zipname));
while (ob_get_level()) {//this is new
  ob_end_clean();//this is new
}//this is new
readfile($zipname);
0
$zip->close();
ob_clean(); // <-- this is the asnwer

header('Content-Type: application/zip');
header('Content-disposition: attachment; filename='.$nombreDir.'.zip');
header('Content-Length: ' . filesize($directorioCreado . '.zip'));
header('cache-control: no-cache, must-revalidate');
header('Expires: 0');
readfile($directorioCreado . '.zip');
2
  • Hello, please add any info about why you think this is the optimal solution.
    – Destroy666
    Commented Sep 10, 2023 at 20:52
  • Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
    – Community Bot
    Commented Sep 11, 2023 at 8:41

Not the answer you're looking for? Browse other questions tagged or ask your own question.