visualight's avatar

Amazon S3 Upload ACL

Hello,

I try to upload a file to Amazon S3. If i do this, it work fine but the file is not accessible to public-read ACL.

Storage::disk('s3')->put($fileName, File::get($file))

Next, i try this but it doesn't work.

Storage::disk('s3')->put($fileName, File::get($file), 'public')

Can someone help me ?

Thank You

0 likes
22 replies
visualight's avatar

@vipin93 I had already made this operation on the bucket but it does not work For my key and secret i generated a user on the IAM interface on amazon. In laravel i use these credential but all file uploaded with the permission is : (ME)

ME : List - Upload/Delete - View Permissions - Edit Permissions EVERYONE : View Permissions AUTH USER : Upload/Delete - View Permissions

visualight's avatar

@vipin93 It change nothing i get always an error : AccessDenied after upload (when i try to view it by clicking on the link provided by amazon). If i manually set the permission on S3 panel (make public) it work.

Here is my config (filesystems.php)

's3' => [

'driver' => 's3',

'key' => '********',

'secret' => '*******',

'region' => 'eu-central-1',

'bucket' => '*******',

'visibility' => 'public'

],

BENderIsGr8te's avatar

I am not sure what version of the API you are using, but I use the following and it works just fine for me.

Storage::disk('s3')->put($fileName, $fileContents, 'public');

I see in your original question you said that's not working. While this worked great for me, the only other problem I had is that I needed another service to be able to connect and add files to this bucket, so in that case I had to put a Bucket Policy in place which basically overrides everything.

Amazon has a Bucket Policy Generator here....

http://awspolicygen.s3.amazonaws.com/policygen.html

However, in Typical AWS fashion you really have to know every setting name and option to be able to use it. So you're better off just using some Sample policies and using them to build your policy.

http://docs.aws.amazon.com/AmazonS3/latest/dev/example-bucket-policies.html

I am not sure why the normal method is not working for you (and it could be that your Access Credentials don't allow for creating custom ACL...maybe insure your IAM Keys you are using give you FULL S3 access.

1 like
superern's avatar

This one still works. adding 'public'

Storage::disk('s3')->put($fileName, $fileContents, 'public');

aroggio's avatar

It looks like you already have an answer you like, but I had a similar problem, which I solved with an Amazon Identity and Access Management (IAM) policy. I thought you might also find it interesting.

My symptoms were:

  • I could not set file-level Amazon S3 visibility when I uploaded a file (using the league/flysystem-aws-s3-v2 package).
  • Using Flysystem with the Storage facade, the exists() method returned false, even though the files() method returned an array of file names.
  • The get() method returned a file not found exception.

The fix was to create a new user group in Amazon IAM. I gave this new user group an inline access policy, granting group members full access to S3.

Here is what that policy looks like.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": "*"
    }
  ]
}

The final step was to add my user to this group. Once the user had full access to S3, I was able to use Flysystem as expected.

Lenius's avatar

I use this when I upload.

If there are other ways to set the Expires and CacheControl please let me know.

http://docs.aws.amazon.com/aws-sdk-php/latest/class-Aws.S3.S3Client.html#_putObject

           $ext = $request->file('file')->guessExtension(); 
       $file = "test/".time().".".$ext;

            Storage::disk('s3')->getDriver()->getAdapter()->getClient()->putObject(array(
                'Bucket'        => env('AWS_BUCKET'),
                'Key'           => $file,
                'Body'          => file_get_contents($request->file('file')),
                'ACL'           => 'public-read',
                'ContentType'   => $request->file('file')->getMimeType(),
                'Expires'       => 'Thu, 01 Dec 2030 16:00:00 GMT',
                'CacheControl'  => 'max-age'
            ));

Normal upload (Storage::disk) https://netferie.s3-eu-west-1.amazonaws.com/abc/1432757755.jpeg

Special upload (Expires & CacheControl are set) https://netferie.s3-eu-west-1.amazonaws.com/abc/1432759675.jpeg

1 like
qwertynik's avatar

@Lenius Thanks for sharing the code to access the internal driver.

Using the env might have unintended consequences - after caching values might be returned as null. Best to use the values from config.

duby2008's avatar

Hello,

I tried this solution but it is assumed that when it comes to the date indicated in the file expires AWS S3 storage should not be disposed ?.

As follows :(.

Thanks in advance and greetings

ctaljaardt's avatar

It is possible

Here is a snippet of my code to handle avatars.

Storage::disk('s3')->put('/profile/avatar/' . $request->user()->id . ".png", file_get_contents($file));
Storage::disk('s3')->setVisibility('/profile/avatar/' . $request->user()->id . ".png", 'public');
6 likes
rui.castro's avatar

Using laravel 5.3 as suggested in the documentation works perfectly.

Storage::putFile('path/to/image', $image, 'public');

Files are created in S3 with this permission:

Grantee: Everyone
Open/Download: TRUE 
View Permissions: FALSE
Edit permissions: FALSE
2 likes
theotherdy's avatar

Late to the party about this but, picking up on @aroggio's point, just thought it worth sharing that if you want to use:

$path = Storage::putFile('avatars', $request->file('avatar'), 'public');

with aws s3, ie you are setting permissions on the file (public) as well as uploading them, your IAM user will need to have permission to do this (s3:PutObjectAcl) - e.g:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket",
                "s3:ListAllMyBuckets",
                "s3:GetBucketLocation"
            ],
            "Resource": [
                "arn:aws:s3:::my-bucket"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:PutObjectAcl", 
                "s3:GetObject",
                "s3:DeleteObject"
            ],
            "Resource": [
                "arn:aws:s3:::my-bucket/*"
            ]
        }
    ]
}
9 likes
BradKriss's avatar

@theotherdy is correct. I'd also add the s3:GetObjectAcl permission so the Storage::getVisibility($file) method works

jv2222's avatar

There is a new issue when creating a new bucket. As of today (Apr 2019) AWS defaults both of these settings to checked when creating a new bucket:

Manage public access control lists (ACLs) for this bucket
Access control lists (ACLs) are used to grant basic read/write permissions to other AWS accounts.
[x] Block new public ACLs and uploading public objects (Recommended) 
[x] Remove public access granted through public ACLs (Recommended)

Effectively these render this code here useless:

\Storage::put($s3Path, file_get_contents($localFile), 'public');

You'll need to uncheck them to allow Laravel to make those ACL changes on a per upload basis.

I think I read that this only affects new buckets (not existing ones) which may be the reason why it was so hard to work out what was going on!

To change those permissions navigate to the top level of your bucket and click permissions and then click edit.

4 likes
SinghWithLaravel's avatar

As per the question, we can simply give permissions while uploading image, while we can do the same using bucket policy as well. Below code will make uploaded file as publicily readable.

$path = Storage::putFile('avatars', $request->file('avatar'), 'public');

That't it.

Please or to participate in this conversation.