Laravel Image Upload not saving to public folder
Stefan Bogdanescu
Founder & Senior Architect · 2026-06-29
Laravel Image Upload Fiasco: Why Your Files Don't Show Up in Public
As a senior developer, I’ve seen countless frustrations with file uploads in modern frameworks. The scenario you're describing—where the database stores a path, but the actual files are missing from the public directory—is one of the most common stumbling blocks when working with Laravel's powerful Filesystem abstraction. It usually boils down to a misunderstanding of how storage disks, local paths, and public access interact within the framework.
Let’s dive into your specific scenario, analyze the logic you provided, and fix this image upload issue permanently.
Diagnosing the Problem: Disk vs. Storage Location
The core issue is likely not in how you are saving the file (using storeAs), but rather in where that file is being saved conceptually, and how Laravel's public access mechanism is configured.
When you use methods like $uploadedFile->storeAs($folder, $filename, 'public'), you are instructing Laravel to save the file onto a specific storage "disk." If this disk isn't properly mapped to a publicly accessible location (like the public directory via symbolic links), the resulting path stored in your database will be syntactically correct but point to an inaccessible location on the server.
The symptoms you described—seeing a random temporary file path in the DB and no actual image—strongly suggest that while the file exists somewhere within the application's storage structure, it is not accessible via the standard web routes.
Reviewing Your Code Implementation
Let’s examine your provided logic to pinpoint where the disconnect occurs:
The Controller Logic (PostController.php)
// ... inside store method
$folder = '/uploads/images/';
$filePath = $folder . $name . '.' . $image->getClientOriginalExtension();
$this->uploadOne($image, $folder, 'public', $name); // This uses the trait
$post->post_image = Storage::url($filePath); // Accessing the file via URL helper
// ...
The issue here is how you are mixing raw file paths ($filePath) with Laravel's storage facade. When you use storeAs and specify a disk (like 'public'), Laravel handles the internal path management. You should rely on the Storage facade methods rather than manually concatenating paths if you want robust, framework-aware handling.
The Upload Trait (UploadTrait.php)
public function uploadOne(UploadedFile $uploadedFile, $folder = null, $disk = 'public', $filename = null)
{
$name = !is_null($filename) ? $filename : Str::random(25);
// This line is the key: it uses the disk specified ('public') correctly.
$file = $uploadedFile->storeAs($folder, $name . '.' . $uploadedFile->getClientOriginalExtension(), $disk);
return $file;
}
The use of storeAs with the 'public' disk is correct for saving the file to the configured public storage location. The problem then shifts from saving the file to retrieving it correctly in the model and view.
The Solution: Leveraging Laravel's Storage Facade
Instead of manually constructing paths, let the Storage facade handle everything. This ensures that your application remains portable and respects the configured filesystem settings.
1. Refactor the Controller to Use Full Paths
You should use the Storage facade directly when saving files, ensuring you are working with the storage system itself:
use Illuminate\Support\Facades\Storage; // Make sure this is imported
// ... inside store method
if ($request->has('post_image')) {
$image = $request->file('post_image');
$name = Str::slug($request->input('title')) . '_' . time();
// Define the path relative to the disk root
$path = "uploads/images/" . $name . '.' . $image->getClientOriginalExtension();
// Store the file using the public disk
$disk = 'public';
$file = $image->storeAs($path, $disk);
// Store the relative path in the database
$post->post_image = $file; // Store the path returned by storeAs
// ... rest of your logic
}
2. Model and View Retrieval
When you retrieve this file later, you must use Storage::url() or asset() to generate the correct web-accessible link based on the stored relative path:
In your Post model (or attribute accessor):
public function getImageAttribute()
{
// Use the stored path and prepend the public disk root
return Storage::disk('public')->url($this->post_image);
}
By structuring your code this way, you delegate the complex task of mapping internal storage to external web URLs to Laravel. This aligns perfectly with the principles of building robust applications using modern Laravel features, as promoted by resources like laravelcompany.com.
Conclusion
File upload issues in Laravel are rarely about incorrect file handling; they are almost always about incorrect filesystem configuration or misusing the Storage facade. By shifting from manual path concatenation to utilizing $file->storeAs() with a specified disk, and then using Storage::url() for retrieval, you ensure that your application correctly interacts with its storage layer. Always remember to check your config/filesystems.php to confirm that the 'public' disk is set up correctly to point to your web-accessible directory. Happy coding!