igorw/file-serve-bundle
Symfony bundle for serving static files through your application, with efficient delivery and support for web-server file serving. Handy for protected downloads and streaming files without exposing real paths on disk.
First, calibrate expectations: this is a Symfony 2 bundle, not a Laravel package. There is no service provider, no facade, no config stub — it wires into AppKernel and the Symfony DIC. So "getting started" here means either (a) you're maintaining a legacy Symfony 2 app that already has it, or (b) you're reading it as a reference implementation for the 30 lines of logic it contains and re-implementing it in Laravel.
Where to look first:
Igorw/FileServeBundle/FileServe.php — the entire package is essentially this class. It exposes a createResponse($path) method returning a BinaryFileResponse.DependencyInjection/Configuration.php — the only real option is x_sendfile (offload byte transfer to the web server).Minimal steps (Symfony 2): register new Igorw\FileServeBundle\IgorwFileServeBundle() in AppKernel, add a one-line igorw_file_serve: { x_sendfile: true } to config.yml only if your web server is configured for it, then inject the igorw_file_serve service and call ->createResponse($path) after your own authorization check.
First use case: serving an invoice PDF stored outside web/ (e.g. app/../data/invoices/1234.pdf) to a logged-in user. The bundle builds a BinaryFileResponse with correct Content-Type, Content-Length, and Last-Modified, and optionally just emits an X-Sendfile header instead of streaming bytes through PHP.
Laravel equivalent: return response()->file($path); or return response()->download($path, $name, $headers);. That is the entire feature set — worth knowing before you spend time on an adapter.
Gate/policy (or Symfony voter), then pass the resolved path to createResponse().FileServe in the constructor. The $this->get('igorw_file_serve') style you'll see in old examples is Symfony 2.0-era and untestable.GET /files/{id} → controller → createResponse($file->getPath()) → optional setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $file->getOriginalName()) for downloads vs. inline.x_sendfile: true plus mod_xsendfile (Apache) or X-Accel-Redirect (Nginx), PHP-FPM sends a header and exits; the web server streams the bytes. This is the difference between a 200 MB download tying up a PHP worker and not.BinaryFileResponse sets Last-Modified and Content-Length for you; add ETag/Cache-Control yourself on the response object. Ranged requests are handled natively as long as you don't call setContent().response()->file($path)response()->download($path, $name)->deleteFileAfterSend(true)Storage::disk('s3')->download($key) or response()->streamDownload(fn () => ..., $name)response()->file() if files are local and small.composer require it into Laravel. It depends on Symfony 2's HttpFoundation and kernel contracts; Laravel ships a much newer HttpFoundation. There is no shim, and with 0 dependents on record, nobody is maintaining one.x_sendfile: true fails silently. Enable it without the Apache module or the Nginx internal location and you get empty or truncated responses with a 200 status. Verify with curl -I: you should see X-Sendfile (Apache) or X-Accel-Redirect (Nginx) and PHP should emit no body.X-Sendfile entirely. It needs X-Accel-Redirect pointing at a URI, plus a matching location /internal/ { internal; alias /srv/files/; }. Sending an absolute filesystem path is the classic mistake and produces a 404 from Nginx, not from PHP.BinaryFileResponse. Feed it user input and you have local file inclusion. Build the path server-side from a trusted root.new Response(file_get_contents($path)). That's the anti-pattern this bundle exists to avoid; it loads the whole file into PHP memory. BinaryFileResponse, response()->file(), and streamDownload() all avoid it.Content-Length vs. stat() size mismatch → X-Sendfile misconfig. 404 from the web server with a valid PHP path → wrong header type for your server. 403 → the PHP-FPM user can't read the file even though your shell user can.BinaryFileResponse; subclassing it buys you nothing over writing your own six-line service. If you need range requests, S3, or signed URLs, you're writing custom code either way — in Laravel, start from response()->streamDownload().How can I help you explore Laravel packages today?