Gazelle/classes/zip.class.php

187 lines
7.2 KiB
PHP
Raw Normal View History

2011-03-28 14:21:28 +00:00
<?
/*************************************************************************|
|--------------- Zip class -----------------------------------------------|
|*************************************************************************|
This class provides a convenient way for us to generate and serve zip
2013-02-22 08:00:24 +00:00
archives to our end users, both from physical files, cached
2011-03-28 14:21:28 +00:00
or already parsed data (torrent files). It's all done on the fly, due to
2013-02-22 08:00:24 +00:00
the high probability that a filesystem stored archive will never be
2011-03-28 14:21:28 +00:00
downloaded twice.
Utilizes gzcompress, based upon RFC 1950
//------------- How it works --------------//
Basic concept is construct archive, add files, and serve on the fly.
//------------- How to use it --------------//
* First, construct the archive:
2013-02-21 08:00:30 +00:00
$Zip = new Zip('FileName');
2011-03-28 14:21:28 +00:00
Adds the headers so that add_file can stream and we don't need to create a massive buffer.
open_stream(); was integrated into the constructor to conform with Object-Oriented Standards.
$Zip->unlimit();
A simple shortcut function for raising the basic PHP limits, time and memory for larger archives.
-----
* Then, add files and begin streaming to the user to avoid memory buffering:
$Zip->add_file(file_get_contents("data/file.txt"), "File.txt");
Adds the contents of data/file.txt into File.txt in the archive root.
$Zip->add_file($TorrentData, "Bookmarks/Artist - Album [2008].torrent");
Adds the parsed torrent to the archive in the Bookmarks folder (created simply by placing it in the path).
2013-02-22 08:00:24 +00:00
2011-03-28 14:21:28 +00:00
-----
* Then, close the archive to the user:
$Zip->close_stream();
This collects everything put together thus far in the archive, and streams it to the user in the form of Test7.zip
//------ Explanation of basic functions ------//
add_file(Contents, Internal Path)
Adds the contents to the archive, where it will be extracted to Internal Path.
close_stream();
Collect and stream to the user.
//------------- Detailed example -------------//
2013-05-27 08:00:58 +00:00
require('classes/zip.class.php');
2013-02-21 08:00:30 +00:00
$Zip = new Zip('FileName');
2011-03-28 14:21:28 +00:00
$Name = 'Ubuntu-8.10';
2012-10-11 08:00:15 +00:00
$Zip->add_file($TorrentData, 'Torrents/'.Misc::file_string($Name).'.torrent');
2011-03-28 14:21:28 +00:00
$Zip->add_file(file_get_contents('zip.php'), 'zip.php');
$Zip->close_stream();
//---------- Development reference -----------//
http://www.pkware.com/documents/casestudies/APPNOTE.TXT - ZIP spec (this class)
http://www.ietf.org/rfc/rfc1950.txt - ZLIB compression spec (gzcompress function)
http://www.fileformat.info/tool/hexdump.htm - Useful for analyzing ZIP files
|*************************************************************************/
if (!extension_loaded('zlib')) {
error('Zlib Extension not loaded.');
}
/*
//Handles timestamps
function dostime($TimeStamp = 0) {
2013-04-13 08:00:19 +00:00
if (!is_number($TimeStamp)) { // Assume that $TimeStamp is SQL timestamp
if ($TimeStamp == '0000-00-00 00:00:00') {
return 'Never';
}
2011-03-28 14:21:28 +00:00
$TimeStamp = strtotime($TimeStamp);
}
2013-05-28 08:01:02 +00:00
$Date = (($TimeStamp == 0) ? getdate() : getdate($TimeStamp));
2011-03-28 14:21:28 +00:00
$Hex = dechex((($Date['year'] - 1980) << 25) | ($Date['mon'] << 21) | ($Date['mday'] << 16) | ($Date['hours'] << 11) | ($Date['minutes'] << 5) | ($Date['seconds'] >> 1));
eval("\$Return = \"\x$Hex[6]$Hex[7]\x$Hex[4]$Hex[5]\x$Hex[2]$Hex[3]\x$Hex[0]$Hex[1]\";");
return $Return;
}
*/
2013-02-21 08:00:30 +00:00
class Zip {
2013-05-28 08:01:02 +00:00
public $ArchiveSize = 0; // Total size
2011-03-28 14:21:28 +00:00
public $ArchiveFiles = 0; // Total files
private $Structure = ''; // Structure saved to memory
private $FileOffset = 0; // Offset to write data
private $Data = ''; //An idea
2013-02-22 08:00:24 +00:00
2013-05-28 08:01:02 +00:00
public function __construct ($ArchiveName = 'Archive') {
header("Content-type: application/octet-stream"); // Stream download
header("Content-disposition: attachment; filename=\"$ArchiveName.zip\""); // Name the archive - Should not be urlencoded
2011-03-28 14:21:28 +00:00
}
2013-02-22 08:00:24 +00:00
2011-03-28 14:21:28 +00:00
public static function unlimit () {
ob_end_clean();
2013-05-28 08:01:02 +00:00
set_time_limit(3600); // Limit 1 hour
2011-03-28 14:21:28 +00:00
ini_set('memory_limit', '1024M'); // Because the buffers can get extremely large
}
public function add_file ($FileData, $ArchivePath, $TimeStamp = 0) {
/* File header */
$this->Data = "\x50\x4b\x03\x04"; // PK signature
$this->Data .= "\x14\x00"; // Version requirements
2013-02-21 08:00:30 +00:00
$this->Data .= "\x00\x08"; // Bit flag - 0x8 = UTF-8 file names
2011-03-28 14:21:28 +00:00
$this->Data .= "\x08\x00"; // Compression
2013-05-28 08:01:02 +00:00
//$this->Data .= dostime($TimeStamp); // Last modified
2011-03-28 14:21:28 +00:00
$this->Data .= "\x00\x00\x00\x00";
2013-05-28 08:01:02 +00:00
$DataLength = strlen($FileData); // Saved as variable to avoid wasting CPU calculating it multiple times.
2011-03-28 14:21:28 +00:00
$CRC32 = crc32($FileData); // Ditto.
$ZipData = gzcompress($FileData); // Ditto.
2013-05-28 08:01:02 +00:00
$ZipData = substr ($ZipData, 2, (strlen($ZipData) - 6)); // Checksum resolution
$ZipLength = strlen($ZipData); // Ditto.
$this->Data .= pack('V', $CRC32); // CRC-32
$this->Data .= pack('V', $ZipLength); // Compressed file size
$this->Data .= pack('V', $DataLength); // Uncompressed file size
$this->Data .= pack('v', strlen($ArchivePath)); // Path name length
2011-03-28 14:21:28 +00:00
$this->Data .="\x00\x00"; // Extra field length (0'd so we can ignore this)
2013-05-28 08:01:02 +00:00
$this->Data .= $ArchivePath; // File name & Extra Field (length set to 0 so ignored)
2011-03-28 14:21:28 +00:00
/* END file header */
/* File data */
$this->Data .= $ZipData; // File data
/* END file data */
2013-04-18 08:00:54 +00:00
/* Data descriptor
Not needed (only needed when 3rd bitflag is set), causes problems with OS X archive utility
2013-05-28 08:01:02 +00:00
$this->Data .= pack('V', $CRC32); // CRC-32
$this->Data .= pack('V', $ZipLength); // Compressed file size
$this->Data .= pack('V', $DataLength); // Uncompressed file size
2011-03-28 14:21:28 +00:00
END data descriptor */
2013-02-22 08:00:24 +00:00
2011-03-28 14:21:28 +00:00
$FileDataLength = strlen($this->Data);
$this->ArchiveSize = $this->ArchiveSize + $FileDataLength; // All we really need is the size
$CurrentOffset = $this->ArchiveSize; // Update offsets
echo $this->Data; // Get this out to reduce our memory consumption
/* Central Directory Structure */
$CDS = "\x50\x4b\x01\x02"; // CDS signature
2013-02-21 08:00:30 +00:00
$CDS .="\x14\x00"; // Constructor version
2013-02-22 08:00:24 +00:00
$CDS .="\x14\x00"; // Version requirements
2013-02-21 08:00:30 +00:00
$CDS .="\x00\x08"; // Bit flag - 0x8 = UTF-8 file names
2011-03-28 14:21:28 +00:00
$CDS .="\x08\x00"; // Compression
2013-02-22 08:00:24 +00:00
$CDS .="\x00\x00\x00\x00"; // Last modified
2013-05-28 08:01:02 +00:00
$CDS .= pack('V', $CRC32); // CRC-32
$CDS .= pack('V', $ZipLength); // Compressed file size
$CDS .= pack('V', $DataLength); // Uncompressed file size
$CDS .= pack('v', strlen($ArchivePath)); // Path name length
2011-03-28 14:21:28 +00:00
$CDS .="\x00\x00"; // Extra field length (0'd so we can ignore this)
$CDS .="\x00\x00"; // File comment length (no comment, 0'd)
$CDS .="\x00\x00"; // Disk number start (0 seems valid)
$CDS .="\x00\x00"; // Internal file attributes (again with the 0's)
$CDS .="\x20\x00\x00\x00"; // External file attributes
2013-05-28 08:01:02 +00:00
$CDS .= pack('V', $this->FileOffset); // Offsets
$CDS .= $ArchivePath; // File name & Extra Field (length set to 0 so ignored)
2011-03-28 14:21:28 +00:00
/* END central Directory Structure */
$this->FileOffset = $CurrentOffset; // Update offsets
$this->Structure .= $CDS; // Append to structure
$this->ArchiveFiles++; // Increment file count
}
public function close_stream() {
2013-02-22 08:00:24 +00:00
echo $this->Structure; // Structure Root
2011-03-28 14:21:28 +00:00
echo "\x50\x4b\x05\x06"; // End of central directory signature
echo "\x00\x00"; // This disk
2013-02-22 08:00:24 +00:00
echo "\x00\x00"; // CDS start
2013-05-28 08:01:02 +00:00
echo pack('v', $this->ArchiveFiles); // Handle the number of entries
echo pack('v', $this->ArchiveFiles); // Ditto
echo pack('V', strlen($this->Structure)); //Size
echo pack('V', $this->ArchiveSize); // Offset
2011-03-28 14:21:28 +00:00
echo "\x00\x00"; // No comment, close it off
}
}