Fix for optimized php zip class that now works on windows and mac

Hi,

I was using the php zip class of Rochak Chauhan for a while but because of the high memory usage I switched to an optimized version of this class from ironhawk.

Unfortunately both implementations where creating zip files that where not working properly on mac. I compared the implementations against some other classes and found out that a section what printed twice which causes mac to fail.
the other zip implementation also where using proper time stamps and I merged this two implementation and added some stuff I needed. So the code below has the following differences to the original implementations:

  • The created zip files where not working on Mac. Now they do. A couple of elements where added twice!
  • It also does not require php 5 anymore. It works also fine on php 4.
  • Compression can be turned off to get more speed if this is important for you.
  • I also added the patch from Peter Listiak <mlady@users.sourceforge.net> for last modified date and time of the compressed file.

The code I used is based on an older class then the one currently published on phpclasses.org. The newer one do also contain directory functions. You would have to merge this if you need them.

You can download the example below here:  zip_class.zip

<?php
/**
* Original source was created by Rochak Chauhan, www.rochakchauhan.com.
* But it used a lot of memory so it was modified.
* The original code kept the continuesly growing .zip stream in memory and in addition
* adding a new file to the stream required filesize*4 bytes.

* Now the growing .zip stream is not kept in memory, it is written to the file continuesly.
* Adding a new file to the .zip requires filesize*2 bytes of memory.
* Plus some more memory required to store the .zip entries -
* this is written only at the end of the process.
*
* 20.2.2011: Fixed by Michael Dempfle (www.tinywebgallery.com).
*            - The created zip files do now work on Mac.
*            - It also does not require php 5 anymore. It works also fine on php 4.
*            - Compression can be turned off to get more speed if this is important for you.
*            - I also added the patch from Peter Listiak <mlady@users.sourceforge.net>
*              for last modified date and time of the compressed file.
*
* This code based on an older class then the one currently published on phpclasses.org.
* The newer one do also contain
* directory functions. You would have to merge this if you need them.
*
* @author ironhawk, Rochak Chauhan  www.rochakchauhan.com
* @package zip
*/
class ZipFile {

var $centralDirectory = array(); // central directory
var $endOfCentralDirectory = "x50x4bx05x06x00x00x00x00"; //end of Central directory record
var $oldOffset = 0;

var $fileHandle;
var $compressedDataLength = 0;

function unix2DosTime($unixtime = 0) {
$timearray = ($unixtime == 0) ? getdate() : getdate($unixtime); if ($timearray['year'] < 1980) {
$timearray['year'] = 1980;
$timearray['mon'] = 1;
$timearray['mday'] = 1;
$timearray['hours'] = 0;
$timearray['minutes'] = 0;
$timearray['seconds'] = 0; }
return (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) | ($timearray['hours'] << 11) | ($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1);
}

/**
* Creates a new ZipFile object
*
* @param resource $_fileHandle file resource opened using fopen() with "w+" mode
* @return ZipFile
*/
function ZipFile($_fileHandle)
{
$this->fileHandle = $_fileHandle;
}

/**
* Adds a new file to the .zip in the specified .zip folder - previously created using addDirectory()!
*
* @param string $directoryName full path of the previously created .zip folder the file is inserted into
* @param string $filePath full file path on the disk
* @return void
*/
function addFile($filePath, $directoryName, $doCompress = true)   {

// reading content into memory
$data = file_get_contents($filePath);

// adding the time
$time = 0;
$dtime = dechex($this->unix2DosTime($time));
$hexdtime = 'x' . $dtime[6] . $dtime[7] . 'x' . $dtime[4] . $dtime[5] . 'x' . $dtime[2] . $dtime[3] . 'x' . $dtime[0] . $dtime[1];
eval('$hexdtime = "' . $hexdtime . '";');

// create some descriptors
$directoryName = str_replace("", "/", $directoryName);
$feedArrayRow = "x50x4bx03x04";
$feedArrayRow .= "x14x00";
$feedArrayRow .= "x00x00";
$feedArrayRow .= "x08x00";
$feedArrayRow .= $hexdtime;
$uncompressedLength = strlen($data);

// compression of the data
$compression = crc32($data);
// at this point filesize*2 memory is required for a moment but it will be released immediatelly
// once the compression itself done
// compression does not work with mac - I use the compression only to download multiple file so I skip it!
if ($doCompress) {
$data = gzcompress($data);
}
// manipulations
$data = substr($data, 2, strlen($data) - 6);

// writing some info
$compressedLength = strlen($data);
// Compression does not work with mac
if ($doCompress) {
$feedArrayRow .= pack("V",$compression);
$feedArrayRow .= pack("V",$compressedLength);
$feedArrayRow .= pack("V",$uncompressedLength);
}
$feedArrayRow .= pack("v", strlen($directoryName) );
$feedArrayRow .= pack("v", 0 );
$feedArrayRow .= $directoryName;
fwrite($this->fileHandle, $feedArrayRow);
$this->compressedDataLength += strlen($feedArrayRow);

// writing out the compressed content
fwrite($this->fileHandle, $data);
$this->compressedDataLength += $compressedLength;

// some more info...
// The part below cause the mac to fail!
//   $feedArrayRow = pack("V",$compression);
//   $feedArrayRow .= pack("V",$compressedLength);
//   $feedArrayRow .= pack("V",$uncompressedLength);
//fwrite($this->fileHandle, $feedArrayRow);
//$this->compressedDataLength += strlen($feedArrayRow);
$newOffset = $this->compressedDataLength;

// adding entry
$addCentralRecord = "x50x4bx01x02";
$addCentralRecord .="x00x00";
$addCentralRecord .="x14x00";
$addCentralRecord .="x00x00";
$addCentralRecord .="x08x00";
$addCentralRecord .= $hexdtime;
$addCentralRecord .= pack("V",$compression);
$addCentralRecord .= pack("V",$compressedLength);
$addCentralRecord .= pack("V",$uncompressedLength);
$addCentralRecord .= pack("v", strlen($directoryName) );
$addCentralRecord .= pack("v", 0 );
$addCentralRecord .= pack("v", 0 );
$addCentralRecord .= pack("v", 0 );
$addCentralRecord .= pack("v", 0 );
$addCentralRecord .= pack("V", 32 );
$addCentralRecord .= pack("V", $this->oldOffset );
$this->oldOffset = $newOffset;
$addCentralRecord .= $directoryName;
$this->centralDirectory[] = $addCentralRecord;

}

/**
* Close the .zip - we do not add more stuff
*
* @param boolean $closeFileHandle if true the file resource will be closed too
*/
function close($closeFileHandle = true) {

$controlDirectory = implode("", $this->centralDirectory);

fwrite($this->fileHandle, $controlDirectory);
fwrite($this->fileHandle, $this->endOfCentralDirectory);
fwrite($this->fileHandle, pack("v", sizeof($this->centralDirectory)));
fwrite($this->fileHandle, pack("v", sizeof($this->centralDirectory)));
fwrite($this->fileHandle, pack("V", strlen($controlDirectory)));
fwrite($this->fileHandle, pack("V", $this->compressedDataLength));
fwrite($this->fileHandle, "x00x00");

if($closeFileHandle)
fclose($this->fileHandle);
}
}
?>

The usage is really simple:

<?php
/**
*  Example how to create the zip.
*
*  Thanks to Rochak Chauhan for the initial implementation and
*  Attila Wind for the memory optimization.
*  I only fixed and extended the class. So the main credits goes to the guys above.
*
*  The TWG Flash Uploader is using this class as well.
*
* 20.2.2011: Fixed by Michael Dempfle (www.tinywebgallery.com).
*            - The created zip files where not working on Mac. Now they do. A couple of elements where added twice!
*            - It also does not require php 5 anymore. It works also fine on php 4.
*            - Compression can be turned off to get more speed if this is important for you.
*            - I also added the patch from Peter Listiak <mlady@users.sourceforge.net>
*              for last modified date and time of the compressed file.
*
*/

include("zip.class.php");
$fileonserver = "filename.txt";
$filename = "example.zip";
$fd = fopen ($filename, "wb");
$createZip = new ZipFile($fd);
$createZip -> addFile($fileonserver, $fileonserver, true);
$createZip -> close();

?>

 

This new zip class is also included in TFU 2.13.

Hope this helps someone ;).

Have fun using TFU,

Michael

Leave a Reply