expertapproach's avatar

S3 private downloads changing name of downloaded file

I've been creating a document management system using laravel 5.3 on s3 storage.

I've been following this video as my starting point: https://laracasts.com/series/whats-new-in-laravel-5-3/episodes/12

I'm saving as private files because I require privacy on my documents.

I'm able to upload and download files to/from my s3 bucket. I am generating a random name for uploaded files and saving that information in a documents table.

When downloading from a regular local disk or public s3, I can use HTML 5 to change the name of the downloaded file from the random name to the original name that existed when the file was uploaded. Essentially using: download

This doesn't work with private s3.

I've found that with a private s3 file, if i set a meta content-disposition header (in s3), it will save the name I need and download it properly, but I can't find out how to set the content-disposition header using the storeAs function. So I probably have to go deeper. I see that in older versions of laravel, while it was more difficult, we could do more. Any assistance would be greatly appreciated.

To download the file I'm using this in the model:

use Storage;
use Config;

protected $appends = ['url'];

 public function getUrlAttribute()
 {
     return $this->getFileUrl($this->attributes['document']);
 }

 private function getFileUrl($key) {
     $s3 = Storage::disk('s3');
     $client = $s3->getDriver()->getAdapter()->getClient();
     $bucket = Config::get('filesystems.disks.s3.bucket');

     $command = $client->getCommand('GetObject', [
         'Bucket' => $bucket,
         'Key' => $key
     ]);

     $request = $client->createPresignedRequest($command, '+20 minutes');
     
     //Log::info($request->getUri());
     
     return (string) $request->getUri();
 }

This in the view:

<a download="{{ $doc->original_name }}" href="{{ $doc->url }}">{{ $doc->description }}</a>

To upload the file, I'm using this in the controller:

    public function addDocument(Request $request, $agent_id)
    {
        $datePath = date("Y/m/d");
        $file = $request->file('file');
        $extension = $file->getClientOriginalExtension();
        $random_string = chr(rand(65,90)) . chr(rand(65,90)) . chr(rand(65,90)) . chr(rand(65,90)) . chr(rand(65,90)) . chr(rand(65,90)) . chr(rand(65,90)) . chr(rand(65,90)) . chr(rand(65,90)) . chr(rand(65,90)) . chr(rand(65,90)) . chr(rand(65,90)); // random(ish) 12 character string
        $newfilename = $random_string.'.'.$extension;
        $destinationPath = $file->storeas('documents/' . $datePath, $newfilename);
        $filename = $file->getClientOriginalName();
        $document = new Document();
        $document->documentable_id = $agent_id;
        $document->user_id = Auth::user()->id;
        $document->original_name = $filename;
        $document->documentable_type = 'App\Agent';
        $document->description = $filename;
        $document->document = $destinationPath;
        $document->save();
        return Response::json(['id'=>$document->id, 'filename'=>$filename, 'fullpath'=>$destinationPath, 'created_at'=>$document->created_at->toDateTimeString(), 'category'=>empty($document_category) ? '' : App\ItemType::find($document_category)->name]);
    }
0 likes
4 replies
meeshka's avatar
meeshka
Best Answer
Level 5

Are you using putObject to push the files to bucket? If yes you can add something like below to solve your problem.

'ContentDisposition' => 'attachment; filename="' . $target_filename . '"'
meeshka's avatar

Also, is there a reason for using

$random_string = chr(rand(65,90)) . chr(rand(65,90)) . chr(rand(65,90)) . chr(rand(65,90)) . chr(rand(65,90)) . chr(rand(65,90)) . chr(rand(65,90)) . chr(rand(65,90)) . chr(rand(65,90)) . chr(rand(65,90)) . chr(rand(65,90)) . chr(rand(65,90));

instead of uniqid or Laravel's str_random?

expertapproach's avatar

storeAs function does the storing. The random id will be fixed thanks to you.

expertapproach's avatar

I didn't find the putObject documentation. I did find the put - which is what I assume you meant. I've resolved my problem with the following code thanks to your assistance:

    public function addDocument(Request $request, $agent_id)
    {
        Log::info('Trying to add document to agent.');
        $datePath = date("Y/m/d");
        $file = $request->file('file');
        $extension = $file->getClientOriginalExtension();
        $newfilename = uniqid().'.'.$extension;
        $filename = $file->getClientOriginalName();

        $destinationPath = 'documents/' . $datePath . '/' . $newfilename;

        Storage::disk('s3')->getDriver()->put($destinationPath, $file->__toString(), [ 'ContentDisposition' => 'attachment; filename="' . $filename . '"']);

        $document = new Document();
        $document->documentable_id = $agent_id;
        $document->user_id = Auth::user()->id;
        $document->original_name = $filename;
        $document->documentable_type = 'App\Agent';
        $document->description = $filename;
        $document->document = $destinationPath;
        $document->save();
        return Response::json(['id'=>$document->id, 'filename'=>$filename, 'fullpath'=>$destinationPath, 'created_at'=>$document->created_at->toDateTimeString(), 'category'=>empty($document_category) ? '' : App\ItemType::find($document_category)->name]);
    }

Please or to participate in this conversation.