0

I am struggling to offer the user a link to a file that they can download when logged in. I read this and that (because eventually I want to protect the file from public access).

So under my app /storage folder I created a "private" directory next to "public". Inside it there is a subfolder "A" containing a "fileA.tar.gz" (I also tried with a simple test.txt without luck).

MyLaravelApp/
├── storage/
│   ├── public/
│   ├── private/
│        └── A/
│            └──fileA.tar.gz 

In my controller I do :

$softwarePath = "private/A/fileA.tar.gz";

$urlToArchive = \Storage::disk('local')->url(
                                $softwarePath);

$exists = \Storage::disk('local')->exists($softwarePath); // returns true

But in the view when I click on the link http://127.0.0.1:8000/storage/private/A/fileA.tar.gz I get 404 although the exists function returns true.

So I tried to define in /config/filesystems.php a direct "short-cut" to my "private" folder :

'private' => [
    'driver' => 'local',
    'root' => storage_path('app/private'),
    'url' => env('APP_URL').'/privateDownload',
    'visibility' => 'public',
],

and made the following changes in the controller :

$softwarePath = "A/fileA.tar.gz";

$urlToArchive = \Storage::disk('private')->url($softwarePath);

$exists = \Storage::disk('private')->exists($softwarePath); // keeps returning true

But now I get 403 when I click on the generated link http://localhost/privateDownload/A/fileA.tar.gz (note the localhost without port address) and if I change the address to localhost:8000 I get the 404 back.

The route to the aforementioned controller is :

Route::get('/account', 'AccountController@showAccountDetails')->middleware('auth');

And I also tried to remove the middleware('auth') and access to private/A/fileA.tar.gz without luck (404).

Please note : if I keep the same subdirectory hierachy and moved it under public like :

MyLaravelApp/
├── storage/
│   ├── public/
│        └── A/
│            └──fileA.tar.gz 
│   ├── private/

There is no issue, and the file can be downloaded. This is not interesting because I want to prevent this file from being downloaded without being logged in.

According to the doc and other SO answers it seems possible to access to a different directory than public. How can it be done ? Why exists() returns true whereas I get 404 then ? What's my setup / code failure actually?

Any help appreciated!

Solution

Based on @Namoshek's answer, here is what I did (for the records) :

In the aforementioned controller I simply checked whether or not the user has the right to download fileA. If so then I return a view which has a link to a route named downloadFileA that points to a function downloadFileA still in the same aforementioned controller.

Finally in the function downloadFileA I return \Storage::disk('private')->download('fileA') after checking whether the user is entitled or not to download the file. So I check twice but that's not a problem because there is very low traffic (once a week or so).

HelloWorld
  • 2,275
  • 18
  • 29

1 Answers1

2

You can use Storage::download('filename.xyz') instead of generating a URL with Storage::url('filename.xyu'). This will send the file as content of the response. It may be resource intensive for large files though.

Namoshek
  • 6,394
  • 2
  • 19
  • 31
  • Thanks @Namoshek! I updated my question with the solution I used based on your answer. – HelloWorld Nov 23 '18 at 10:51
  • Note about your comment in the solution `So I check twice but that's not a problem because there is very low traffic (once a week or so)`: It is actually required for you to check the permissions twice (at least from what I got from the requirements). Otherwise a user with the permission to see or download the file could simply send the link to someone else. And to be fair, such a permission check does not cost a lot of resources at all. – Namoshek Nov 23 '18 at 11:08