Jump to content

Zipping files on demand


weee

Recommended Posts

Hello Everyone,

 

I seem to have stumbled upon something that looks like it is next to impossible in PHP; zipping files/folders on demand in php :P

 

So, after doing some extensive google searching, I have found various functions; some work only zipping files, other have no comments, some just don't work at all, etc.  After looking at multiple projects, I have finally compiled something that works and has some sort of comments on what the heck is going on.

<?php
class zipfile
{
/**
     * Array to store compressed data
     *
     * @var  array    $datasec
     */
    var $datasec      = array();

    /**
     * Central directory
     *
     * @var  array    $ctrl_dir
     */
    var $ctrl_dir     = array();

    /**
     * End of central directory record
     *
     * @var  string   $eof_ctrl_dir
     */
    var $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00";

    /**
     * Last offset position
     *
     * @var  integer  $old_offset
     */
    var $old_offset   = 0;

 /**
  * Add a directory
  *
  * @var	string	$name
  */
function add_dir($name) {
	$name = str_replace("", "/", $name);
	$fr = "\x50\x4b\x03\x04";
	$fr .= "\x0a\x00";
	$fr .= "\x00\x00";
	$fr .= "\x00\x00";
	$fr .= "\x00\x00\x00\x00";
	$fr .= pack("V",0);
	$fr .= pack("V",0);
	$fr .= pack("V",0);
	$fr .= pack("v", strlen($name) );
	$fr .= pack("v", 0 );
	$fr .= $name;
	$fr .= pack("V", 0);
	$fr .= pack("V", 0);
	$fr .= pack("V", 0);
	$this -> datasec[] = $fr;
	$new_offset = strlen(implode("", $this->datasec));
	$cdrec = "\x50\x4b\x01\x02";
	$cdrec .="\x00\x00";
	$cdrec .="\x0a\x00";
	$cdrec .="\x00\x00";
	$cdrec .="\x00\x00";
	$cdrec .="\x00\x00\x00\x00";
	$cdrec .= pack("V",0);
	$cdrec .= pack("V",0);
	$cdrec .= pack("V",0);
	$cdrec .= pack("v", strlen($name) );
	$cdrec .= pack("v", 0 );
	$cdrec .= pack("v", 0 );
	$cdrec .= pack("v", 0 );
	$cdrec .= pack("v", 0 );
	$cdrec .= pack("V", 16 );
	$cdrec .= pack("V", $this -> old_offset );
	$cdrec .= $name;
	$this -> ctrl_dir[] = $cdrec;
	$this -> old_offset = $new_offset;
}

/**
 * Converts an Unix timestamp to a four byte DOS date and time format (date
 * in high two bytes, time in low two bytes allowing magnitude comparison).
 *
 * @param  integer  the current Unix timestamp
 *
 * @return integer  the current date in a four byte DOS format
 *
 * @access private
 */
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;
	} // end if

	return (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) |
			($timearray['hours'] << 11) | ($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1);
} // end of the 'unix2DosTime()' method

/**
     * Adds "file" to archive
     *
     * @param  string   file contents
     * @param  string   name of the file in the archive (may contains the path)
     *
     * @access public
     */
function add_file($data, $name) {
	// Read the file
	$fp = fopen($data,"r");
	$data = fread($fp,filesize($data));
	fclose($fp);

	// Clean the file name
	$name = str_replace("", "/", $name);

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

        $fr   = "\x50\x4b\x03\x04";
        $fr   .= "\x14\x00";            // ver needed to extract
        $fr   .= "\x00\x00";            // gen purpose bit flag
        $fr   .= "\x08\x00";            // compression method
        $fr   .= $hexdtime;             // last mod time and date

	// "local file header" segment
        $unc_len = strlen($data);
        $crc     = crc32($data);
        $zdata   = gzcompress($data);
	$zdata   = substr(substr($zdata, 0, strlen($zdata) - 4), 2); // fix crc bug
	$c_len   = strlen($zdata);
	$fr      .= pack('V', $crc);             // crc32
        $fr      .= pack('V', $c_len);           // compressed filesize
        $fr      .= pack('V', $unc_len);         // uncompressed filesize
        $fr      .= pack('v', strlen($name));    // length of filename
        $fr      .= pack('v', 0);                // extra field length
        $fr      .= $name;

	// "file data" segment
        $fr .= $zdata;

	// add this entry to array
	$this -> datasec[] = $fr;

	// now add to central directory record
	$new_offset = strlen(implode("", $this->datasec));

	$cdrec = "\x50\x4b\x01\x02";
	$cdrec .="\x00\x00";
	$cdrec .="\x14\x00";
	$cdrec .="\x00\x00";
	$cdrec .="\x08\x00";
	$cdrec .="\x00\x00\x00\x00";
	$cdrec .= pack("V",$crc);
	$cdrec .= pack("V",$c_len);
	$cdrec .= pack("V",$unc_len);
	$cdrec .= pack("v", strlen($name) );
	$cdrec .= pack("v", 0 );
	$cdrec .= pack("v", 0 );
	$cdrec .= pack("v", 0 );
	$cdrec .= pack("v", 0 );
	$cdrec .= pack("V", 32 );
	$cdrec .= pack("V", $this -> old_offset );

	$this -> old_offset = $new_offset;

	$cdrec .= $name;
	$this -> ctrl_dir[] = $cdrec;
}

/**
     * Dumps out file
     *
     * @return  string  the zipped file
     *
     * @access public
     */
    function file()
    {
        $data    = implode('', $this -> datasec);
        $ctrldir = implode('', $this -> ctrl_dir);

        return
            $data .
            $ctrldir .
            $this -> eof_ctrl_dir .
            pack('v', sizeof($this -> ctrl_dir)) .  // total # of entries "on this disk"
            pack('v', sizeof($this -> ctrl_dir)) .  // total # of entries overall
            pack('V', strlen($ctrldir)) .           // size of central dir
            pack('V', strlen($data)) .              // offset to start of central dir
            "\x00\x00";                             // .zip file comment length
    }
}

// Test this class
$zipTest = new zipfile();
$zipTest->add_dir("images/");
$zipTest->add_file("images/test.jpg", "test.jpg");
$zipTest->add_file("images/test2.jpg", "images/test2.jpg");

// Return Zip File to Browser
Header("Content-type: application/octet-stream");
Header ("Content-disposition: attachment; filename=zipTest.zip");
echo $zipTest->file();
?>

 

On windows and linux, this class works flawlessly.  On a Mac, this works with the stuffit program, but the default archive program does not like the zips if it contains a directory.  If you comment out  $zipTest->add_dir("images/");, the archive will work great on a Mac as well.

 

Could someone help me get this class to create a working directory on a  Mac as well?  I am at a point where I would be willing to pay for your time if you can help me get this working (via paypal of course).  :D

 

Thanks in advance!

weeee

Link to comment
Share on other sites

If you have access to your server root, you can recompile PHP with Zip support.  The ZipArchive class is very useful for creating and managing dynamic Zip files.

 

ZipArchive on the PHP Manual

 

I didn't read through your code too closely, so it may be that your method is just as effective, just with a simple issue somewhere in the code, but I've used ZipArchive many times and have never occurred any issues.

 

Good luck!

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.