Weave 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

Unduhan Laravel Package

nuewire/unduhan

nuewire/unduhan is a Laravel/PHP package for simplifying file downloads, helping developers organize, serve, and manage downloadable content in their applications with a lightweight, framework-friendly setup. Ideal for projects that need a clean, reusable download workflow without extra complexity.

View on GitHub
Deep Wiki
Context7

Getting Started

Version reviewed: 1.0.1 (patch release on top of 1.0.0)

Release notes caveat: the 1.0.1 notes contain only a compare link (1.0.0...1.0.1) with no described changes. Before adopting, run git log --oneline 1.0.0..1.0.1 or open the compare URL to see what actually changed. A patch bump on a freshly-published 1.0.x package can be anything from a README fix to a silent behavioural correction, so treat 1.0.1 as "unverified parity with 1.0.0" rather than "guaranteed no-op".

Install and orient yourself:

composer require nuewire/unduhan

Then read, in this order:

  1. README.md — the public surface is almost certainly small (one facade or a couple of helpers), so the README is your real API reference.
  2. src/UnduhanServiceProvider.php — tells you what is registered: config file, facade alias, routes, migrations, Blade directives, macros.
  3. config/unduhan.php (after php artisan vendor:publish --tag=unduhan-config) — the knobs you'll actually tune: default disk, path/prefix, filename strategy, TTL for temporary links.
  4. tests/ — for a young package this is often the most honest documentation of intended behaviour.

First use case: serve a generated file (invoice PDF, CSV export, uploaded asset) through an authenticated route instead of exposing a storage path. That exercises the package's core responsibility — building the download response, setting disposition/filename headers, and optionally signing the URL — and will immediately reveal whether it wraps response()->download()/streamDownload() or does something more opinionated.

If the package ships routes or a facade alias, confirm they're registered with php artisan route:list --path=unduhan and php artisan about before writing any code against it.

Implementation Patterns

Thin controller, package does the response. Keep authorisation in your app and delegate only the response construction:

public function download(Invoice $invoice)
{
    $this->authorize('download', $invoice);

    return Unduhan::download($invoice->pdfPath(), "invoice-{$invoice->number}.pdf");
}

This keeps the package replaceable — if it stagnates, you swap the single call for response()->download(...) without touching the rest of the app. Resist letting it own authorisation or path resolution.

Signed / time-limited links. For anything emailed or shared, prefer a signed route over a raw storage URL:

URL::temporarySignedRoute('unduhan.download', now()->addMinutes(30), ['export' => $export->id]);

Then verify with the signed middleware. This is the main reason to reach for a download package at all — it centralises TTL and signature handling rather than duplicating it per feature.

Large files: stream, never load. For exports over a few MB, use the streaming path (streamDownload/readStream) rather than reading into memory. Pair with a queued job that writes to storage first, then redirect to the signed download URL once ready — the request/response cycle is the wrong place to generate big files.

Export workflow that scales:

  1. User requests export → dispatch job, return 202/redirect.
  2. Job writes to a dedicated disk (exports), applies retention/TTL.
  3. Notification/UI polls status, then hits the signed download route.
  4. Scheduled command prunes expired artefacts.

The package fits at step 3 only; steps 1, 2, and 4 are yours.

Testing. Storage::fake('exports') plus Laravel's assertDownload() covers most of it:

Storage::fake('exports');
Storage::disk('exports')->put('report.csv', 'a,b');

$this->get(route('unduhan.download', $export))
    ->assertOk()
    ->assertDownload('report.csv');

Add one test asserting an unauthorised user gets 403 and one asserting an expired signature gets 403 — those two are where download packages most often get misconfigured.

Blade integration. If a directive or component is provided, keep it to the anchor/button markup and let your own policy decide whether it renders. Don't let template code become the only place a URL is constructed.

Gotchas and Tips

Diff the patch before trusting it. With an empty changelog, the fastest sanity check is composer update nuewire/unduhan --with-dependencies in a branch and a run of your download tests. If nothing breaks and the diff is cosmetic, pin "nuewire/unduhan": "^1.0" and move on; if the diff touches response headers or config keys, pin the exact version until you've read it.

Headers already sent. Any echo, BOM, or stray whitespace before the response — including from a debug dump() — corrupts the download. Symptom: file arrives but is truncated or the browser reports a corrupt archive. This is the single most common failure mode with download helpers.

Non-ASCII filenames. Names with accents, CJK, or spaces need RFC 5987 encoding (filename*=UTF-8''...). If the package builds Content-Disposition by hand, test with a non-ASCII filename early; if it delegates to Symfony's BinaryFileResponse, you're fine.

Reverse proxy buffering. Behind Nginx, large streamed downloads get buffered and can hit fastcgi_read_timeout. Set X-Accel-Buffering: no (or the Nginx equivalent) for download routes and raise the timeout. Symptom: download stalls at ~30–60s with the process still running.

Disk configuration mismatch. storage_path() vs a configured disk vs an S3 bucket is a frequent source of 404s. Confirm the package resolves paths through Storage::disk($name) and that the same disk name is used in the job that writes the file and the route that serves it. S3 additionally needs correct visibility and a presigned URL — check the package's temporary-URL support actually delegates to Storage::temporaryUrl() rather than composing one itself.

Signature expiry and clock skew. Temporary signed URLs fail silently with a bare 403 and no reason. In local/dev, check APP_URL matches the host you're actually browsing on — a mismatched scheme or port invalidates the signature and is easy to misdiagnose as a package bug.

Output buffering in tests. assertDownload can mask real header issues. Once, do a manual curl -I against the route in a staging-like environment and confirm Content-Type, Content-Length (or Transfer-Encoding: chunked), and Content-Disposition are what you expect.

Extension points to check before forking. Look for config-driven disk/filename/TTL (usually enough), a container binding you can override, or a facade with a macro()-friendly accessor. If none exist and you find yourself needing behaviour changes, wrap the package in your own small service class rather than patching vendor code — at 1.0.1 you should assume the API will still move.

Maturity signal. Version 1.0.1 with an empty changelog and no visible maintainer notes suggests a solo/small-maintainer package. That's fine for a narrow download helper; it is not a reason to route all file delivery through it. Keep the call sites thin (one line per download) so an exit is cheap, and watch the repo for a 1.1.0 that adds config keys you'll want to review.

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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager