For this tutorial what we want to achieve is to have a file upload and download system in our Laravel 5application using the new Storage features. The example is intentionally easy but you can easily extend the concept.
For the purpose we are going to create a database table where we store the file informations. This is needed when we download the file from the application. With the abstraction of the Storage API you can save the file where you like, locally on the local disk like in the example or Amazon S3 servers, or others.
Let’s begin creating a table migration for our fileentries model.
php artisan make:migration create_fileentries_table --table="fileentries"
Edit the code of the created file and add the fields we need.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateFileEntryTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('fileentries', function(Blueprint $table) { $table->increments('id'); $table->string('filename'); $table->string('mime'); $table->string('original_filename'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('fileentries'); } } |
Migrate the DB and go on :
php artisan migrate
To get advantage from the ORM feature let’s create also the model for our fileentries.
php artisan make:model Fileentry
Before implementing the controller and the view, to get an idea of what we need is better to define the routes needed. We need basically 2 route, one for adding file entries, one for download it. We are going to add a third route to have an index page with a form and where we will display our files.
| Route::get('fileentry/get/{filename}', [ Route::post('fileentry/add',[ |
With our routes in place let’s create the controller.
php artisan make:controller FileEntryController --plain
The index method of the controller will render only the view where we have the form for the upload and a list of the image.
Upload a file to the Storage
The add method have to make two things, one is to save our file to the Storage, other is to insert a row into DB to take track of the file info.
Below the first part of the controller code :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | <?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use App\Fileentry; use Request; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\File; use Illuminate\Http\Response; class FileEntryController extends Controller { /** * Display a listing of the resource. * * @return Response */ public function index() { $entries = Fileentry::all(); return view('fileentries.index', compact('entries')); } public function add() { $file = Request::file('filefield'); $extension = $file->getClientOriginalExtension(); Storage::disk('local')->put($file->getFilename().'.'.$extension, File::get($file)); $entry = new Fileentry(); $entry->mime = $file->getClientMimeType(); $entry->original_filename = $file->getClientOriginalName(); $entry->filename = $file->getFilename().'.'.$extension; $entry->save(); return redirect('fileentry'); } } |
And the code for the form with a first list of the files name. We will modify this part later adding a display for the pictures.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | @extends('app') @section('content') <form action="add" method="post" enctype="multipart/form-data"> <input type="file" name="filefield"> <input type="submit"> </form> <h1> Pictures list</h1> <div class="row"> <ul> @foreach($entries as $entry) <li>{{entry->filename}}</li> @endforeach </ul> </div> @endsection |
At this point we are able to upload files to the app and save them into the Storage. But how to vew or download this files ? We didn’t save it int he public directory so we need a method to get it retrieved from the Storage and the present to the broswer.
Download files from Storage
To get the file back to the storage and also have it in response we saved a row in the database. On this example we retrieve the file based on it random name assigned but easily it can be changed to get it via the original name or the id.
It’s important to note that we saved in the database also the mime type of the file. It’s important now to geenrate the correct response header, adding this method to the controller we are now able to retrieve the files.
| public function get($filename){ $entry = Fileentry::where('filename', '=', $filename)->firstOrFail(); $file = Storage::disk('local')->get($entry->filename); return (new Response($file, 200)) ->header('Content-Type', $entry->mime); } |
To complete the tutorial we can implement a list of the picture calling for every picture the method to retrieve the image. Change the index.blade.php to this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | @extends('app') @section('content') <form action="{{route('addentry', [])}}" method="post" enctype="multipart/form-data"> <input type="file" name="filefield"> <input type="submit"> </form> <h1> Pictures list</h1> <div class="row"> <ul class="thumbnails"> @foreach($entries as $entry) <div class="col-md-2"> <div class="thumbnail"> <img src="{{route('getentry', $entry->filename)}}" alt="ALT NAME" class="img-responsive" /> <div class="caption"> <p>{{$entry->original_filename}}</p> </div> </div> </div> @endforeach </ul> </div> @endsection |
Conclusion
That’s a first step in the file managment for a Laravel 5 application. Using the Storage API we can abstract our filesystem and we can save the files where we like or where it’s better for our app. In a future tutorial i would like to cover the file upload and retrieve with a single page application made withAngularJS.
If you want to take a look at the code you can check my repo on github where almost all of the tutorial are saved.
https://github.com/andbet39/todoApp
Great tutorial sir. Thanks a lot
@foreach($entries as $entry)
<li>{{entry->filename}}</li>
@endforeach
is wrong (missing $), it needs to be replaced by:
@foreach($entries as $entry)
<li>{{$entry->filename}}</li>
@endforeach
my error it is Undefined variable: entries
in this line
@foreach($entries as $entry)
where world we get the app master template?
why when i click submit query it cannot upload and show error token mismatch exeception?
<input type="hidden" name="_token" value="{{ csrf_token() }}">
pls use this code to under open form tag.
please send me router code & view form for download as soon as possible please
Hi cool tutorial, but I have a little question, How can I put the original filename again before the download??
Questo commento è stato eliminato.
If i remember well there is no needed change if the image data is posted via dropzone. Is it a multipart form data post ? if yes it works.
hi! nice tutorial :D
i can't get (download) an mp4 file, and the header response in chrome was 404 not found but an image it's properly shown.
Maybe your webserver doesn't add mime type for mp4 file support.
That's strange. Check if the required file name exactly match the mongoDB collection field and if the mime type for the video is saved in the right format.
hi andrea, do you have any idea why i can't get properly the files ? thanks :D
i use mysql and mime type is in the right format e.g : jpeg , video/mp4.
the jpeg is shown properly though.
Is it justified to return image using Laravel instead of giving full URL? It could save much server resources, I guess. Am I right?
If you use a storage like S3 or GridFS like in the tutorial you don't have a real full url to return the image. This solution is good for uploaded images and not for static contents where you can use the full url if placed in the public folder.
Thanks for reading :)
TokenMismatchException uploading large files
were is the model code for this sir?
Nice tutorial! I get this error: The requested resource /fileentry/get/phpg6PN8V.jpg was not found on this server.
The files are saved in my storage/app directory, but the image src keeps pointing to /fileentry/get/phpg6PN8V.jpg instead of storage/app/
Any hints?
Kind regards
Kristiaan
How to delete exists file? I try this but it is not working.
//Route
Route::post('fileentry/{filename}', ['as' => 'destroy-entry', 'uses' => 'FileEntryController@destroy']);
//In fileentries.index i added this code.
{!! Form::open([
'method' => 'DELETE',
'route' => ['destroy-entry', $entry->filename],
]) !!}
{!! Form::submit('Delete', ['class' => 'glyphicon-remove']) !!}
{!! Form::close() !!}
//FileEntrycontroller
public function destroy($filename){
Fileentry::whereFilename($filename)->delete();
if (Storage::disk('local')->exists($filename)){
Storage::disk('local')->delete($filename);
}
return Redirect::route('fileentry');
}
I don't know why my code going fail and produce
MethodNotAllowedHttpException in RouteCollection.php line 207:
I think your problem is only on the form method and the redirect. Make a try with a link without using the form.
i'll make more check when have some time.
I check source code from browser, which is good. Every post route was correctly pointed exact destroy route.
Put this inside your form:
<input type="hidden" name="_token" value="{{ csrf_token() }}">
Very nice tutorial, I have suggestion that if you change filename using time. again well done.
Very cool tutorial. Thanks Sir for this!
Thanks for reading... If you liked it i can suggest to read the reloaded one, it's really made better :)
I have everything exactly the same, but when I select a picture and press the send button, I get a white page on '/fileentry/add'. I checked your GitHub to see if anything was different, but everything matches. Did anything change so your code breaks or is it on my end and do you know how to solve it? Or do I need to follow a previous tutorial to make it work?
It's look strange. Can you check if the entry in the database is present ? Also try to put a dd($file) in the add method and post the output here. Looks like there is somenthing strange in the form send.
Sorry for the late reply. It apparently was a permission issue with my model file. So I changed the permissions for it and everything worked fine.
Thank you for the great tutorial and feedback!
Happy you solved your problem.
Thanks for the nice tutorial! Excellent introduction of the new Laravel filesystem.
There are though two mistakes, if I am not mistaken: The "first part FileEntryController", could you add a closing curly bracket there, even though you add a method later to the controller. And the model should probably be called "Fileentry" and not "FileEntry", because the db table is "fileentries" and not "fileEntries". If I use your command line, I get SQLSTATE[42S02]: Base table or view not found" errors.
Thanks for the compliment.
You are right on both mistake :) This tutorial was written changing some entities name in run so wasn't easy to maintain it consistent. Anyway thanks for let me know about that, I've updated the article.
Hello, did you try to do an AngularJS app to download a file from a server using an API ( developed in Laravel)?
Thanks