7

I'm using Laravel 5.4.*. I've this simple code in a helper file to upload images/gif in S3 bucket under a folder named say "instant_gifs/". The code is below:

if ( !function_exists('uploadFile') ) {

function uploadFile($fileContent, $fileName, $size='full', $disk='s3')
{
    $rv = '';
    if( empty($fileContent) ) {
        return $rv;
    }

    if($size == 'full') {
        dump($fileName);

        $path = Storage::disk($disk)->put(
                    $fileName,
                    $fileContent,
                    'public'
                );
    }

    if ( $path ) {
        $rv = $fileName;
    }

    return $rv;
}

}

From the controller, I'm calling the helper method as below:

$file = $request->gif;
$file_name = 'instant_gifs/' . $user_id . '_' . time() . '_' . $file->getClientOriginalName();
$result = uploadFile($file, $file_name);

In the the $fileName parameter of the helper method, I'm providing the fileName as for example in this format:

"instant_gifs/83_1518596022_giphy.gif"

but after the upload, I see that the file gets stored under this folder

"vvstorage/instant_gifs/83_1518596022_giphy.gif/CRm1o1YEcvX3fAulDeDfwT7DIMCxOKG8WFGcA3lB.gif"

with a random file name

CRm1o1YEcvX3fAulDeDfwT7DIMCxOKG8WFGcA3lB.gif

Whereas, according to the code, it should get stored in this path:

"vvstorage/instant_gifs/83_1518596022_giphy.gif"

Doesn't get any explanation why this is happening. Any clue will be appreciated.

BucketName = vvstorage
Folder I'm mimicking = instant_gifs

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
usamurai
  • 501
  • 1
  • 4
  • 9
  • check this for better solution:: https://stackoverflow.com/questions/56105104/how-to-fix-upload-image-to-s3-using-laravel/61826028#61826028 – pankaj May 15 '20 at 18:37

2 Answers2

33

After some research & testing, found the issue. put() method expects the 2nd parameter as the file contents or stream not the file object. In my code, I was sending the file as $file = $request->gif; or $file = $request->file('gif'); hoping that Storage class will implicitly get the file contents. But to get the expected result, I needed to call the helper method from the controller as below. Notice the file_get_contents() part.

$file = $request->gif;
$file_name = 'instant_gifs/' . $user_id . '_' . time() . '_' . $file>getClientOriginalName();
$result = uploadFile( file_get_contents($file), $file_name );

Now, I got the image correctly stored under the correct path for example in /instant_gifs/9_1518633281_IMG_7491.jpg.

Now, let me compare/summarize the available methods for achieving the same result:

1) put():

 $path = Storage::disk('s3')->put(
                    '/instant_gifs/9_1518633281_IMG_7491.jpg', #$path
                    file_get_contents($request->file('gif')), #$fileContent
                   'public' #$visibility

Got it stored in /vvstorage/instant_gifs/9_1518633281_IMG_7491.jpg

2) putFileAs(): To achieve the same thing withputFileAs(), I needed to write it as below. 1st parameter expects the directory name, I left it blank as I'm mimicking the directory name in s3 through the filename.

$path = Storage::disk('s3')->putFileAs(
                '', ## 1st parameter expects directory name, I left it blank as I'm mimicking the directory name through the filename
                '/instant_gifs/9_1518633281_IMG_7491.jpg',
                $request->file('gif'), ## 3rd parameter file resource
                ['visibility' => 'public'] #$options
            );

Got it stored in /vvstorage/instant_gifs/9_1518633281_IMG_7491.jpg

3) storeAs():

$path = $request->file('gif')->storeAs(
             '', #$path
             '/instant_gifs/9_1518633281_IMG_7491.jpg', #$fileName
             ['disk'=>'s3', 'visibility'=>'public'] #$options
             );    

Got it stored in /vvstorage/instant_gifs/9_1518633281_IMG_7491.jpg

Extras:: 4) For storing Thumbnails through put(). Example of stream() ...

$imgThumb = Image::make($request->file('image'))->resize(300, 300)->stream(); ##create thumbnail
        $path = Storage::disk('s3')->put(
                    'profilethumbs/' . $imgName,
                    $imgThumb->__toString(),
                    'public'
                );

Hope that it helps someone.

usamurai
  • 501
  • 1
  • 4
  • 9
2

1.) Why is there vvstorage in the url?

It is appending that route because your root folder inside of your configuration for S3 is set as vvstorage, so whenever you upload to S3 all files will be prepended with vvstorage.

2.) Why random name even when you passed the name of the file?

Because when using put the file will get a unique ID generated and set as it's file name so no matter what you pass, it won't save the file under the name you wanted. But if you use putFileAs then you can override the default behaviour of put and pass a name of the file.

Hope this clarifies it

Nikola Gavric
  • 3,507
  • 1
  • 8
  • 16
  • 1) "vvstorage" is the main Bucket, it'll be always in the file URL. No problem with that. 2) You might have confused `put` with `putFile` method. `putFile` will automatically generate unique filename. `put` method is supposed to store the file in the given path in the disk [Laravel Storing Files](https://laravel.com/docs/5.5/filesystem). The same code with `put` worked while uploading profile images but not working in the files upload section for some reason. Thanks! – usamurai Feb 14 '18 at 09:47
  • I said `putFileAs` not `putFile` – Nikola Gavric Feb 14 '18 at 09:48
  • `Put` should work too as I'm providing a filePath to store. BTW, I tried with `putFileAs` in the above uploadFile code. `$path = Storage::disk($disk)->putFileAs( 'instant_gifs/9_1518602282_IMG_9594.jpg', $fileContent, 'public' );` It now get stored in this path `instant_gifs/9_1518602282_IMG_9594.jpg/public`, with the image name "public". May be something to deal with the Bucket Access Policy. – usamurai Feb 14 '18 at 10:14
  • Might be, but still you will be able to access it with the original name, no matter under what name the storage saved it – Nikola Gavric Feb 14 '18 at 10:17