Playing
04. Temporary URLs for the local filesystem

Transcript

00:00
Laravel now has support for signed URLs for the local file system driver.
00:05
That means that if you are uploading files and not storing them somewhere like Amazon S3, you can still secure them with a token and an expiry. To demonstrate this, we're going to build out a full upload example where we store files in our local file system
00:19
and we protect them with a token and an expiry. Let's get started. Okay, so to start with, we're going to go ahead and make out a model which will be for our files.
00:29
So let's go and create that out alongside a migration. Let's open this up and we'll just keep this really basic. So let's open up our create files table migration and let's go ahead and just store out a name in here for this or a title.
00:44
And let's also do the same thing, but for the path. So this is where it will be stored. Okay, we can go ahead and migrate that. And while we're here, let's go ahead and build out a controller.
00:55
So let's make out a controller and call this file upload controller. And let's define out some of the methods that we're going to need in here. So file upload controller, we're going to have one method to go ahead and show all of the files that will list the files for us.
01:10
We'll have a second one to go ahead and actually upload this or store a file. And then we'll go ahead and have a method to download or get a file. Okay, so for the index, let's go ahead and return out a view in here. And we'll just say files.index.
01:27
And let's go ahead and make that view on the command line. So let's make out a view called files and index. And there we go. So this files index.blade.php file will just contain a form
01:39
where we can go ahead and upload to a particular location. Let's change the method over here to post and the ink type in here to multi-part form data so we can process file uploads. And let's create an input with a type of file.
01:53
And we'll just call this file, give that an ID of file. And we'll just create a really simple submit bus now in here. So we'll just keep this super basic. Okay, so before we go ahead and hook everything up,
02:03
let's go over to our web routes and just start to define these out. Okay, so for the index, let's go and reference our file upload controller. And let's say index, give that a name of files and index. Let's do the same thing for the upload as well.
02:21
So this is going to be posting through to slash files. And then of course, we'll change this over to use that store method. And we'll change that to store. And then finally, we'll have a get route,
02:35
which goes ahead and grabs a particular file just by its ID. And that's to download the file. Now, the really important part about this is that when we're using signed URLs, of course, anyone within what we're building here could just access this file,
02:52
this URL, and that would download it even if the end URL would be signed. We're not working with authentication here. The whole point of signed URLs is to stop people taking a link, giving it to someone else, or using the link after a long period of time.
03:05
So all of these routes, when we are generating signed URLs, would have to be protected with some auth middleware. So we're just assuming that all of this is within the user's account area where they have to be authenticated to access it.
03:19
Okay, so now that we've got this done, let's go ahead and open up this and set our action for the upload here. So that's going to be going through to the files store route. And let's not forget to include our cross-site request for each read token.
03:34
Okay, if we open this up in the browser, we've got the following. I've gone ahead and just created a really basic file.txt or zip that we can go ahead and hit upload to, which goes through to store here. Okay, let's go ahead and look at actually uploading the file.
03:46
So we're going to head over to our file upload controller. And under store, let's bring in our request object. Let's go ahead and validate this. So we'll use request and validate on the file.
04:01
And we'll just say that this is required. And we'll just set a max of anything in here. It doesn't really matter. Okay, so to grab the file, let's go ahead and call this upload.
04:12
We're going to go from the request. And we use the file method to grab this at the name that we have created. That will give us a uploaded file. So let's just die dump on that upload just to see what we've got.
04:24
So let's go over, choose that file, hit upload. There we go. So we've got an uploaded file. And from this uploaded file class, we can go ahead and store this.
04:34
But at the same time, we want to create a record out in the database using our file model. So let's go ahead and say file create. The first thing that we want to do is provide the name.
04:45
We can do this really easily directly from the upload. We can get the original client extension, or we can get the original client name. So let's just go with get original client name in here. And now we want to store a path.
04:58
So the path will be returned when we store this file. So if we go ahead and say upload store as, we can store this under a particular name, under a particular directory. So let's just store this under an uploads directory.
05:11
And then for the name of this that we want to store it as, let's just go ahead and use the original client name in here as well. Once we've done that, we just want to go ahead and return back. And we'll go and list through all of the files inside of that index as well.
05:25
Okay, let's just make sure that our file uploads are working. So let's go back over to our form here. Let's choose a file, hit upload. And yeah, we just need to make the model fillable.
05:35
So let's go over to that file model that we created. And let's just set this to guarded false, just so we don't need to add our fillable fields. Okay, let's give that a refresh and that should have been uploaded.
05:49
So let's check out where this is stored. If we head under storage app and then private, you can see that we've got this uploads directory. And sure enough, that contains file.txt.zip.
06:00
And if we head over to the database, we should see that under the files just here as well. So we've got the full path that we can use to actually allow this file to be downloaded. Okay, next up, let's go ahead and list through all of our files in the index.
06:13
So at least we can iterate through them and go ahead and allow these to be clicked to be downloaded. So we're going to go over and just pass down a bunch of files here. It doesn't really matter what we're doing at the moment. Let's just scope these by latest and say get.
06:26
And then over in the index, let's iterate through these. So let's just do this down here under the form. And let's do a for each here on each of these files as file. And we'll just dump out for each of these, just a wrapper.
06:41
And then we'll have an anchor that goes and lists the file name and then links through to that file download root that we created. So let's say files and download. Let's also go ahead and hook this up to actually pass the file in, which we're going to need.
06:56
And just while we're here, let's go over to our upload controller and just make sure that our download method takes that file in with root model binding and just goes ahead and die dumps that file. Okay, so if we head over, there we go.
07:11
When we click on this, we go through, we dump that file. Now we're going to get to the point where we want to download this file. So as it stands, there's a couple of ways that we could do this. We could use the storage facade to go ahead and download a file at a particular path.
07:25
Let's go ahead and just pass in the file path into here and see what happens. So if we just give this a refresh, you can see that sure enough, this file gets downloaded. And that means that when we click on a file here, that will instantly download it. So what's actually happening when we use storage download?
07:41
Well, let's go ahead and just die dump on this and see what we get. So we'll die dump on that, go ahead and click on a file, and you can see that we get a streamed response. Now this isn't always what we want.
07:51
What's going to happen here when we go ahead and return this from this controller method, this is going to stream the response to the browser. Sometimes for larger files or where you do want to redirect to the URL, this isn't appropriate.
08:05
So what we want to do is for a publicly or privately uploaded file, we want to allow a link to be used to download the file. But how are we going to do this when this is tucked away privately? Well, first of all, let's just talk about the difference between public and private files
08:21
and the visibility in Laravel. So I'm going to get rid of this die dump, and we're going to slightly change around the way that we store this file. And as the third argument here, we'll change this to public.
08:31
Now, if we open up config and file systems within Laravel, we've got the local driver here, which is what we've been using by default. But now we've changed this over to public. This will put this under the storage directory under public,
08:44
and the visibility of the file will be set to public. Once we've linked up our storage, that means that file will now be available to anyone. So let's just take a look at the difference between this. So let's go ahead and upload a new file.
08:57
So we'll go back over to our homepage here, choose the same file, it doesn't really matter, hit upload, and let's go straight back over to our file list. And you can see that this is now being put under the public directory.
09:08
What this means is, as long as we go ahead and run storage link, if we haven't already, and that hasn't been linked up, we can actually access this directly in the URL. So we should be able to go into uploads and file.txt.zip,
09:21
and that should download that file. Now, that's not always what you want. How do we get the balance between keeping files private, but also providing a link that expires?
09:31
Well, that's what the whole video is about. So let's go ahead and do that now. Okay, so I'm going to get rid of this public directory, because we're not going to use that.
09:37
We want these to be tucked away, so they can't be accessed in the URL by anyone. And we'll get rid of the public driver config that we're using here. Okay, so now what we're going to do is generate our assigned URL.
09:51
So I'm going to go ahead and get rid of this one from here. We'll keep the private one in here ready to go. And let's go ahead and take a look at the difference. So just so we're completely clear at the moment,
10:02
because that file is private, we can't access this in here. So we can't say file.txt.zip. It just doesn't work. It's not going to be found.
10:12
And that's what we're trying to access. So with this change in Laravel, what we can now do is use our storage facade to generate a temporary URL for a local file.
10:26
This has always worked for things like uploading to Amazon S3, where you have a file that's inside of a S3 bucket, and it's private, but you want to generate a temporary URL for. But now we can do this with file paths on our local file system.
10:41
So we pass in the path that we want to download, and then we pass an expiry in minutes. So rather than work out the minutes, a good way to do this is to grab the now helper,
10:51
which returned a carbon instance, and then add on any minutes that you want. So I'm just going to add one minute to the expiry of this, but you can adjust this based on what you need.
11:01
So now that we've done that, let's just die dump on the URL to see what this gives us. Let's go back over and let's choose this. And there we go.
11:09
So now, even though when we tried to access uploads file.txt.zip before, which if we do without this signature in the URL, still will not work.
11:21
We've got a 403 forbidden. Because we've now got this expires timestamp value and the signature, we can copy and paste this in here.
11:30
And sure enough, that now downloads the file. So what we're now doing is treating this private file kind of like a publicly accessible URL, but we're protecting it behind that signature
11:41
and the expiry as well. So what we're going to do is now click on this just to demonstrate this. I'm going to grab the entire URL.
11:50
I'm going to just paste this into here. Remember, we have a minute expiry on this. I'm just going to go ahead and cut the video short for about a minute and a half.
11:58
And then we're going to try and access this file, which we know now works. Let's do that in a minute. Okay. So I've waited for about a minute,
12:06
cut the video short. We're going to go ahead and hit this URL here again, which remember does expire after a minute. And as you can see, we get a 403 forbidden.
12:14
So the golden rule is when you are uploading files in Laravel, it's a good idea to make sure that they're private. But if you need to download them,
12:24
you can either use the download method that we looked at a little bit earlier, or you can generate a temporary URL out. To pull this together completely,
12:32
we can just go ahead and redirect to this URL. That user will have that URL, but remember that they can't copy and paste that to other people without this expiring
12:41
after a certain length of time. And of course, you can adjust how long this expires for. So for example, we could bump this up to 10 minutes. So now if we go back to our file list,
12:51
we have accessible files that are private, which expire after a certain amount of time. And that is how we do signed URLs on the local file system now in Laravel.
26 episodes2 hrs 34 mins

Overview

Need to know what’s new in Laravel as it happens? Every episode of this course is dedicated to covering the most interesting and useful Laravel additions in detail, so you’re ready to start using them in your applications.

Check back often, and stay completely up-to-date with Laravel.

Alex Garrett-Smith
Alex Garrett-Smith
Hey, I'm the founder of Codecourse!

Episode discussion

No comments, yet. Be the first!