#Downloading from "private" Folder in Laravel (Nova)

1 messages ยท Page 1 of 1 (latest)

granite oxide
#

Have you tried to response to a stream instead of a download URL?

#
Route::get('/stream', function () {
    return response()->streamDownload(function () {
        echo file_get_contents("your_file_path");
    }, 'responsed_file_name.ext');
});
dry spoke
#
        $downloadableFile = response()->streamDownload('app/private/applications.pdf');
        Log::debug($downloadableFile);
        $filename = 'bewerbungen_lans ' . Carbon::today();
        $extension = '.pdf';
        return Action::download($downloadableFile, $filename . $extension);```
that gives me no file as well
orchid marsh
#

you tried streaming ? to see what happening?

dry spoke
#

yeah, [2023-02-06 12:47:29] local.DEBUG: HTTP/1.0 200 OK Cache-Control: no-cache, private Date: Mon, 06 Feb 2023 12:47:29 GMT

#

that's the log of the stream

#

but on "download" in the browser it always returns no file

#

maybe i have to ask is it generally possible to download a file if it's not in the public folder at all?

orchid marsh
dry spoke
#

i c

orchid marsh
#

but you have permission setted correctly right?

dry spoke
#

which permission? folder?

orchid marsh
#

yes.. i mean the file is correctly written in that folder right?

dry spoke
#

yep

#

the file exists

#

definetly

orchid marsh
#

and if you open the file you see it correctly?

#

i mean.. manully open that file

dry spoke
#

yep

#

everythings fine with this

orchid marsh
#

try an exists and see what happen

#
$downloadableFile = Storage::disk('private')->exists('applications.pdf');
#

i think you are misspelling the correct path

#

check also in your filesystem to see what "private" disk link to

#

@dry spoke ? still there?

dry spoke
#

yes, just in a meeting ๐Ÿ˜„

orchid marsh
#

oki lemme know when you can try

dry spoke
#

@orchid marsh log says [2023-02-06 13:43:26] local.DEBUG: 1 and browser says again, no file

#
            'driver' => 'local',
            'root' => storage_path('app/private'),
            'url' => env('APP_URL').'/storage',
            'visibility' => 'private',
            'throw' => false,
        ],```
#

that's what it filesystem looks like

orchid marsh
#

oh.. isee now..

#

its not only called "private" .. the visibility is set to private

#

so the permissions will be 0600 for files.. you will not be able to "download" that file

#

when you store something "outside" the public folder you can already consider that as NOT public accessible. but if you set the visibility as "private" the file uploaded in that disk will be chmodded to 0600 so only the "owner" can read and write. useful if you want these files be accessible by your owner. but in this case youre trying to "serve" the files to public and the group doesnt have access.

#

you get what i mean?

dry spoke
#

ok

#

so it should not be private?

orchid marsh
#

btw if you set the "throw" to true you will probably can see the exact error..

dry spoke
#

ok, changed to public

#

no change at all

#

as well as no error in log

#

[2023-02-06 13:57:27] local.DEBUG: 1

#

only my own log again

orchid marsh
#

you need to

try{
} catch(Exception $e){
return $e;
}

dry spoke
#

ok

#

just put the $downloadableFile = Storage::disk('private')->exists('applications.pdf'); in the try and catch?

orchid marsh
#

->get

dry spoke
#

so if i do get, i get the same error as before again

#

so he want's to download, but can't somehow

orchid marsh
#

i think we need to try streaming that file correctly.. and then try the download

dry spoke
#

but how to ๐Ÿ˜„

#

in the meantime wanna thank you for your help already ๐Ÿ˜‰

orchid marsh
#
public function handle(ActionFields $fields, Collection $models)
    {
        $file = Excel::store(new MultipleSheetsApplications($models), 'applications.pdf', 'private');
        $downloadableFile = Storage::disk('private')->get('applications.pdf');
        $filename = "test.pdf"; //here your carbon date things.. but lets try with simple test
        $headers = [
            'Content-Type'        => 'application/octet-stream',
            'Content-Disposition' => 'attachment; filename=' . $fileName,
        ];
        
        return response( $downloadableFile, 200 , $headers);
    }
dry spoke
#

i think that approach wont work.. because if it's no nova action, there won't be any response at all

#

but i test of course

#

yeah, as assumed, nothing happens if i press the according button

#

it just says, action was executed successfully

orchid marsh
#

damn forget you were on nova

dry spoke
#

yes m8, that's the problem ๐Ÿ˜„

orchid marsh
#

try catch a response()->streamDownload('app/private/applications.pdf');

dry spoke
#

instead of the storage::disk part?

orchid marsh
#

yes

dry spoke
#

ok

orchid marsh
#

wait mate... wondering another thing... you changed visibility to "public" but the file has been re-uploaded? could you verify that chmod of that file are 0664 or 0644 ?

dry spoke
#

drwx------ 2 tobi tobi 4096 Feb 6 20:47 private

#

that's the folder...

#

that's the file -rw-r--r-- 1 tobi tobi 50348 Feb 6 22:12 applications.pdf

orchid marsh
#

yep .. its correct

dry spoke
#

can you just help me with the correct syntax for the try catch?

#

i barely use try catch

orchid marsh
#

yep

dry spoke
#

i think i have it

#
        try {
            $downloadableFile = response()->streamDownload('app/private/applications.pdf');
        } catch (Exception $e) {
            return $e;
        }
        // Log::debug($downloadableFile);
        $filename = 'bewerbungen_lans ' . Carbon::today();
        $extension = '.pdf';
        return Action::download($downloadableFile, $filename . $extension);```
#

right?

orchid marsh
#

yep---

dry spoke
#

ok, no change, no error, nothing

orchid marsh
#

i dont know if nova use throwable...

dry spoke
#

afaik it does

#

m8 brb...

orchid marsh
#

}catch (\Throwable $error){
return $error
}

dry spoke
#

ok re

#
        try {
            $downloadableFile = response()->streamDownload('app/private/applications.pdf');
        } catch (\Throwable $error) {
            return $error;
        }
        // Log::debug($downloadableFile);
        $filename = 'bewerbungen_lans ' . Carbon::today();
        $extension = '.pdf';
        return Action::download($downloadableFile, $filename . $extension);```
like this, right?
#

unfortunately no error log

orchid marsh
#

ok latest one

#

put everything in the try

#

also the return:Action etc

dry spoke
#

ok

orchid marsh
#

did you set Throw to true in filesystem right?

dry spoke
#

yep

orchid marsh
#

and please set $filename to some test string like "test.pdf"

dry spoke
#
        try {
            $downloadableFile = response()->streamDownload('app/private/applications.pdf');
            $filename = 'testfile.pdf';
            // $extension = '.pdf';
            return Action::download($downloadableFile, $filename);
        } catch (\Throwable $error) {
            return $error;
        }```
that's the actual code right now
orchid marsh
#

yep

dry spoke
#

no change at all

orchid marsh
#

and ?

dry spoke
#

no error nothing

#

browser just throws

#

german for, could not download, no file

orchid marsh
#

no wait..

#

the $downloadableFile need to be the file.. not the stream

dry spoke
#

ah ok

#

sry

orchid marsh
#

the stream was for the previous test

dry spoke
#

i tried this now $file = Excel::store(new MultipleSheetsApplications($models), 'applications.pdf', 'private'); try { $downloadableFile = 'testfile.pdf'; // $filename = 'testfile.pdf'; // $extension = '.pdf'; return Action::download($downloadableFile, 'test.pdf'); } catch (\Throwable $error) { return $error; }

#

but no change at all

orchid marsh
#

no.

#

again wrong.

dry spoke
#

you mean the applications.pdf?

orchid marsh
#

wait

dry spoke
#

k

orchid marsh
#
$file = Excel::store(new MultipleSheetsApplications($models), 'applications.pdf', 'private');
try {
   $thefilepath = Storage::disk('private')->path('applications.pdf');
   return Action::download($thefilepath , 'test.pdf');
} catch (\Throwable $error) {
   return $error;
}
#

path* not url

dry spoke
#

๐Ÿ˜„

orchid marsh
#

you changed with path?

dry spoke
#

sry

#

same error, nevertheless

#

could not download file

orchid marsh
#

return $thefilepath and see what is it

#
$file = Excel::store(new MultipleSheetsApplications($models), 'applications.pdf', 'private');
try {
   $thefilepath = Storage::disk('private')->path('applications.pdf');
   //return Action::download($thefilepath , 'test.pdf');
   return $thefilepath;
} catch (\Throwable $error) {
   return $error;
}
dry spoke
#

where should the return appear?

orchid marsh
#

depends on where you call it---

#

do a die and dump

dry spoke
#

in the browser

#

the dd doesn't work

#
        try {
           $thefilepath = Storage::disk('private')->path('applications.pdf');
           dd($thefilepath);
        } catch (\Throwable $error) {
           return $error;
        }``` like this, correct?
orchid marsh
#

it seems so...

#

shrug out of ideas here

dry spoke
#

yeah, no dd in nova

dry spoke
#

that's some crazy stuff, right

#

sometimes i hate my client requested nova

#

there's so many restrictions

orchid marsh
#

are you in Debug mode right?

dry spoke
#

yeah

orchid marsh
#

just for curiosity sake ... var_dump() that variable what happen?

dry spoke
#

the var_dump should appear in the browser window as well, right?

orchid marsh
#

ye

dry spoke
#

no output ๐Ÿ˜„

#

i really suuuuuucks

#

i'm already working on a solution for 1 1/2 days

#

that's really impossible...

orchid marsh
#

this sound really weird

dry spoke
#

if the file is in public, everythings easy

#

but as soon it is in a private folder, nothing works anymore

orchid marsh
#
$file = Excel::store(new MultipleSheetsApplications($models), 'applications.pdf', 'private');
try {
   $thefilepath = Storage::disk('private')->path('applications.pdf');
   //return Action::download($thefilepath , 'test.pdf');
   var_dump($thefilepath);
   exit;
} catch (\Throwable $error) {
   return $error;
}
#

if it correctly upload/create the file in that disk.. its weird that it doesnt even show you the path to that file

#

with no error..

dry spoke
#

but the file is there, 4 sure

orchid marsh
#

but probably im missing something ,, or its something Nova related..

dry spoke
#

i see it with my own eyes ๐Ÿ˜„

orchid marsh
#

i think you have to ask some more skilled help then mine bruh!

dry spoke
#

nevertheless, thanks for your effort

#

for how long are you coding already?

orchid marsh
#

in laravel? approx 4/5 months

dry spoke
#

generally?

orchid marsh
dry spoke
#

lol, i c

#

so you're way more experienced than me, i just started out 2 years ago finally pursuing my lifelong dream ๐Ÿ˜„

orchid marsh
#

lol i see..

desert raptor
#

I think the problem is, you're trying to return the PDF from the Nova API.php in a custom tool or action right?

#

The steaming approach should work perfectly as that's what it's good at

desert raptor
#

Is it a custom tool, or action?

dry spoke
#

you mean like this response()->streamDownload('app/private/applications.pdf');

desert raptor
#

Ok, so the problem is, Nova sends the request from your browser to the API, then returns the response to your browser, so any response you send, won't actually send the value back to you

dry spoke
#

ok

desert raptor
#

I'm not sure you can do it with a custom action to be honest, you need a way to return a URL that you can redirect the browser to, which will then download the PDF

dry spoke
#

ok, can you think about another approach which might work in this case?

orchid marsh
#

@dry spoke : that's what a skilled developer looks like -> @desert raptor

desert raptor
#

I would:

  1. Define an endpoint in your normal laravel routes that takes the id of your model.
  2. In the controller, grab the model id and load it, and return the PDF file from storage using the Response::stream method mentioned above
#

this should download it for you and not store it anywhere

#

then to secure it, add something like either:

  1. Laravel signed routes (https://laravel.com/docs/9.x/urls#signed-urls)
  2. or a password / something

If you use signed routes, you can replace your custom action with a custom field, that just generates a signed URL to that endpoint you've defined

Laravel is a PHP web application framework with expressive, elegant syntax. Weโ€™ve already laid the foundation โ€” freeing you to create without sweating the small things.

#

And people can just click "download" and it will download, and if someone hits it without a signing token, it'll 403

#

you just have to lock down who can see that "download" custom field

dry spoke
#

the problem, what i just think of is, how do i get the "custom" download link into the nova backend?

#

you get my point?

#

ah i see, you mean by that custom field, right?

desert raptor
#

Yeah!

dry spoke
#

ok

#

gotta consider that approach

#

thanks for that hint

desert raptor
#

Np

dry spoke
#

@desert raptor i just thought of another possibility.. in the source of Nova Actions i found there's some callback after the execution of the action.. ```` /**
* Register a callback that should be invoked after the action is finished executing.
*
* @param callable(\Illuminate\Support\Collection):mixed $callback
* @return $this
*/
public function then($callback)
{
$this->thenCallback = $callback;

    return $this;
}```

but i'm not sure how / where to execute this callback, can you give me a hint?

#

i thought of, generating the file, put it into the public, and after the file has been downloaded and the action "finished" executing, deleting the file immediately in the callback

desert raptor
#

Yeah you could do that, but there's no guarantee that the user has downloaded the whole file before it's been deleted, and you'd have to be pretty sure that it's deleting them for it to be secure.

dry spoke
#

yeah, ok

#

agree, but how to call this callback?

#

this is what i still stumble upon

desert raptor
#

The one in the example above?

dry spoke
#

yes

desert raptor
#

not 100% sure as i can't see it in the documentation

dry spoke
#

where and how to call this callback?

desert raptor
#

but i think it's when you register it

dry spoke
#

ah ok

desert raptor
#
{
    return [
        Actions\EmailAccountProfile::make()->then(function() {
            // Your stuff here
        });
    ];
}
#

but that's purely a guess

dry spoke
#

ok

#

otherwise i'm gonna check back in the nova specific discord channel

#

but i try your approach first

#

thanks ๐Ÿ™‚

desert raptor
#

np, good luck

granite oxide
#

Have you tried the code I provided?
Perhaps you should modify the file_get_contents to storage and I ensure the code is working well whether it runs in Laravel
@dry spoke

dry spoke
#

do i understand you right?

granite oxide
dry spoke
#

ok, found a solution according to all your hints, i created a protected route and make a redirect in the custom action to the download

#

thanks a lot for your help guys