Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Zipstream Php Laravel Package

maennchen/zipstream-php

Stream ZIP archives on the fly in PHP without writing to disk. Fast ZIP downloads with optional HTTP headers, supports adding files from strings/paths, works with S3 and PSR-7 streams, and can output to custom callbacks.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require maennchen/zipstream-php
    

    Ensure your project uses PHP 8.2+ (check composer.json constraints).

  2. First Use Case: Stream a ZIP file directly to the browser without disk I/O:

    use ZipStream\ZipStream;
    
    $zip = new ZipStream(
        outputName: 'download.zip',
        sendHttpHeaders: true
    );
    $zip->addFile('hello.txt', 'Hello, ZipStream!');
    $zip->finish();
    
    • Key: sendHttpHeaders: true auto-sends Content-Type: application/zip and Content-Disposition.
  3. Where to Look First:

    • Official Documentation (API reference, examples).
    • ZipStream\CompressionMethod enum for compression options (e.g., CompressionMethod::DEFLATE).
    • addFile*() methods for different data sources (strings, files, streams).

Implementation Patterns

1. Dynamic ZIP Generation

Workflow: Build ZIPs on-the-fly from database records, API responses, or user uploads.

// Example: Zip user-uploaded files from a Laravel request
$zip = new ZipStream(outputName: 'user_uploads.zip');
foreach ($request->file('files') as $file) {
    $zip->addFileFromPath(
        fileName: $file->getClientOriginalName(),
        path: $file->getRealPath(),
        compressionMethod: CompressionMethod::DEFLATE
    );
}
$zip->finish();

2. Streaming to Storage (S3, Flysystem)

Integration Tip: Use CallbackStreamWrapper to pipe ZIP data to cloud storage:

use ZipStream\Stream\CallbackStreamWrapper;
use League\Flysystem\Filesystem;

$fs = new Filesystem($adapter);
$zip = new ZipStream(
    outputStream: CallbackStreamWrapper::open(
        fn(string $data) => $fs->writeStream('remote.zip', $data)
    ),
    sendHttpHeaders: false
);
// ... add files ...
$zip->finish();

3. Conditional Compression

Pattern: Optimize compression per file type/size:

$zip = new ZipStream(outputName: 'optimized.zip');
foreach ($files as $file) {
    $method = $file['size'] > 10_000_000 // >10MB
        ? CompressionMethod::STORE
        : CompressionMethod::DEFLATE;
    $zip->addFile($file['name'], $file['content'], compressionMethod: $method);
}

4. Laravel Integration

Tip: Create a service class for reusability:

// app/Services/ZipService.php
class ZipService {
    public function create(array $files, string $name): void {
        $zip = new ZipStream(outputName: $name);
        foreach ($files as $file) {
            $zip->addFile($file['name'], $file['content']);
        }
        $zip->finish();
    }
}

// Usage in Controller:
ZipService::create($files, 'export.zip');

5. Large File Handling

Best Practice: For files >4GB, ensure:

  • Zip64 is auto-enabled (no config needed in v3.1+).
  • Use CompressionMethod::STORE for uncompressed large files:
    $zip->addFileFromPath(
        'huge_file.dat',
        '/path/to/huge_file.dat',
        compressionMethod: CompressionMethod::STORE
    );
    

Gotchas and Tips

Pitfalls

  1. Headers Sent Twice:

    • If using sendHttpHeaders: true with frameworks like Laravel/Symfony, disable framework headers first:
      // Laravel: Remove default headers
      header_remove('Content-Type');
      $zip = new ZipStream(outputName: 'file.zip', sendHttpHeaders: true);
      
  2. Memory Leaks:

    • Avoid holding large streams in memory. Use addFileFromPath() for files >100MB instead of loading into PHP:
      // ❌ Bad: Loads entire file into memory
      $zip->addFile('large.txt', file_get_contents('/path/to/large.txt'));
      
      // ✅ Good: Streams directly
      $zip->addFileFromPath('large.txt', '/path/to/large.txt');
      
  3. Timezone Issues:

    • Zip timestamps use DOS time (UTC). Ensure lastModificationDateTime is timezone-aware:
      $zip->addFile(
          'file.txt',
          'content',
          lastModificationDateTime: new \DateTime('2023-01-01', new \DateTimeZone('UTC'))
      );
      
  4. CallbackStreamWrapper Quirks:

    • Order Matters: Data is written sequentially. For parallel writes (e.g., S3 + browser), buffer chunks:
      $zip = new ZipStream(
          outputStream: CallbackStreamWrapper::open(
              fn(string $chunk) => [
                  $s3Client->putObject(...), // Async
                  echo $chunk,               // Browser
              ]
          )
      );
      

Debugging Tips

  1. Validate ZIP Output: Use ZipArchive to verify:

    $zipData = file_get_contents('php://temp', 'r', null, 0, 0, true);
    $zip = new ZipArchive();
    $zip->openFromString($zipData);
    if ($zip->status !== ZipArchive::ER_OK) {
        throw new \RuntimeException('Invalid ZIP generated');
    }
    
  2. Log Stream Errors: Wrap CallbackStreamWrapper to catch failures:

    $zip = new ZipStream(
        outputStream: CallbackStreamWrapper::open(
            fn(string $data) => try {
                $fs->writeStream('backup.zip', $data);
            } catch (\Throwable $e) {
                Log::error('Zip stream failed', ['error' => $e]);
                throw $e;
            }
        )
    );
    

Extension Points

  1. Custom Compression: Extend via ZipStream\CompressionMethod or create a wrapper:

    class CustomCompressor implements \ZipStream\CompressionInterface {
        public function compress(string $data, int $level): string {
            // Custom logic (e.g., Brotli)
            return gzcompress($data, $level);
        }
    }
    

    Note: Requires internal API access (check GitHub Discussions).

  2. PSR-7 Stream Support: Use ZipStream\Stream\Psr7StreamWrapper for HTTP clients:

    use ZipStream\Stream\Psr7StreamWrapper;
    $zip = new ZipStream(
        outputStream: Psr7StreamWrapper::fromPsr7Stream($response->getBody())
    );
    
  3. Progress Tracking: Override ZipStream to emit events:

    class ProgressZipStream extends ZipStream {
        public function addFile(string $fileName, string $data): void {
            event(new ZipFileAdded($fileName));
            parent::addFile($fileName, $data);
        }
    }
    

Config Quirks

  • defaultEnableZeroHeader: Set to false if you need legacy ZIP compatibility (default: true).
  • Deflate Level: Override per file:
    $zip->addFile('file.txt', 'data', deflateLevel: 9);
    
  • Output Name: Empty string ('') still sends headers (set sendHttpHeaders: false to disable).
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope
anil/file-picker
broqit/fields-ai