Download from Laravel storage without loading whole file in memory
Stefan Bogdanescu
Founder & Senior Architect · 2026-06-29
# Serving Large Files in Laravel Storage: The Art of Streaming Without Memory Overload
As a senior developer working with frameworks like Laravel, managing file I/O efficiently is crucial, especially when dealing with large assets. When users request files larger than the server's configured memory limits, loading the entire file into memory—even temporarily—can lead to fatal errors, as you have experienced.
This post dives into why your initial attempt using `readStream()` might have failed and provides the robust, stream-based methodology for serving large files from Laravel Storage without exhausting your PHP memory limit.
## The Memory Trap with Large Files
Many developers attempt to solve this by reading the file content into a variable before sending the response. While simple, this approach fails spectacularly when dealing with multi-gigabyte files because the entire content must reside in the server's memory space at once.
Your inspiration from Stack Overflow utilizing `$fs->readStream($file->path)` and then piping it via `fpassthru()` is conceptually sound. The goal of streaming is to treat the file as a continuous flow of data rather than a monolithic block. However, subtle interactions between PHP streams, HTTP response objects, and specific server configurations (like memory limits) can cause unexpected behavior.
The error you encountered—`Allowed memory size of 134217728 bytes exhausted`—indicates that despite using the stream functions, the framework or the underlying stream implementation was still attempting to buffer too much data internally before sending it over the wire, leading to a fatal memory exhaustion error during the final output stage (`fpassthru()`).
## The Correct Approach: Direct Stream Piping
The key to successful large file delivery is ensuring that the PHP stream handle is directly piped to the HTTP response mechanism without intermediate buffering. Laravel’s storage system provides access to the underlying file streams, which we must leverage correctly.
Instead of relying solely on a generic `readStream()` followed by manual piping, the most reliable method involves using PHP's file stream functions directly within the response closure, ensuring that data is read and outputted incrementally chunk by chunk.
Here is the refined approach for serving files from Laravel Storage:
```php
use Illuminate\Support\Facades\Storage;
class FileController extends Controller
{
public function downloadLargeFile($fileName)
{
$path = Storage::disk('public')->path($fileName);
if (!file_exists($path)) {
abort(404, 'File not found.');
}
$fileHandle = fopen($path, 'rb');
// Set appropriate headers for file download
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($fileName) . '"');
header('Content-Length: ' . filesize($path));
// Stream the file directly to the output buffer
readfile($fileHandle);
fclose($fileHandle);
return response()->noContent(); // Or a simple stream response if preferred
}
}
```
### Why this works better:
1. **Direct File Handle:** We explicitly open the file using `fopen()`. This gives us direct control over the file pointer.
2. **Header Management:** Crucially, we set the necessary HTTP headers (`Content-Type`, `Content-Disposition`, and most importantly, `Content-Length`) *before* sending any data.
3. **`readfile()`:** The built-in PHP function `readfile()` is highly optimized for reading an entire file stream directly to the output buffer without loading the whole content into application memory. It reads the file in manageable chunks, which is exactly what we need for large files.
This method bypasses potential bottlenecks associated with higher-level abstraction layers when dealing with extreme memory constraints. For deep dives into how Laravel manages storage and file operations efficiently, consulting resources like [laravelcompany.com](https://laravelcompany.com) provides excellent context on building scalable applications.
## Conclusion
Serving large files effectively requires a shift in mindset from "reading the file" to "streaming the file." By utilizing native PHP stream functions like `fopen()` and `readfile()`, you gain precise control over memory usage. This technique ensures that your application remains responsive and avoids fatal memory exhaustion errors, allowing users to download massive assets seamlessly, regardless of their size. Always prioritize direct I/O when dealing with large data payloads in a Laravel environment.