Oppaitime's version of Gazelle
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

zip.class.php 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. <?
  2. /*************************************************************************|
  3. |--------------- Zip class -----------------------------------------------|
  4. |*************************************************************************|
  5. This class provides a convenient way for us to generate and serve zip
  6. archives to our end users, both from physical files, cached
  7. or already parsed data (torrent files). It's all done on the fly, due to
  8. the high probability that a filesystem stored archive will never be
  9. downloaded twice.
  10. Utilizes gzcompress, based upon RFC 1950
  11. //------------- How it works --------------//
  12. Basic concept is construct archive, add files, and serve on the fly.
  13. //------------- How to use it --------------//
  14. * First, construct the archive:
  15. $Zip = new Zip('FileName');
  16. Adds the headers so that add_file can stream and we don't need to create a massive buffer.
  17. open_stream(); was integrated into the constructor to conform with Object-Oriented Standards.
  18. $Zip->unlimit();
  19. A simple shortcut function for raising the basic PHP limits, time and memory for larger archives.
  20. -----
  21. * Then, add files and begin streaming to the user to avoid memory buffering:
  22. $Zip->add_file(file_get_contents("data/file.txt"), "File.txt");
  23. Adds the contents of data/file.txt into File.txt in the archive root.
  24. $Zip->add_file($TorrentData, "Bookmarks/Artist - Album [2008].torrent");
  25. Adds the parsed torrent to the archive in the Bookmarks folder (created simply by placing it in the path).
  26. -----
  27. * Then, close the archive to the user:
  28. $Zip->close_stream();
  29. This collects everything put together thus far in the archive, and streams it to the user in the form of Test7.zip
  30. //------ Explanation of basic functions ------//
  31. add_file(Contents, Internal Path)
  32. Adds the contents to the archive, where it will be extracted to Internal Path.
  33. close_stream();
  34. Collect and stream to the user.
  35. //------------- Detailed example -------------//
  36. require('classes/zip.class.php');
  37. $Zip = new Zip('FileName');
  38. $Name = 'Ubuntu-8.10';
  39. $Zip->add_file($TorrentData, 'Torrents/'.Misc::file_string($Name).'.torrent');
  40. $Zip->add_file(file_get_contents('zip.php'), 'zip.php');
  41. $Zip->close_stream();
  42. //---------- Development reference -----------//
  43. http://www.pkware.com/documents/casestudies/APPNOTE.TXT - ZIP spec (this class)
  44. http://www.ietf.org/rfc/rfc1950.txt - ZLIB compression spec (gzcompress function)
  45. http://www.fileformat.info/tool/hexdump.htm - Useful for analyzing ZIP files
  46. |*************************************************************************/
  47. if (!extension_loaded('zlib')) {
  48. error('Zlib Extension not loaded.');
  49. }
  50. class Zip {
  51. public $ArchiveSize = 0; // Total size
  52. public $ArchiveFiles = 0; // Total files
  53. private $Structure = ''; // Structure saved to memory
  54. private $FileOffset = 0; // Offset to write data
  55. private $Data = ''; //An idea
  56. public function __construct ($ArchiveName = 'Archive') {
  57. header("Content-type: application/octet-stream"); // Stream download
  58. header("Content-disposition: attachment; filename=\"$ArchiveName.zip\""); // Name the archive - Should not be urlencoded
  59. }
  60. public static function unlimit () {
  61. ob_end_clean();
  62. set_time_limit(3600); // Limit 1 hour
  63. ini_set('memory_limit', '1024M'); // Because the buffers can get extremely large
  64. }
  65. public function add_file ($FileData, $ArchivePath, $TimeStamp = 0) {
  66. /* File header */
  67. $this->Data = "\x50\x4b\x03\x04"; // PK signature
  68. $this->Data .= "\x14\x00"; // Version requirements
  69. $this->Data .= "\x00\x08"; // Bit flag - 0x8 = UTF-8 file names
  70. $this->Data .= "\x08\x00"; // Compression
  71. $this->Data .= "\x00\x00\x00\x00";
  72. $DataLength = strlen($FileData); // Saved as variable to avoid wasting CPU calculating it multiple times.
  73. $CRC32 = crc32($FileData); // Ditto.
  74. $ZipData = gzcompress($FileData); // Ditto.
  75. $ZipData = substr ($ZipData, 2, (strlen($ZipData) - 6)); // Checksum resolution
  76. $ZipLength = strlen($ZipData); // Ditto.
  77. $this->Data .= pack('V', $CRC32); // CRC-32
  78. $this->Data .= pack('V', $ZipLength); // Compressed file size
  79. $this->Data .= pack('V', $DataLength); // Uncompressed file size
  80. $this->Data .= pack('v', strlen($ArchivePath)); // Path name length
  81. $this->Data .="\x00\x00"; // Extra field length (0'd so we can ignore this)
  82. $this->Data .= $ArchivePath; // File name & Extra Field (length set to 0 so ignored)
  83. /* END file header */
  84. /* File data */
  85. $this->Data .= $ZipData; // File data
  86. /* END file data */
  87. /* Data descriptor
  88. Not needed (only needed when 3rd bitflag is set), causes problems with OS X archive utility
  89. $this->Data .= pack('V', $CRC32); // CRC-32
  90. $this->Data .= pack('V', $ZipLength); // Compressed file size
  91. $this->Data .= pack('V', $DataLength); // Uncompressed file size
  92. END data descriptor */
  93. $FileDataLength = strlen($this->Data);
  94. $this->ArchiveSize = $this->ArchiveSize + $FileDataLength; // All we really need is the size
  95. $CurrentOffset = $this->ArchiveSize; // Update offsets
  96. echo $this->Data; // Get this out to reduce our memory consumption
  97. /* Central Directory Structure */
  98. $CDS = "\x50\x4b\x01\x02"; // CDS signature
  99. $CDS .="\x14\x00"; // Constructor version
  100. $CDS .="\x14\x00"; // Version requirements
  101. $CDS .="\x00\x08"; // Bit flag - 0x8 = UTF-8 file names
  102. $CDS .="\x08\x00"; // Compression
  103. $CDS .="\x00\x00\x00\x00"; // Last modified
  104. $CDS .= pack('V', $CRC32); // CRC-32
  105. $CDS .= pack('V', $ZipLength); // Compressed file size
  106. $CDS .= pack('V', $DataLength); // Uncompressed file size
  107. $CDS .= pack('v', strlen($ArchivePath)); // Path name length
  108. $CDS .="\x00\x00"; // Extra field length (0'd so we can ignore this)
  109. $CDS .="\x00\x00"; // File comment length (no comment, 0'd)
  110. $CDS .="\x00\x00"; // Disk number start (0 seems valid)
  111. $CDS .="\x00\x00"; // Internal file attributes (again with the 0's)
  112. $CDS .="\x20\x00\x00\x00"; // External file attributes
  113. $CDS .= pack('V', $this->FileOffset); // Offsets
  114. $CDS .= $ArchivePath; // File name & Extra Field (length set to 0 so ignored)
  115. /* END central Directory Structure */
  116. $this->FileOffset = $CurrentOffset; // Update offsets
  117. $this->Structure .= $CDS; // Append to structure
  118. $this->ArchiveFiles++; // Increment file count
  119. }
  120. public function close_stream() {
  121. echo $this->Structure; // Structure Root
  122. echo "\x50\x4b\x05\x06"; // End of central directory signature
  123. echo "\x00\x00"; // This disk
  124. echo "\x00\x00"; // CDS start
  125. echo pack('v', $this->ArchiveFiles); // Handle the number of entries
  126. echo pack('v', $this->ArchiveFiles); // Ditto
  127. echo pack('V', strlen($this->Structure)); //Size
  128. echo pack('V', $this->ArchiveSize); // Offset
  129. echo "\x00\x00"; // No comment, close it off
  130. }
  131. }