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 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. <?php
  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. {
  52. public $ArchiveSize = 0; // Total size
  53. public $ArchiveFiles = 0; // Total files
  54. private $Structure = ''; // Structure saved to memory
  55. private $FileOffset = 0; // Offset to write data
  56. private $Data = ''; //An idea
  57. public function __construct($ArchiveName = 'Archive')
  58. {
  59. header("Content-type: application/octet-stream"); // Stream download
  60. header("Content-disposition: attachment; filename=\"$ArchiveName.zip\""); // Name the archive - Should not be urlencoded
  61. }
  62. public static function unlimit()
  63. {
  64. ob_end_clean();
  65. set_time_limit(3600); // Limit 1 hour
  66. ini_set('memory_limit', '1024M'); // Because the buffers can get extremely large
  67. }
  68. public function add_file($FileData, $ArchivePath, $TimeStamp = 0)
  69. {
  70. /* File header */
  71. $this->Data = "\x50\x4b\x03\x04"; // PK signature
  72. $this->Data .= "\x14\x00"; // Version requirements
  73. $this->Data .= "\x00\x08"; // Bit flag - 0x8 = UTF-8 file names
  74. $this->Data .= "\x08\x00"; // Compression
  75. $this->Data .= "\x00\x00\x00\x00";
  76. $DataLength = strlen($FileData); // Saved as variable to avoid wasting CPU calculating it multiple times.
  77. $CRC32 = crc32($FileData); // Ditto.
  78. $ZipData = gzcompress($FileData); // Ditto.
  79. $ZipData = substr($ZipData, 2, (strlen($ZipData) - 6)); // Checksum resolution
  80. $ZipLength = strlen($ZipData); // Ditto.
  81. $this->Data .= pack('V', $CRC32); // CRC-32
  82. $this->Data .= pack('V', $ZipLength); // Compressed file size
  83. $this->Data .= pack('V', $DataLength); // Uncompressed file size
  84. $this->Data .= pack('v', strlen($ArchivePath)); // Path name length
  85. $this->Data .="\x00\x00"; // Extra field length (0'd so we can ignore this)
  86. $this->Data .= $ArchivePath; // File name & Extra Field (length set to 0 so ignored)
  87. /* END file header */
  88. /* File data */
  89. $this->Data .= $ZipData; // File data
  90. /* END file data */
  91. /* Data descriptor
  92. Not needed (only needed when 3rd bitflag is set), causes problems with OS X archive utility
  93. $this->Data .= pack('V', $CRC32); // CRC-32
  94. $this->Data .= pack('V', $ZipLength); // Compressed file size
  95. $this->Data .= pack('V', $DataLength); // Uncompressed file size
  96. END data descriptor */
  97. $FileDataLength = strlen($this->Data);
  98. $this->ArchiveSize = $this->ArchiveSize + $FileDataLength; // All we really need is the size
  99. $CurrentOffset = $this->ArchiveSize; // Update offsets
  100. echo $this->Data; // Get this out to reduce our memory consumption
  101. /* Central Directory Structure */
  102. $CDS = "\x50\x4b\x01\x02"; // CDS signature
  103. $CDS .="\x14\x00"; // Constructor version
  104. $CDS .="\x14\x00"; // Version requirements
  105. $CDS .="\x00\x08"; // Bit flag - 0x8 = UTF-8 file names
  106. $CDS .="\x08\x00"; // Compression
  107. $CDS .="\x00\x00\x00\x00"; // Last modified
  108. $CDS .= pack('V', $CRC32); // CRC-32
  109. $CDS .= pack('V', $ZipLength); // Compressed file size
  110. $CDS .= pack('V', $DataLength); // Uncompressed file size
  111. $CDS .= pack('v', strlen($ArchivePath)); // Path name length
  112. $CDS .="\x00\x00"; // Extra field length (0'd so we can ignore this)
  113. $CDS .="\x00\x00"; // File comment length (no comment, 0'd)
  114. $CDS .="\x00\x00"; // Disk number start (0 seems valid)
  115. $CDS .="\x00\x00"; // Internal file attributes (again with the 0's)
  116. $CDS .="\x20\x00\x00\x00"; // External file attributes
  117. $CDS .= pack('V', $this->FileOffset); // Offsets
  118. $CDS .= $ArchivePath; // File name & Extra Field (length set to 0 so ignored)
  119. /* END central Directory Structure */
  120. $this->FileOffset = $CurrentOffset; // Update offsets
  121. $this->Structure .= $CDS; // Append to structure
  122. $this->ArchiveFiles++; // Increment file count
  123. }
  124. public function close_stream()
  125. {
  126. echo $this->Structure; // Structure Root
  127. echo "\x50\x4b\x05\x06"; // End of central directory signature
  128. echo "\x00\x00"; // This disk
  129. echo "\x00\x00"; // CDS start
  130. echo pack('v', $this->ArchiveFiles); // Handle the number of entries
  131. echo pack('v', $this->ArchiveFiles); // Ditto
  132. echo pack('V', strlen($this->Structure)); //Size
  133. echo pack('V', $this->ArchiveSize); // Offset
  134. echo "\x00\x00"; // No comment, close it off
  135. }
  136. }