Product Decisions This Supports
- Build vs. buy on export features: Bulk-export flows (CSV/PDF packs, invoice archives, "download all my attachments," admin data dumps, GDPR/DSAR packages) are a recurring request in almost every Laravel product. This package lets a single developer ship them as a streamed HTTP response instead of scoping a background-job + object-storage + cleanup workstream.
- Feature scope: Enables "select all / export everything" without a size cap negotiation, because the archive is never fully materialized in memory or on disk. That directly expands the product surface we can promise in a UI (e.g., "download 5 years of records").
- Architecture decision — synchronous vs. queued: Default to synchronous streaming for archives that can be produced in a few minutes; reserve queued/background generation for cases requiring approval, virus scanning, or retention. This avoids premature job-queue and storage infrastructure spend.
- Infrastructure/cost decision: Removes the need for a shared temp volume or scratch bucket on export endpoints, which matters on ephemeral containers, read-only filesystems, and small shared-hosting tiers. Lowers the risk of orphaned temp files filling disks and paging people.
- Perceived performance / UX roadmap: Time-to-first-byte improves because the browser begins downloading immediately; this is a measurable UX win versus "spinner for 90 seconds, then a download appears," and it justifies keeping exports in-product rather than emailing links.
- Source-agnostic exports: Works with local paths, S3 buckets, and PSR-7 streams, so the same endpoint can bundle DB rows, generated PDFs, and stored media without introducing a filesystem-coupling design.
- Roadmap sequencing signal: If export requests are low-frequency and small, this is a weekend-sized improvement — not a pillar. Treat it as a tactical unblocker that removes an excuse to defer adjacent features (audit logs on exports, download history).
When to Consider This Package
Adopt when most of these hold:
- Runtime fits: PHP 8.2+ on 64-bit, modern Laravel. The library tracks PHP's supported-version lifecycle and drops old majors, so older applications on PHP 8.0/8.1 are stuck on v2.
- The archive is generated top-to-bottom in one pass. Streaming ZIP cannot seek backward to patch entries, so anything needing post-processing of the archive (re-ordering, signing, injecting a manifest after the fact) is a poor fit.
- Content is already available or cheaply streamable — DB cursor results, generated documents, S3 objects. If every entry requires an expensive render, the bottleneck is generation, not zipping; solve that separately.
- You do not need to inspect the artifact before delivery. Because bytes are flushed as they are produced, you cannot scan, virus-check, or validate the completed archive before the user receives it.
- Your edge tolerates streaming. Proxies, CDNs, and gzip middleware that buffer responses silently defeat the entire benefit — validate this in staging, not production.
- Known-size downloads aren't mandatory. If a client requires an exact
Content-Length for progress UI, either use the built-in size simulation or reconsider.
Look elsewhere when:
- You need to read, extract, or modify existing archives — this is write-only; use
ZipArchive or a dedicated reader.
- You need password-protected/encrypted ZIPs — not supported. Use application-level encryption of individual files, or a different tool, and be explicit that "zip with password" is not a feature you can promise.
- You need archive-to-archive transformation, appending, or incremental updates.
- Throughput is the only concern and native
ZipArchive with disk is simpler — for tiny, low-volume archives the streaming benefit is marginal and the extra dependency earns nothing.
- Your team can't add an integration test that downloads and opens the produced ZIP. Streaming bugs (truncated output, header misconfiguration) are invisible in unit tests and only surface as "corrupt file" support tickets. If the testing appetite isn't there, prefer the boring path.
Dependency-risk read: MIT licensed, ~1.9k stars, multiple maintainers, recent releases, OpenSSF Best Practices and Scorecard badges, and a deliberately tiny public API surface (ZipStream, CompressionMethod, exception types). That is a favorable profile for a piece of plumbing that sits directly in a user-facing download path — but note the low dependents figure in the supplied metadata, which argues for pinning the version and covering it with our own tests rather than assuming broad third-party battle-testing.
How to Pitch It (Stakeholders)
For executives (2–3 sentences): Export and "download everything" features are among our most requested and most frequently deferred items, usually because the "zip it to disk, then send it" approach needs storage, cleanup jobs, and a queue. This small, MIT-licensed, actively maintained PHP library lets us stream the archive straight to the user instead — faster downloads, no temp-file sprawl, no new infrastructure, and roughly a week of work rather than a quarter. Risk is low and contained: one dependency, one primary class, and it only touches export endpoints.
For engineering leads: composer require maennchen/zipstream-php, then construct ZipStream in a controller and return a streamed response, adding entries with addFile / addFileFromPath. Complements response()->streamDownload(); works with Flysystem/S3 and any PSR-7 stream; CallbackStreamWrapper (3.2) lets us tee bytes to a backup or audit sink if we need a retained copy. Zip64 kicks in only when required, Zero-Header is on by default (that's what makes true streaming work without pre-computing sizes), and per-entry compression level and modification timestamps are configurable. Constraints to design around: write-only (no extraction), no ZIP password encryption, 64-bit PHP 8.2+, and the v3 API is the supported line — internal classes are marked @internal, so extend via the public API only. Testing plan: an end-to-end test that captures the streamed bytes and asserts the result opens as a valid archive with the expected file list, plus a staging check that no proxy is buffering the response.
One-line risk statement: The main failure mode isn't the library — it's environment and expectations: buffering middleboxes, teams assuming password-protected ZIPs, and clients expecting a precise Content-Length. All three are known up front and cheap to design around.