Zip Stream in PHP

How can I stream a zip in a zip stream download?

Well, this kind of works. I wanted to post it although I decided to fix the problem in another way. But just for future reference this may be helpful for someone.

<?php
$zip_header_outer = "\x1f\x8b\x08\x08i\xbb\xb9X\x00\xffinner.zip\x00";
$zip_header_inner = "\x1F\x8B\x08\x08\x69\xBB\xB9\x58\x00\xFF";
$compression_level = 0;
$download_headers = true;
$download_file_name = 'my.zip';
$inner_file_name = 'inner.zip';
$first_level_file = 'a.txt';
$second_level_file = 'b.txt';

$fout = fopen("php://output", "wb");
if ($fout === FALSE) {
die('Problem outputting');
}

if ($download_headers) {
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"" . $download_file_name . "\"");
}

function add_to_inner_zip($filename, $path, &$fout, &$fsize_outer, &$hctx_outer) {
$zip_header_inner = "\x1F\x8B\x08\x08\x69\xBB\xB9\x58\x00\xFF";

fwrite($fout, $zip_header_inner);
hash_update($hctx_outer, $zip_header_inner);
$fsize_outer += strlen($zip_header_inner);
$hctx_inner = hash_init("crc32b");
$fsize_inner = 0;

// Inner Add file name
$file_name = str_replace("\0", "", basename($filename));
$data = $file_name."\0";
fwrite($fout, $data, 1+strlen($data));
hash_update($hctx_outer, $data);
$fsize_outer += strlen($data);

// Start inner.zip contents
// Inner Add file data * STREAM CHUNKS HERE *
$file_data = file_get_contents($path);
$clen = strlen($file_data);
hash_update($hctx_inner, $file_data);
$fsize_inner += $clen;
/*hash_update($hctx_member, $file_data);
$fsize_member += $clen;*/

// Actually encode the chunk and add to the main stream
$gziped_chunk = zlib_encode($file_data, ZLIB_ENCODING_RAW);
fwrite($fout, $gziped_chunk);
hash_update($hctx_outer, $gziped_chunk);
$fsize_outer += strlen($gziped_chunk);

// Close the inner zip
$crc = hash_final($hctx_inner, TRUE);
$zip_footer = $crc[3].$crc[2].$crc[1].$crc[0] . pack("V", $fsize_inner);
fwrite($fout, $zip_footer);

// update outer crc + size
hash_update($hctx_outer, $zip_footer);
$fsize_outer += strlen($zip_footer);
}

// Outer
fwrite($fout, $zip_header_outer);
$fltr_outer = stream_filter_append($fout, "zlib.deflate", STREAM_FILTER_WRITE, $compression_level);
$hctx_outer = hash_init("crc32b");
$fsize_outer = 0;

add_to_inner_zip($first_level_file, $first_level_file, $fout, $fsize_outer, $hctx_outer);

stream_filter_remove($fltr_outer);
$crc = hash_final($hctx_outer, TRUE);
fwrite($fout, $crc[3].$crc[2].$crc[1].$crc[0], 4);
fwrite($fout, pack("V", $fsize_outer), 4);

Plus, I know the code sucks, and is hacky - but the situation asked for it :P

Create a Zip from file streams in PHP

I ultimately found my solution here:

Similar to the solution given there, I used the class from phpmyadmin as follows:

header('Content-Type: application/octet-stream');
header("Content-Disposition: attachment; filename='whatever.zip'");

$zip = new ZipFile;
foreach($pdfs as $idx => $content) {
$zip->addFile($content, "file$idx.pdf");
}
file_put_contents("php://output", $zip->file());

String to Zipped Stream in php

Does it have to be a Zip archive? Since you're trying to save bandwith it could be a gzip too.

<?php
$ftp_credentials = "ftp://USER:PASSWORD@HOST/helloworld.gz";
$gz = gzencode("Hello World!", 9);
$options = array('ftp' => array('overwrite' => true));
$stream_context = stream_context_create($options);
file_put_contents($ftp_credentials, $gz, 0, $stream_context);
?>

Using ZipStream in Symfony: streamed zip download will not decompress using Archive Utility on Mac OSX

The issue with the zip downloaded is that it contained html from the response in the Symfony controller that was calling ZipStream->addFileFromStream(). Basically, when ZipStream was streaming data to create zip download in client browser, a controller action response was also sent and, best guess, the two were getting mixed up on client's browser. Opening the zip file in hex editor and seeing the html in there it was obviously the issue.

To get this working in Symfony 2.8 with ZipStream, I just used Symfony's StreamedResponse in the controller action and used ZipStream in the streamedResponse's function. To stream S3 files I just passed in an array of s3keys and the s3client into that function. So:

use Symfony\Component\HttpFoundation\StreamedResponse;
use Aws\S3\S3Client;
use ZipStream;

//...

/**
* @Route("/zipstream", name="zipstream")
*/
public function zipStreamAction()
{
//test file on s3
$s3keys = array(
"ziptestfolder/file1.txt"
);

$s3Client = $this->get('app.amazon.s3'); //s3client service
$s3Client->registerStreamWrapper(); //required

$response = new StreamedResponse(function() use($s3keys, $s3Client)
{

// Define suitable options for ZipStream Archive.
$opt = array(
'comment' => 'test zip file.',
'content_type' => 'application/octet-stream'
);
//initialise zipstream with output zip filename and options.
$zip = new ZipStream\ZipStream('test.zip', $opt);

//loop keys useful for multiple files
foreach ($s3keys as $key) {
// Get the file name in S3 key so we can save it to the zip
//file using the same name.
$fileName = basename($key);

//concatenate s3path.
$bucket = 'bucketname';
$s3path = "s3://" . $bucket . "/" . $key;

//addFileFromStream
if ($streamRead = fopen($s3path, 'r')) {
$zip->addFileFromStream($fileName, $streamRead);
} else {
die('Could not open stream for reading');
}
}

$zip->finish();

});

return $response;
}

Solved. Maybe this helps someone else use ZipStream in Symfony.

Create a zip file using PHP class ZipArchive without writing the file to disk?

Take a look at the following library, it allows creating zip files and returning them as a stream: PHPClasses.org.



Related Topics



Leave a reply



Submit