### Basic ZipStream PHP Usage Example Source: https://github.com/maennchen/zipstream-php/blob/main/guides/index.rst A simple example demonstrating how to create a zip stream, add files from string data, local paths, and streams, and finish the stream. Ensure dependencies are autoloaded. ```php // Autoload the dependencies require 'vendor/autoload.php'; // create a new zipstream object $zip = new ZipStream\ZipStream( outputName: 'example.zip', // enable output of HTTP headers sendHttpHeaders: true, ); // create a file named 'hello.txt' $zip->addFile( fileName: 'hello.txt', data: 'This is the contents of hello.txt', ); // add a file named 'some_image.jpg' from a local file 'path/to/image.jpg' $zip->addFileFromPath( fileName: 'some_image.jpg', path: 'path/to/image.jpg', ); // add a file named 'goodbye.txt' from an open stream resource $filePointer = tmpfile(); fwrite($filePointer, 'The quick brown fox jumped over the lazy dog.'); rewind($filePointer); $zip->addFileFromStream( fileName: 'goodbye.txt', stream: $filePointer, ); fclose($filePointer); // add a file named 'streamfile.txt' from the body of a `guzzle` response // Setup with `psr/http-message` & `guzzlehttp/psr7` dependencies required. $zip->addFileFromPsr7Stream( fileName: 'streamfile.txt', stream: $response->getBody(), ); // finish the zip stream $zip->finish(); ``` -------------------------------- ### Install ZipStream-PHP with Composer Source: https://github.com/maennchen/zipstream-php/blob/main/README.md Use Composer to add the ZipStream-PHP package to your project's dependencies. ```bash composer require maennchen/zipstream-php ``` -------------------------------- ### Install PSR7 and GuzzleHTTP Dependencies Source: https://github.com/maennchen/zipstream-php/blob/main/guides/index.rst Install additional dependencies if you plan to use PSR7 streams or GuzzleHTTP with ZipStream PHP. ```sh composer require psr/http-message guzzlehttp/psr7 ``` -------------------------------- ### Resolve Composer Error: Missing mbstring Extension Source: https://github.com/maennchen/zipstream-php/blob/main/guides/index.rst If Composer reports a missing mbstring extension, install it or use the symfony/polyfill-mbstring package. ```sh composer require symfony/polyfill-mbstring ``` -------------------------------- ### Flysystem Integration for Storage Source: https://context7.com/maennchen/zipstream-php/llms.txt Use ZipStream with Flysystem to save zip archives to various storage backends. This example demonstrates saving to a local filesystem after creating an in-memory zip stream. ```php addFile('readme.txt', 'This is the readme file.'); $zip->addFile('data/config.json', json_encode(['setting' => 'value'])); $zip->finish(); // Rewind stream before reading rewind($tempStream); // Save using Flysystem (can be any adapter) $adapter = new LocalFilesystemAdapter('/var/www/storage'); $filesystem = new Filesystem($adapter); $filesystem->writeStream('backups/archive.zip', $tempStream); fclose($tempStream); ``` -------------------------------- ### Upload File to S3 with Public Read ACL using stream_context_create Source: https://github.com/maennchen/zipstream-php/blob/main/guides/Symfony.rst This example shows how to create a stream context for uploading a file to an S3 bucket with a 'public-read' ACL. This is useful when you need to control access permissions during the upload process. The `stream_context_create` function is used to define these options. ```php $path = "s3://{$adapter->getBucket()}/{$this->getArchivePath()}"; // the important bit $outputContext = stream_context_create([ 's3' => ['ACL' => 'public-read'], ]); fopen($path, 'w', null, $outputContext); ``` -------------------------------- ### Symfony StreamedResponse Integration Source: https://context7.com/maennchen/zipstream-php/llms.txt Integrate ZipStream with Symfony controllers using StreamedResponse for efficient browser streaming. This example shows how to stream files from S3. ```php registerStreamWrapper(); $bucket = 'my-bucket'; $s3keys = [ 'documents/file1.txt', 'documents/file2.pdf', 'images/photo.jpg', ]; return new StreamedResponse(function () use ($s3keys, $bucket) { $zip = new ZipStream( outputName: 'download.zip', defaultEnableZeroHeader: true, contentType: 'application/octet-stream', ); foreach ($s3keys as $key) { $s3path = "s3://$bucket/$key"; $stream = fopen($s3path, 'r'); if ($stream) { $zip->addFileFromStream( fileName: basename($key), stream: $stream, ); } } $zip->finish(); }); } } ``` -------------------------------- ### Create Zip Archive and Upload to S3 Source: https://github.com/maennchen/zipstream-php/wiki/Stream-S3 This snippet demonstrates how to create a zip archive with multiple files using ZipStream PHP and save it directly to an AWS S3 bucket. Ensure AWS SDK and ZipStream libraries are installed and configured. ```php use Aws\S3\S3Client; use Aws\Credentials\CredentialProvider; use ZipStream\Option\Archive; use ZipStream\ZipStream; $bucket = 'your bucket name'; $client = new S3Client([ 'region' => 'your region', 'version' => 'latest', 'bucketName' => $bucket, 'credentials' => CredentialProvider::defaultProvider(), ]); $client->registerStreamWrapper(); $zipFile = fopen("s3://$bucket/example.zip", 'w'); $options = new Archive(); $options->setEnableZip64(false); $options->setOutputStream($zipFile); $zip = new ZipStream(null, $options); $zip->addFile('file1.txt', 'File1 data'); $zip->addFile('file2.txt', 'File2 data'); $zip->finish(); fclose($zipFile); ``` -------------------------------- ### Stream Zip from AWS S3 using Symfony StreamedResponse Source: https://github.com/maennchen/zipstream-php/blob/main/guides/Symfony.rst This example demonstrates streaming files from an AWS S3 bucket into a zip archive using ZipStream, all within a Symfony controller action. It utilizes Symfony's StreamedResponse to efficiently send the zip file to the browser without creating it on the server. Ensure the S3 stream wrapper is registered with the S3 client. ```php use Symfony\Component\HttpFoundation\StreamedResponse; use Aws\S3\S3Client; use ZipStream; //... /** * @Route("/zipstream", name="zipstream") */ public function zipStreamAction() { // sample test file on s3 $s3keys = array( "ziptestfolder/file1.txt" ); $s3Client = $this->get('app.amazon.s3'); //s3client service $s3Client->registerStreamWrapper(); //required // using StreamedResponse to wrap ZipStream functionality // for files on AWS s3. $response = new StreamedResponse(function() use($s3keys, $s3Client) { // Define suitable options for ZipStream Archive. // this is needed to prevent issues with truncated zip files //initialise zipstream with output zip filename and options. $zip = new ZipStream\ZipStream( outputName: 'test.zip', defaultEnableZeroHeader: true, contentType: 'application/octet-stream', ); //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. // replace with your bucket name or get from parameters file. $bucket = 'bucketname'; $s3path = "s3://" . $bucket . "/" . $key; //addFileFromStream if ($streamRead = fopen($s3path, 'r')) { $zip->addFileFromStream( fileName: $fileName, stream: $streamRead, ); } else { die('Could not open stream for reading'); } } $zip->finish(); }); return $response; } ``` -------------------------------- ### Initialize ZipStream with Options Source: https://github.com/maennchen/zipstream-php/blob/main/guides/Options.rst Demonstrates how to initialize ZipStream with a comprehensive set of options, including output stream, deflate level, comments, HTTP headers, and zip64 extension. ```php use ZipStream\ZipStream; require_once 'vendor/autoload.php'; $zip = new ZipStream( // Define output stream // (argument is either a resource or implementing // `Psr\Http\Message\StreamInterface`) // // Setup with `psr/http-message` & `guzzlehttp/psr7` dependencies // required when using `Psr\Http\Message\StreamInterface`. // // Can also use CallbackStreamWrapper for custom output handling: // outputStream: CallbackStreamWrapper::open(function($data) { /* handle data */ }), outputStream: $filePointer, // Set the deflate level (default is 6; use -1 to disable it) defaultDeflateLevel: 6, // Add a comment to the zip file comment: 'This is a comment.', // Send http headers (default is true) sendHttpHeaders: false, // HTTP Content-Disposition. // Defaults to 'attachment', where FILENAME is the specified filename. // Note that this does nothing if you are not sending HTTP headers. contentDisposition: 'attachment', // Output Name for HTTP Content-Disposition // Defaults to no name outputName: "example.zip", // HTTP Content-Type. // Defaults to 'application/x-zip'. // Note that this does nothing if you are not sending HTTP headers. contentType: 'application/x-zip', // Set the function called for setting headers. // Default is the `header()` of PHP httpHeaderCallback: header(...), // Enable streaming files with single read where general purpose bit 3 // indicates local file header contain zero values in crc and size // fields, these appear only after file contents in data descriptor // block. // Set to true if your input stream is remote // (used with addFileFromStream()). // Default is false. defaultEnableZeroHeader: false, // Enable zip64 extension, allowing very large archives // (> 4Gb or file count > 64k) // Default is true enableZip64: true, // Flush output buffer after every write // Default is false flushOutput: true, ); ``` -------------------------------- ### ZipStream Constructor Source: https://context7.com/maennchen/zipstream-php/llms.txt Demonstrates how to create a ZipStream instance with basic and advanced configuration options. ```APIDOC ## ZipStream Constructor ### Description Instantiates the ZipStream class, allowing for configuration of archive name, HTTP headers, compression methods, and more. ### Method `__construct(array $options = [])` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```php addFile('hello.txt', 'Hello World!'); $zip->finish(); ``` ### Response #### Success Response (200) N/A (This is a constructor) #### Response Example N/A ``` -------------------------------- ### Add Files with Different Compression Methods Source: https://context7.com/maennchen/zipstream-php/llms.txt Demonstrates adding files to a ZIP archive using different compression methods (STORE, DEFLATE) and specifying compression levels. Use STORE for pre-compressed content like images or archives. ```php addFile( fileName: 'image.jpg', data: file_get_contents('/path/to/image.jpg'), compressionMethod: CompressionMethod::STORE, ); // DEFLATE: Standard compression - slower, smaller file size // Best for text files, documents, data files $zip->addFile( fileName: 'document.txt', data: file_get_contents('/path/to/document.txt'), compressionMethod: CompressionMethod::DEFLATE, deflateLevel: 9, // Maximum compression (1-9) ); // Use STORE for pre-compressed content $zip->addFile( fileName: 'archive.tar.gz', data: file_get_contents('/path/to/archive.tar.gz'), compressionMethod: CompressionMethod::STORE, ); $zip->finish(); ``` -------------------------------- ### Create ZipStream Instance Source: https://context7.com/maennchen/zipstream-php/llms.txt Instantiate ZipStream with basic or advanced configuration options for archive behavior, output, and compression. ```php addFile('hello.txt', 'Hello World!'); $zip->finish(); ``` -------------------------------- ### Configure ZipStream Archive Options Source: https://github.com/maennchen/zipstream-php/wiki/Available-options Instantiate and configure Archive options before passing them to the ZipStream constructor. Use setters to modify values like output stream, deflate level, comments, and large file handling. ```php use ZipStream\ZipStream; use ZipStream\Option\Archive as ArchiveOptions; require_once 'vendor/autoload.php'; $opt = new ArchiveOptions(); // Define output stream (argument is of type resource) $opt->setOutputStream($fd); // Set the deflate level (default is 6; use -1 to disable it) $opt->setDeflateLevel(6); // Add a comment to the zip file $opt->setComment('This is a comment.'); // Size, in bytes, of the largest file to try and load into memory (used by addFileFromPath()). Large files may also be compressed differently; see the 'largeFileMethod' option. $opt->setLargeFileSize(30000000); // How to handle large files. Legal values are STORE (the default), or DEFLATE. Store sends the file raw and is significantly faster, while DEFLATE compresses the file and is much, much slower. Note that deflate must compress the file twice and is extremely slow. $opt->setLargeFileMethod(ZipStream\Option\Method::STORE()); $opt->setLargeFileMethod(ZipStream\Option\Method::DEFLATE()); // Send http headers (default is false) $opt->setSendHttpHeaders(false); // HTTP Content-Disposition. Defaults to 'attachment', where FILENAME is the specified filename. Note that this does nothing if you are not sending HTTP headers. $opt->setContentDisposition('attachment'); // Set the content type (does nothing if you are not sending HTTP headers) $opt->setContentType('application/x-zip'); // Set the function called for setting headers. Default is the `header()` of PHP $opt->setHttpHeaderCallback('header'); // Enable streaming files with single read where general purpose bit 3 indicates local file header contain zero values in crc and size fields, these appear only after file contents in data descriptor block. Default is false. Set to true if your input stream is remote (used with addFileFromStream()). $opt->setZeroHeader(false); // Enable reading file stat for determining file size. When a 32-bit system reads file size that is over 2 GB, invalid value appears in file size due to integer overflow. Should be disabled on 32-bit systems with method addFileFromPath if any file may exceed 2 GB. In this case file will be read in blocks and correct size will be determined from content. Default is true. $opt->setStatFiles(true); // Enable zip64 extension, allowing very large archives (> 4Gb or file count > 64k) // default is true $opt->setEnableZip64(true); // Flush output buffer after every write // default is false $opt->setFlushOutput(true); // Now that everything is set you can pass the options to the ZipStream instance $zip = new ZipStream('example.zip', $opt); ``` -------------------------------- ### ZipStream with S3 Multipart Upload Source: https://github.com/maennchen/zipstream-php/blob/main/guides/LargeFilesToS3.rst Use this snippet to compress files and upload them in parts to an S3 bucket using ZipStream and a custom buffer stream. Ensure you have the AWS SDK for PHP and ZipStream installed. ```php 'your region', 'version' => 'latest', 'bucketName' => $bucket, 'credentials' => CredentialProvider::defaultProvider(), ]); $bufferStream = new MultipartUploadBufferStream( 'destination-file.zip', $bucket, $client, ); $zip = new ZipStream( outputStream: $destination, defaultCompressionMethod: CompressionMethod::STORE, defaultEnableZeroHeader: true, sendHttpHeaders: false, ); $zip->addFile( fileName: 'big-file-1.txt', data: 'File1 data', ); $zip->addFile( fileName: 'big-file-2.txt', data: 'File2 data', ); $zip->finish(); $destination->close(); // Needed after $zip->finish() to upload the last remaining bytes to S3 ``` -------------------------------- ### Add File with Deferred Loading (Callback) Source: https://context7.com/maennchen/zipstream-php/llms.txt Use `addFileFromCallback` for deferred file loading, where the file is only opened when it's needed during archive creation. This is efficient for handling many files. You can also provide `exactSize` or return a PSR-7 stream from the callback. ```php '/path/to/report1.pdf', 'report2.pdf' => '/path/to/report2.pdf', 'report3.pdf' => '/path/to/report3.pdf', ]; foreach ($files as $zipName => $path) { $zip->addFileFromCallback( fileName: "reports/$zipName", callback: function () use ($path) { return fopen($path, 'rb'); }, ); } // With known exact size for efficiency $zip->addFileFromCallback( fileName: 'known-size.txt', exactSize: 1000, callback: function (): string { return str_repeat('A', 1000); }, ); // Return PSR-7 stream from callback $zip->addFileFromCallback( fileName: 'api-data.json', callback: function (): StreamInterface { $client = new \GuzzleHttp\Client(); $response = $client->get('https://api.example.com/data'); return $response->getBody(); }, ); $zip->finish(); ``` -------------------------------- ### Create and Save Zip Archive with Flysystem Source: https://github.com/maennchen/zipstream-php/wiki/FlySystem-Example This snippet demonstrates how to create a zip archive in memory, add files to it, and then save it to a specified location using Flysystem. Ensure the stream is opened in 'w+' mode for read/write access. ```php // Open Stream only once for read and write since it's a memory stream and // the content is lost when closing the stream / opening another one $tempStream = fopen('php://memory', 'w+'); // Init Options $zipStreamOptions = new Archive(); $zipStreamOptions->setOutputStream($tempStream); // Create Zip Archive $zipStream = new ZipStream('test.zip', $zipStreamOptions); $zipStream->addFile('test.txt', 'text'); $zipStream->finish(); // Store File (see Flysystem documentation, and all its framework integration) $adapter = new Local(__DIR__.'/path/to/folder'); // Can be any adapter (AWS, Google, Ftp, etc.) $filesystem = new Filesystem($adapter); $filesystem->putStream('test.zip', $tempStream) // Close Stream fclose($tempStream); ``` -------------------------------- ### Configure ZipStream for Content-Length Header Source: https://github.com/maennchen/zipstream-php/blob/main/guides/ContentLength.rst Use SIMULATION_STRICT or SIMULATION_LAX in operationMode to enable Content-Length header calculation. SIMULATION_STRICT is efficient as it avoids reading the whole file. ```php use ZipStream\OperationMode; use ZipStream\ZipStream; $zip = new ZipStream( operationMode: OperationMode::SIMULATE_STRICT, // or SIMULATE_LAX defaultEnableZeroHeader: false, sendHttpHeaders: true, outputStream: $stream, ); // Normally add files $zip->addFile('sample.txt', 'Sample String Data'); // Use addFileFromCallback and exactSize if you want to defer opening of // the file resource $zip->addFileFromCallback( 'sample.txt', exactSize: 18, callback: function () { return fopen('...'); } ); // Read resulting file size $size = $zip->finish(); // Tell it to the browser header('Content-Length: '. $size); // Execute the Simulation and stream the actual zip to the client $zip->executeSimulation(); ``` -------------------------------- ### addFileFromPath - Add File from Local Path Source: https://context7.com/maennchen/zipstream-php/llms.txt Details how to add files to the zip archive directly from the local filesystem. ```APIDOC ## addFileFromPath - Add File from Local Path ### Description Adds a file from the local filesystem to the archive. It automatically reads the file's modification time and handles file access errors. ### Method `addFileFromPath(string $fileName, string $path, ?string $comment = null, ?CompressionMethod $compressionMethod = null, ?int $deflateLevel = null, ?DateTimeInterface $lastModificationDateTime = null): void` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```php addFileFromPath( fileName: 'images/logo.png', path: '/var/www/assets/logo.png', ); // Add multiple files from directory $files = glob('/var/www/uploads/*.pdf'); foreach ($files as $filePath) { $zip->addFileFromPath( fileName: 'pdfs/' . basename($filePath), path: $filePath, comment: 'Uploaded document', ); } // Add with custom modification time $zip->addFileFromPath( fileName: 'archive/old_document.doc', path: '/backup/document.doc', lastModificationDateTime: new DateTime('2023-06-01'), ); $zip->finish(); } catch (FileNotFoundException $e) { error_log("File not found: " . $e->path); } catch (FileNotReadableException $e) { error_log("File not readable: " . $e->path); } ``` ### Response #### Success Response (200) N/A (This method modifies the zip archive in memory) #### Response Example N/A ``` -------------------------------- ### Basic ZipStream-PHP Usage Source: https://github.com/maennchen/zipstream-php/blob/main/README.md Create a new ZipStream object, add files by content or path, and finish the stream. Ensure dependencies are autoloaded. ```php // Autoload the dependencies require 'vendor/autoload.php'; // create a new zipstream object $zip = new ZipStream\ZipStream( outputName: 'example.zip', // enable output of HTTP headers sendHttpHeaders: true, ); // create a file named 'hello.txt' $zip->addFile( fileName: 'hello.txt', data: 'This is the contents of hello.txt', ); // add a file named 'some_image.jpg' from a local file 'path/to/image.jpg' $zip->addFileFromPath( fileName: 'some_image.jpg', path: 'path/to/image.jpg', ); // finish the zip stream $zip->finish(); ``` -------------------------------- ### Create and Save Zip Archive with FlySystem Source: https://github.com/maennchen/zipstream-php/blob/main/guides/FlySystem.rst Use a memory stream for the zip archive and then save it using a Flysystem adapter. Ensure the stream is opened for read/write and closed only after writing to Flysystem. ```php // Open Stream only once for read and write since it's a memory stream and // the content is lost when closing the stream / opening another one $tempStream = fopen('php://memory', 'w+'); // Create Zip Archive $zipStream = new ZipStream( outputStream: $tempStream, outputName: 'test.zip', ); $zipStream->addFile('test.txt', 'text'); $zipStream->finish(); // Store File // (see Flysystem documentation, and all its framework integration) // Can be any adapter (AWS, Google, Ftp, etc.) $adapter = new Local(__DIR__.'/path/to/folder'); $filesystem = new Filesystem($adapter); $filesystem->writeStream('test.zip', $tempStream) // Close Stream fclose($tempStream); ``` -------------------------------- ### Simulate Archive Size with SIMULATE_LAX Source: https://context7.com/maennchen/zipstream-php/llms.txt Employs SIMULATE_LAX operation mode to calculate archive size, which can read files or use exact sizes provided. This mode supports deflation and is useful when file sizes are not initially known but can be determined by reading. ```php addFileFromCallback( fileName: 'data.txt', exactSize: 100, callback: fn() => str_repeat('X', 100), ); $size = $zipLax->finish(); header('Content-Length: ' . $size); $zipLax->executeSimulation(); ``` -------------------------------- ### addFile - Add File from String Data Source: https://context7.com/maennchen/zipstream-php/llms.txt Explains how to add files to the zip archive using string content, with options for compression and metadata. ```APIDOC ## addFile - Add File from String Data ### Description Adds a file to the archive using string data as its content. Supports optional compression settings, comments, and modification timestamps. ### Method `addFile(string $fileName, string $data, ?string $comment = null, ?CompressionMethod $compressionMethod = null, ?int $deflateLevel = null, ?DateTimeInterface $lastModificationDateTime = null): void` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```php addFile( fileName: 'readme.txt', data: 'This is the contents of the readme file.', ); // File with custom options $zip->addFile( fileName: 'documents/report.txt', data: $reportContent, comment: 'Monthly sales report', compressionMethod: CompressionMethod::DEFLATE, deflateLevel: 9, lastModificationDateTime: new DateTime('2024-01-15 10:30:00'), ); // JSON data file $zip->addFile( fileName: 'data/config.json', data: json_encode(['version' => '1.0', 'settings' => []]), compressionMethod: CompressionMethod::STORE, // No compression for small files ); $zip->finish(); ``` ### Response #### Success Response (200) N/A (This method modifies the zip archive in memory) #### Response Example N/A ``` -------------------------------- ### Implement Multipart Upload Buffer Stream for S3 Source: https://github.com/maennchen/zipstream-php/blob/main/guides/LargeFilesToS3.rst This class implements a PSR-7 stream to buffer ZipStream's output and upload it to S3 in chunks using multipart upload. It handles creating the multipart upload, uploading parts, and completing the upload. ```php client->createMultipartUpload( [ 'Bucket' => $bucket, 'Key' => $destinationFileName, 'StorageClass' => 'REDUCED_REDUNDANCY', ] ); $this->uploadId = $result['UploadId']; $this->parts['Parts'] = []; } public function write($string): int { $chunkSize = strlen($string); $this->buffer .= $string; $this->bufferSize += $chunkSize; if ($this->bufferSize >= self::PART_SIZE) { $this->uploadPart(); } return $chunkSize; } public function close(): void { // Upload remaining closing bytes from zip $this->uploadPart(); $this->client->completeMultipartUpload([ 'Bucket' => $this->bucket, 'Key' => $this->destinationFileName, 'UploadId' => $this->uploadId, 'MultipartUpload' => $this->parts, ]); $this->buffer = ''; $this->bufferSize = 0; } private function uploadPart(): void { $result = $this->client->uploadPart([ 'Bucket' => $this->bucket, 'Key' => $this->destinationFileName, 'UploadId' => $this->uploadId, 'PartNumber' => $this->partNumber, 'Body' => $this->buffer, ]); $this->buffer = ''; $this->bufferSize = 0; $this->parts['Parts'][$this->partNumber] = [ 'PartNumber' => $this->partNumber, 'ETag' => $result['ETag'], ]; $this->partNumber++; $result = null; gc_collect_cycles(); // To avoid memory leaks. @see github.com/aws/aws-sdk-php/issues/1273 } public function __toString(): string { return $this->getContents(); } public function getContents(): string { $buffer = $this->buffer; $this->buffer = ''; $this->bufferSize = 0; return $buffer; } public function detach(): null { $this->close(); return null; } public function getSize(): int { return $this->bufferSize; } public function isReadable(): bool { return true; } public function isWritable(): bool { return true; } public function isSeekable(): bool { return false; } public function rewind(): void { $this->seek(0); } public function seek($offset, $whence = SEEK_SET): void { throw new \RuntimeException('Cannot seek a BufferStream'); } public function eof(): bool { return $this->bufferSize === 0; } public function tell(): int { throw new \RuntimeException('Cannot determine the position of a BufferStream'); } public function read($length): string { $currentLength = $this->bufferSize; if ($length >= $currentLength) { // No need to slice the buffer because we don't have enough data. $result = $this->buffer; $this->buffer = ''; $this->bufferSize = 0; } else { ``` -------------------------------- ### Add File from Local Path Source: https://context7.com/maennchen/zipstream-php/llms.txt Add files to the archive directly from the local filesystem. Handles modification times and file access errors. ```php addFileFromPath( fileName: 'images/logo.png', path: '/var/www/assets/logo.png', ); // Add multiple files from directory $files = glob('/var/www/uploads/*.pdf'); foreach ($files as $filePath) { $zip->addFileFromPath( fileName: 'pdfs/' . basename($filePath), path: $filePath, comment: 'Uploaded document', ); } // Add with custom modification time $zip->addFileFromPath( fileName: 'archive/old_document.doc', path: '/backup/document.doc', lastModificationDateTime: new DateTime('2023-06-01'), ); $zip->finish(); } catch (FileNotFoundException $e) { error_log("File not found: " . $e->path); } catch (FileNotReadableException $e) { error_log("File not readable: " . $e->path); } ``` -------------------------------- ### Configure Varnish to Pipe Large Files Source: https://github.com/maennchen/zipstream-php/wiki/Varnish Add this to your VCL file to enable the pipe directive for archive files. This prevents Varnish from closing the stream prematurely when serving large zip, tar, or gz files. ```vcl sub vcl_recv { # Varnish can’t intercept the discussion anymore # helps for streaming big zips if (req.url ~ "\.(tar|gz|zip|7z|exe)$") { return (pipe); } } # Varnish can’t intercept the discussion anymore # helps for streaming big zips sub vcl_pipe { set bereq.http.connection = "close"; return (pipe); } ``` -------------------------------- ### addFileFromCallback - Add File with Deferred Loading Source: https://context7.com/maennchen/zipstream-php/llms.txt Allows deferred file loading through a callback function, useful for handling many files without keeping all file handles open simultaneously. ```APIDOC ## addFileFromCallback - Add File with Deferred Loading ### Description The `addFileFromCallback` method allows deferred file loading through a callback function, useful for handling many files without keeping all file handles open simultaneously. ### Method `addFileFromCallback` ### Parameters #### Path Parameters - **fileName** (string) - Required - The desired name of the file within the zip archive. - **callback** (callable) - Required - A callable function that returns a file resource (stream) or a PSR-7 `StreamInterface` when executed. - **exactSize** (int) - Optional - The exact size of the content to be generated by the callback in bytes. Providing this can improve efficiency. - **enableZeroHeader** (bool) - Optional - If true, skips the ZIP header for this entry. Defaults to false. ### Request Example ```php '/path/to/report1.pdf', 'report2.pdf' => '/path/to/report2.pdf', 'report3.pdf' => '/path/to/report3.pdf', ]; foreach ($files as $zipName => $path) { $zip->addFileFromCallback( fileName: "reports/$zipName", callback: function () use ($path) { return fopen($path, 'rb'); }, ); } // With known exact size for efficiency $zip->addFileFromCallback( fileName: 'known-size.txt', exactSize: 1000, callback: function (): string { return str_repeat('A', 1000); }, ); // Return PSR-7 stream from callback $zip->addFileFromCallback( fileName: 'api-data.json', callback: function (): StreamInterface { $client = new \GuzzleHttp\Client(); $response = $client->get('https://api.example.com/data'); return $response->getBody(); }, ); $zip->finish(); ?> ``` ### Response This method does not return a value directly but modifies the zip archive being built. ``` -------------------------------- ### Simulate Archive Size with SIMULATE_STRICT Source: https://context7.com/maennchen/zipstream-php/llms.txt Uses SIMULATE_STRICT operation mode to calculate archive size without actual compression. This mode requires known file sizes and no deflation, suitable for sending a Content-Length header before streaming. ```php addFile('file1.txt', 'Content of file 1'); $zip->addFile('file2.txt', 'Content of file 2'); // finish() returns the calculated size during simulation $size = $zip->finish(); // Send Content-Length header header('Content-Type: application/zip'); header('Content-Disposition: attachment; filename="download.zip"'); header('Content-Length: ' . $size); // Execute simulation - actually stream the zip $zip->executeSimulation(); ``` -------------------------------- ### Add File from String Data Source: https://context7.com/maennchen/zipstream-php/llms.txt Add files to the archive using string data as content. Supports optional compression, comments, and modification timestamps. ```php addFile( fileName: 'readme.txt', data: 'This is the contents of the readme file.', ); // File with custom options $zip->addFile( fileName: 'documents/report.txt', data: $reportContent, comment: 'Monthly sales report', compressionMethod: CompressionMethod::DEFLATE, deflateLevel: 9, lastModificationDateTime: new DateTime('2024-01-15 10:30:00'), ); // JSON data file $zip->addFile( fileName: 'data/config.json', data: json_encode(['version' => '1.0', 'settings' => []]), compressionMethod: CompressionMethod::STORE, // No compression for small files ); $zip->finish(); ``` -------------------------------- ### Stream ZIP to Multiple Destinations with CallbackStreamWrapper Source: https://context7.com/maennchen/zipstream-php/llms.txt Utilizes CallbackStreamWrapper to stream ZIP data to multiple output streams simultaneously, such as files and direct output, while also enabling progress tracking. ```php addFile('hello.txt', 'Hello World!'); $zip->finish(); fclose($outputFile); fclose($backupFile); ``` ```php // Progress tracking example $totalBytes = 0; $zip = new ZipStream( outputStream: CallbackStreamWrapper::open(function (string $data) use (&$totalBytes) { $totalBytes += strlen($data); // Report progress (e.g., to websocket, database, log) error_log("Progress: $totalBytes bytes written"); echo $data; }), sendHttpHeaders: false, ); $zip->addFile('large-file.txt', str_repeat('A', 100000)); $zip->finish(); // $totalBytes now contains total bytes written ``` -------------------------------- ### Add File from PSR-7 Stream Source: https://context7.com/maennchen/zipstream-php/llms.txt The `addFileFromPsr7Stream` method is for adding files from PSR-7 compatible streams, such as HTTP response bodies. Providing `exactSize` can improve efficiency. `enableZeroHeader` is recommended for large or remote streams. ```php get('https://api.example.com/files/document.pdf'); $zip->addFileFromPsr7Stream( fileName: 'downloads/document.pdf', stream: $response->getBody(), enableZeroHeader: true, ); // From Guzzle PSR-7 stream $psr7Stream = Utils::streamFor('PSR-7 stream content'); $zip->addFileFromPsr7Stream( fileName: 'psr7/content.txt', stream: $psr7Stream, ); // With exact size known (more efficient) $zip->addFileFromPsr7Stream( fileName: 'known-size/file.dat', stream: $dataStream, exactSize: 1024, // Size in bytes enableZeroHeader: false, ); $zip->finish(); ``` -------------------------------- ### ZipStream Exception Handling Source: https://context7.com/maennchen/zipstream-php/llms.txt Handle specific ZipStream exceptions for various error conditions during archive creation. This includes FileNotFoundException, FileNotReadableException, OverflowException, and FileSizeIncorrectException. ```php addFileFromPath('missing.txt', '/nonexistent/path.txt'); } catch (FileNotFoundException $e) { error_log("File not found: {$e->path}"); } try { // FileNotReadableException - file exists but cannot be read $zip->addFileFromPath('protected.txt', '/root/protected-file.txt'); } catch (FileNotReadableException $e) { error_log("Cannot read file: {$e->path}"); } try { // OverflowException - archive exceeds 4GB without Zip64 $zip->addFile('huge.bin', str_repeat('X', PHP_INT_MAX)); $zip->finish(); } catch (OverflowException $e) { error_log("Archive too large. Enable Zip64 option."); } try { // FileSizeIncorrectException - exactSize doesn't match actual size $zip->addFileFromCallback( fileName: 'wrong-size.txt', exactSize: 100, callback: fn() => 'Only 10 bytes', ); } catch (FileSizeIncorrectException $e) { error_log("File size mismatch"); } ``` -------------------------------- ### Add File from PHP Stream Source: https://context7.com/maennchen/zipstream-php/llms.txt Use `addFileFromStream` to add files from PHP stream resources like temporary files, network connections, or memory streams. Ensure streams are rewound before adding and closed afterward. `enableZeroHeader` is recommended for large or remote streams. ```php addFileFromStream( fileName: 'temp/sample.txt', stream: $tempStream, ); // From file pointer $fileStream = fopen('/path/to/large-file.bin', 'rb'); $zip->addFileFromStream( fileName: 'binaries/large-file.bin', stream: $fileStream, enableZeroHeader: true, // Recommended for large/remote streams ); // From PHP memory stream $memoryStream = fopen('php://memory', 'r+'); fwrite($memoryStream, 'In-memory content'); rewind($memoryStream); $zip->addFileFromStream( fileName: 'memory/data.txt', stream: $memoryStream, ); $zip->finish(); // Clean up streams fclose($tempStream); fclose($fileStream); fclose($memoryStream); ```