diff options
Diffstat (limited to 'zipstream.php')
-rw-r--r-- | zipstream.php | 216 |
1 files changed, 162 insertions, 54 deletions
diff --git a/zipstream.php b/zipstream.php index 9323569..39173ec 100644 --- a/zipstream.php +++ b/zipstream.php @@ -6,27 +6,96 @@ # # Usage: # +# Streaming zip archives is a simple, three-step process: +# +# 1. Create the zip stream: +# +# $zip = new ZipStream('example.zip'); +# +# 2. Add one or more files to the archive: +# +# # add first file +# $data = file_get_contents('some_file.gif'); +# $zip->add_file('some_file.gif', $data); +# +# # add second file +# $data = file_get_contents('some_file.gif'); +# $zip->add_file('another_file.png', $data); +# +# 3. Finish the zip stream: +# +# $zip->finish(); +# +# You can also add an archive comment, add comments to individual files, +# and adjust the timestamp of files. See the API documentation below +# for additional information. +# +# Example: +# # # create a new zip stream object -# $zs = new ZipStream(); +# $zip = new ZipStream('some_files.zip'); # # # list of local files # $files = array('foo.txt', 'bar.jpg'); # # # read and add each file to the archive # foreach ($files as $path) -# $zs->add_file($path, file_get_contents($path)); +# $zip->add_file($path, file_get_contents($path)); # # # write archive footer to stream -# $zs->finish(); +# $zip->finish(); # class ZipStream { - var $files = array(), + var $opt = array(), + $files = array(), $cdr_ofs = 0, $ofs = 0; - function ZipStream($name = 'untitled.zip', $send_http_headers = false) { + # + # Create a new ZipStream object. + # + # Parameters: + # + # $name - Name of output file (optional). + # $opt - Hash of archive options (optional, see "Archive Options" + # below). + # + # Archive Options: + # + # comment - Comment for this archive. + # content_type - HTTP Content-Type. Defaults to 'application/x-zip'. + # content_disposition - HTTP Content-Disposition. Defaults to + # 'attachment; filename=\"FILENAME\"', where + # FILENAME is the specified filename. + # send_http_headers - Boolean indicating whether or not to send + # the HTTP headers for this file. + # + # Note that content_type and content_disposition do nothing if you are + # not sending HTTP headers. + # + # Examples: + # + # # create a new zip file named 'foo.zip' + # $zip = new ZipStream('foo.zip'); + # + # # create a new zip file named 'bar.zip' with a comment + # $zip = new ZipStream('bar.zip', array( + # 'comment' => 'this is a comment for the zip file.', + # )); + # + # Notes: + # + # If you do not set a filename, then this library _DOES NOT_ send HTTP + # headers by default. This behavior is to allow software to send its + # own headers (including the filename), and still use this library. + # + function ZipStream($name = null, $opt = array()) { + # save options + $this->opt = $opt; + $this->output_name = $name; - $this->need_headers = $send_http_headers; + if ($name || $opt['send_http_headers']) + $this->need_headers = true; } # @@ -36,16 +105,30 @@ class ZipStream { # # $name - path of file in archive (including directory). # $data - contents of file - # $time - last-modified timestamp of file (optional) + # $opt - Hash of options for file (optional, see "File Options" + # below). # - # Example: + # File Options: + # time - Last-modified timestamp (seconds since the epoch) of + # this file. Defaults to the current time. + # comment - Comment related to this file. + # + # Examples: # + # # add a file named 'foo.txt' # $data = file_get_contents('foo.txt'); - # $zs->add_file('foo.txt', $data); + # $zip->add_file('foo.txt', $data); # - function add_file($name, $data, $time = 0) { + # # add a file named 'bar.jpg' with a comment and a last-modified + # # time of two hours ago + # $data = file_get_contents('bar.jpg'); + # $zip->add_file('bar.jpg', $data, array( + # 'time' => time() - 2 * 3600, + # 'comment' => 'this is a comment about bar.jpg', + # )); + # + function add_file($name, $data, $opt = array()) { # compress data - # $zdata = substr(gzcompress($data), 2, -4); $zdata = gzdeflate($data); # calculate header attributes @@ -55,8 +138,8 @@ class ZipStream { $len = strlen($data); # create dos timestamp - $time = $time ? $time : time(); - $dts = $this->dostime($time); + $opt['time'] = $opt['time'] ? $opt['time'] : time(); + $dts = $this->dostime($opt['time']); # build file header $fields = array( # (from V.A of APPNOTE.TXT) @@ -76,7 +159,7 @@ class ZipStream { $ret = $this->pack_fields($fields) . $name . $zdata; # add to central directory record and increment offset - $this->add_to_cdr($name, $time, $crc, $zlen, $len, strlen($ret)); + $this->add_to_cdr($name, $opt, $crc, $zlen, $len, strlen($ret)); # print data $this->send($ret); @@ -90,63 +173,55 @@ class ZipStream { # # add a list of files to the archive # $files = array('foo.txt', 'bar.jpg'); # foreach ($files as $path) - # $zs->add_file($path, file_get_contents($path)); + # $zip->add_file($path, file_get_contents($path)); # # # write footer to stream - # $zs->finish(); + # $zip->finish(); # - function finish($opt = null) { - $this->add_cdr($opt); + function finish() { + $this->add_cdr($this->opt); $this->clear(); } - function send_http_headers($name = 'untitled.zip') { - $headers = array( - 'Content-Type' => 'application/x-zip', - 'Content-Disposition' => "attachment; filename=\"{$name}\"", - 'Pragma' => 'public', - 'Cache-Control' => 'public, must-revalidate', - 'Content-Transfer-Encoding' => 'binary', - ); - - foreach ($headers as $key => $val) - header("$key: $val"); - } - ################### # PRIVATE METHODS # ################### - function add_to_cdr($name, $time, $crc, $zlen, $len, $rec_len) { - $this->files[] = array($name, $time, $crc, $zlen, $len, $this->ofs); + function add_to_cdr($name, $opt, $crc, $zlen, $len, $rec_len) { + $this->files[] = array($name, $opt, $crc, $zlen, $len, $this->ofs); $this->ofs += $rec_len; } function add_cdr_file($args) { - list ($name, $time, $crc, $zlen, $len, $ofs) = $args; - $dts = $this->dostime($time); + list ($name, $opt, $crc, $zlen, $len, $ofs) = $args; + + # get attributes + $comment = $opt['comment'] ? $opt['comment'] : ''; + + # get dos timestamp + $dts = $this->dostime($opt['time']); $fields = array( # (from V,F of APPNOTE.TXT) - array('V', 0x02014b50), # central file header signature - array('v', 0x00), # version made by - array('v', 0x14), # version needed to extract - array('v', 0x00), # general purpose bit flag - array('v', 0x08), # compresion method (deflate) - array('V', $dts), # dos timestamp - array('V', $crc), # crc32 of data - array('V', $zlen), # compressed data length - array('V', $len), # uncompressed data length - array('v', strlen($name)), # filename length - array('v', 0), # extra data len - array('v', 0), # file comment length - array('v', 0), # disk number start - array('v', 0), # internal file attributes - array('V', 32), # external file attributes - array('V', $ofs), # relative offset of local header + array('V', 0x02014b50), # central file header signature + array('v', 0x00), # version made by + array('v', 0x14), # version needed to extract + array('v', 0x00), # general purpose bit flag + array('v', 0x08), # compresion method (deflate) + array('V', $dts), # dos timestamp + array('V', $crc), # crc32 of data + array('V', $zlen), # compressed data length + array('V', $len), # uncompressed data length + array('v', strlen($name)), # filename length + array('v', 0), # extra data len + array('v', strlen($comment)), # file comment length + array('v', 0), # disk number start + array('v', 0), # internal file attributes + array('V', 32), # external file attributes + array('V', $ofs), # relative offset of local header ); - # pack fields and append name - $ret = $this->pack_fields($fields) . $name; + # pack fields, then append name and comment + $ret = $this->pack_fields($fields) . $name . $comment; $this->send($ret); @@ -191,9 +266,42 @@ class ZipStream { $this->cdr_ofs = 0; } + ########################### + # PRIVATE UTILITY METHODS # + ########################### + + function send_http_headers() { + # grab options + $opt = $this->opt; + + # grab content type from options + $content_type = 'application/x-zip', + if ($opt['content_type']) + $content_type = $this->opt['content_type']; + + # grab content disposition + $disposition = 'attachment'; + if ($opt['content_disposition']) + $disposition = $opt['content_disposition']; + + if ($this->output_name) + $disposition .= "; filename=\"{$this->output_name}\""; + + $headers = array( + 'Content-Type' => $content_type, + 'Content-Disposition' => $disposition, + 'Pragma' => 'public', + 'Cache-Control' => 'public, must-revalidate', + 'Content-Transfer-Encoding' => 'binary', + ); + + foreach ($headers as $key => $val) + header("$key: $val"); + } + function send($str) { if ($this->need_headers) - $this->send_http_headers($this->output_name); + $this->send_http_headers(); $this->need_headers = false; echo $str; |