mstnorris's avatar

Where to store files that a user uploads

Currently when a user uploads a file, they are stored in /public/uploads which was fine, but now I've just tried to add a route of uploads which conflicts with the directory which is protected by nginx.

That coupled with the fact that I don't want the files to be directly available using just the http://mydomain.com/uploads/name_of_file URL has lead me to think that I should do one of the following:

  1. Look at S3
  2. Rename the uploads directory to anything else
  3. Store the files in the storage directory

What are your thoughts? What are the implications of the above?

0 likes
12 replies
ohffs's avatar

I use the storage dir for uploaded files then return the file via a download() response. There's a little performance hit as you're booting up the framework for every file, but the overhead didn't really matter for me - thankfully the sites aren't very popular ;-) I also figured it was a nice way to hide the implementation so if I needed to shift things around the code path should (no - don't laugh!) stay much the same :-)

2 likes
mstnorris's avatar

I thought about the same approach. Right now I have zero users as I'm building an MVP so best practices, and all that jazz aren't the main factors. I'm a perfectionist but right now I'm in "just get it done" mode, so that sounds like a good solution.

toniperic's avatar

Renaming the uploads directory to anything else wouldn't fix the issue, would it?

Keeping everything at the storage directory vs an S3 server - it depends whether the server your app runs on can (and should) deal with all the uploaded files and possible download bandwidth, without affecting everyone else's experience while using the application.

Hope I could help.

mstnorris's avatar

Renaming the uploads directory to anything else wouldn't fix the issue, would it?

Yes it does as my route uploads/ was trying to access the directory, and not my Controller method.

Right now, I'm not worried about performance, upload download as this is a proof of concept. But yes, in practice it would definitely be a major consideration.

mstnorris's avatar

@ohffs what would the method look like that downloads the file?

To everyone else, what is a good way to protect the files? I don't think I'm too concerned with passwords, but I would like to limit downloading the file to (a) the person who uploaded it, and (b) the teacher of the module the the file essentially "belongsTo".

nrmorgan's avatar

I've been using S3, taking the uploaded file and moving it straight onto S3 where it is encrypted using the inbuilt S3 encryption. It streams the file so can handle large files because the file is not read into RAM.

In your controller, and assuming you have configured your S3 bucket settings:

$uploadedFile = $request->file('file');
Storage
    ::disk("s3")
    ->getDriver()
    ->put(
    "<path/filename on S3>",
    fopen($uploadedFile->getRealPath(), "r"),
    ["ServerSideEncryption" => "AES256"]);
mstnorris's avatar

@nrmorgan for formatting, check out some Guidelines I wrote.

Use three backticks like so ```

```

your code goes here

```

As for your suggestion, thank you, I'll check it out.

1 like
toniperic's avatar
Level 30

You can do the guarding likewise:

Route::get('download/{filename}', function($filename)
{
    $file_path = storage_path() . $filename;
    if (file_exists($file_path) && Auth::user()->canDownload($filename))
    {
        return Response::download($file_path, $filename, [
            'Content-Length: '. filesize($file_path)
        ]);
    }

    return 'Requested file does not exist on our server!';
})
->where('filename', '[A-Za-z0-9\-\_\.]+');

or this would rather be a good opportunity for a new middleware.

1 like
ohffs's avatar

What @toniperic says :-) (I'd have been quicker if I hadn't just spent the last hour watching my new roomba clean my sitting room. Gadget-tastic!) :-D

1 like
michaeldyrynda's avatar

@mstnorris just be careful with downloading responses from S3; they'll stream via your server to the client (I'm quite sure...)

jekinney's avatar

@deringer is right, streaming from s3 goes to your server then to the client by default. Not a huge deal unless your talking about video.

If you want to secure the path to the file why not set a session or token for access to the url? Secure it with middleware.

Please or to participate in this conversation.