
Upload and download from Laravel private storage
In this article, I will share with you, how you can upload and download files from Laravel private storage. First, you need to make sure your config/filesystems.php file look like this.
<?php
// config/filesystems.php
return [
/*
|--------------------------------------------------------------------------
| Default Filesystem Disk
|--------------------------------------------------------------------------
|
| Here you may specify the default filesystem disk that should be used
| by the framework. The "local" disk, as well as a variety of cloud
| based disks are available to your application for file storage.
|
*/
'default' => "local", // env('FILESYSTEM_DISK', 'local'),
/*
|--------------------------------------------------------------------------
| Filesystem Disks
|--------------------------------------------------------------------------
|
| Below you may configure as many filesystem disks as necessary, and you
| may even configure multiple disks for the same driver. Examples for
| most supported storage drivers are configured here for reference.
|
| Supported Drivers: "local", "ftp", "sftp", "s3"
|
*/
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
'throw' => false,
],
// ... other disks
],
/*
|--------------------------------------------------------------------------
| Symbolic Links
|--------------------------------------------------------------------------
|
| Here you may configure the symbolic links that will be created when the
| `storage:link` Artisan command is executed. The array keys should be
| the locations of the links and the values should be their targets.
|
*/
'links' => [
public_path('storage') => storage_path('app/public'),
],
];
Show the form
First I need to show the form with an input field to select the file and a submit button. I will create a web route that when accessed will call a function in controller.
// routes/web.php
Route::get("/private-storage", [UserController::class, "private_storage"]);
Then in your controller class, create a function that will render a blade template.
// app\Http\Controllers\UserController.php
class UserController extends Controller
{
public function private_storage()
{
return view("private-storage");
}
}
After that, we need to create this view file and create a form in it.
// resources/views/private-storage.blade.php
<form onsubmit="doUpload(event);">
{{ csrf_field() }}
<input type="file" name="file" required />
<input type="submit" name="submit" value="Upload" />
</form>
<script>
function doUpload() {
event.preventDefault();
const form = event.target;
form.submit.setAttribute("disabled", "disabled");
const ajax = new XMLHttpRequest();
ajax.open("POST", "/upload-private", true);
ajax.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
form.submit.removeAttribute("disabled");
console.log(this.responseText);
}
}
const formData = new FormData(form);
ajax.send(formData);
}
</script>
This calls an AJAX and pass the FormData to it. Now we need to create a route that will handle this request.
Upload the file in Laravel private storage
First, I will create a POST route that will handle this request.
// routes/web.php
Route::post("/upload-private", [UserController::class, "upload_private"]);
Then in UserController we need to create this function.
// app\Http\Controllers\UserController.php
public function upload_private()
{
$file = request()->file("file");
$file_path = "files/" . uniqid() . "." . $file->getClientOriginalExtension();
$file->storeAs("/private", $file_path);
return response()->json([
"status" => "success",
"message" => "File has been uploaded."
]);
}
If you test the app now, you will be able to upload the file. You can see that file will be saved in your app/storage/private/files/{file_name}.jpg path.
Save the paths in database
We need to save the paths of uploaded files in database to keep the reference of them. So I will first create a migration for table named “files”.
// CMD
php artisan make:migration create_files_table
Then in your migration file, create the following columns:
- name: Name of file.
- path: Path of file.
- type: Either public or private
<?php
// database/migrations/{date_time}_create_files_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('files', function (Blueprint $table) {
$table->id();
$table->text("name")->nullable();
$table->text("path")->nullable();
$table->enum("type", ["public", "private"])->default("public");
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('files');
}
};
Then finally run the migration:
// CMD
php artisan migrate
Now change the UserController@upload_private() function to save the file name and path in database after saving it in private storage.
public function upload_private()
{
$file = request()->file("file");
$file_path = "files/" . uniqid() . "." . $file->getClientOriginalExtension();
$file->storeAs("/private", $file_path);
DB::table("files")
->insertGetId([
"name" => $file->getClientOriginalName(),
"path" => $file_path,
"type" => "private",
"created_at" => now()->utc(),
"updated_at" => now()->utc()
]);
return response()->json([
"status" => "success",
"message" => "File has been uploaded."
]);
}
Try uploading another file and you will see that a new row should be inserted in “files” table.
View the files
In order to download the files, first we need to view them on browser. In this tutorial, I am using Laravel query builder directly in the controller to fetch the data, but you can use Laravel models for re-usability. So change your UserController@private_storage() function to fetch all the uploaded private files from database.
public function private_storage()
{
$files = DB::table("files")
->where("type", "=", "private")
->orderBy("id", "desc")
->paginate();
return view("private-storage", [
"files" => $files
]);
}
Then in your blade template resources/views/private-storage.blade.php, you need to loop through all the files and show their name. The name should be a hyperlink which when clicked, should open a new URL in another tab and download the file.
@foreach ($files as $file)
<p>
<a href="{{ url('/download-private/' . $file->id) }}" target="_blank">{{ $file->name }}</a>
</p>
<hr />
@endforeach
If you refresh the page now, you will start seeing all your uploaded files names.
Download file from Laravel private storage
We need to create a GET route that will handle the download request.
// routes/web.php
Route::get("/download-private/{id}", [UserController::class, "download_private"]);
Then I will create a new function UserController to download the file to the user’s local system.
public function download_private()
{
$id = request()->id ?? 0;
$file = DB::table("files")
->where("id", "=", $id)
->where("type", "=", "private")
->first();
if ($file == null)
{
abort(404);
}
if ($file->path && Storage::exists("private/" . $file->path))
{
return Storage::download("private/" . $file->path);
}
abort(404);
}
You can also get authenticated user in it and check if user is authorized to access this file or not. So that’s it, that’s how you can upload and download files from Laravel private storage. Private files does not have a direct URL so they can’t be accessed directly. Thus making them secure as compared to public storage files which are publicly available for everyone.
If you need any help in integrating this in your project, feel free to contact me.