Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP ZipArchive Corrupt in Windows

I am using PHP's ZipArchive class to create a zip file containing photos and then serve it up to the browser for download. Here is my code:

/**
 * Grabs the order, packages the files, and serves them up for download.
 *
 * @param string $intEntryID 
 * @return void
 * @author Jesse Bunch
 */
public static function download_order_by_entry_id($intUniqueID) {

    $objCustomer = PhotoCustomer::get_customer_by_unique_id($intUniqueID);

    if ($objCustomer):

        if (!class_exists('ZipArchive')):
            trigger_error('ZipArchive Class does not exist', E_USER_ERROR);
        endif;

        $objZip = new ZipArchive();
        $strZipFilename = sprintf('%s/application/tmp/%s-%s.zip', $_SERVER['DOCUMENT_ROOT'], $objCustomer->getEntryID(), time());

        if ($objZip->open($strZipFilename, ZIPARCHIVE::CREATE) !== TRUE):

            trigger_error('Unable to create zip archive', E_USER_ERROR);

        endif;          

        foreach($objCustomer->arrPhotosRequested as $objPhoto):

            $filename = PhotoCart::replace_ee_file_dir_in_string($objPhoto->strHighRes);
            $objZip->addFile($filename,sprintf('/press_photos/%s-%s', $objPhoto->getEntryID(), basename($filename)));

        endforeach;

        $objZip->close();

        header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($strZipFilename)).' GMT',  TRUE, 200);
        header('Cache-Control: no-cache', TRUE);
        header('Pragma: Public', TRUE);
        header('Expires: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT', TRUE);
        header('Content-Length: '.filesize($strZipFilename), TRUE);
        header('Content-disposition: attachment; filename=press_photos.zip', TRUE);

        header('Content-Type: application/octet-stream', TRUE);

        ob_start();
        readfile($strZipFilename);
        ob_end_flush();
        exit;

    else:

        trigger_error('Invalid Customer', E_USER_ERROR);

    endif;

}

This code works really well with all browsers but IE. In IE, the file downloads correctly, but the zip archive is empty. When trying to extract the files, Windows tells me that the zip archive is corrupt. Has anyone had this issue before?

Edit Update: After suggestion from @profitphp, I changed my headers to this:

header("Cache-Control: public");
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
//header("Content-Description: File Transfer");
//header("Content-type: application/zip");
header("Content-Disposition: attachment; filename=\"pressphotos.zip\"");
//header("Content-Transfer-Encoding: binary");
header("Content-length: " . filesize($strZipFilename));

Also, here is a screenshot of the error in Windows after opening with Firefox:

alt text

This error occurs in both IE and Firefox on Windows. It works fine in Mac. Also, in Windows, the filesize appears to be correct:

alt text

Edit #2 This issue is sovled. See my answer below.

like image 471
Jesse Bunch Avatar asked Jan 06 '11 21:01

Jesse Bunch


4 Answers

I had this same problem, and my solution was similar to the correct answer on this thread. When you put a file in the archive, you can't have absolute files (files starting with a slash) or else it won't open in Windows for some reason.

So got it working not because he (Jesse Bunch, the selected answer at the time of this writing) removed the containing folder but because he removed the starting slash.

I fixed the issue by changing

$zip->addFile($file, $file); // $file is something like /path/to/file.png

to

// we make file relative by removing beginning slash so it will open in Windows
$zip->addFile($file, ltrim($file, '/'));

and then it was able to open in Windows!

That's probably the same reason pclzip (Plahcinski's answer) works. I bet it automatically strips off the beginning slash.

I wouldn't have figured this out without a particular comment on the PHP ZipArchive::addFile documentation page.

like image 171
CWSpear Avatar answered Nov 03 '22 03:11

CWSpear


I recently had a similar issue as you described. I found ZipArchive to be unstable at best.

I solved my problems with this simple library

http://www.phpconcept.net/pclzip

include_once('libs/pclzip.lib.php');

...

function zip($source, $destination){
$zipfile = new PclZip($destination);
$v_list = $zipfile->create($source, '', $source); }

$source = folder i wanted to zip $destination = zip file location

I spent 2 days looking to ZipArchive and then solved all problems with PCLZip in 5 minutes.

Hope this helps you and anyone else having this issue (as this is near the top google result on the issue).

like image 25
Plahcinski Avatar answered Nov 03 '22 04:11

Plahcinski


All of this suggestions may help you, but in my case I need to write an ob_clean(); before first header(''); because some file that I include before print some characters that broken zip file on windows.

$zip=new ZipArchive();
$zip->open($filename, ZIPARCHIVE::CREATE);
$zip->addFile($file_to_attach,$real_file_name_to_attach);
$zip->close();

ob_clean();
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
header('Content-Type: application/x-download');
header('Content-Disposition: attachment; filename="file.zip"');
readfile($filename);
exit;
like image 14
Luca Camillo Avatar answered Nov 03 '22 02:11

Luca Camillo


Ok, after much strife, I figured out the problem. The issue comes from the following line of code:

$objZip->addFile($filename,sprintf('/press_photos/%s-%s', $objPhoto->getEntryID(), basename($filename)));

For some reason, the /press_photos/ part of that path for the local (internal) file name inside the zip archive was causing Windows to think the zip file was corrupt. After modifying the line to look like what is below, Windows opened the zip files correctly. Phew.

$objZip->addFile($filename,sprintf('%s-%s', $objPhoto->getEntryID(), basename($filename)));
like image 6
Jesse Bunch Avatar answered Nov 03 '22 04:11

Jesse Bunch