How to set Open/Download permissions on a file cre

2019-08-15 03:53发布

问题:

I have an Amazon Lambda function that successfully writes files to an Amazon S3 bucket. However, by default these files are not publicly accessible. How do I make them automatically accessible when they are written?

Is there a way to change the bucket itself so that all items are publicly readable (open/download)?

Alternatively, I've gleaned that this can be done with the IAM role policy. This is what I have:

{
"Version": "2012-10-17",
"Statement": [
{
  "Effect": "Allow",
  "Action": [
    "logs:CreateLogGroup",
    "logs:CreateLogStream",
    "logs:PutLogEvents"
  ],
  "Resource": "arn:aws:logs:*:*:*"
},
{
  "Effect": "Allow",
  "Action": [
    "s3:GetObject",
    "s3:PutObject"
  ],
  "Resource": [
    "arn:aws:s3:::*"
  ]
}
]
}

I think it's the "Resource" that needs to be changed, but I'm not sure how.

For completeness (in case anyone else needs to do something similar and is wading into the morass that is the Lambda documentation), here's my ajax call on the clientside (I'm writing an image to S3 and need the filename back):

$.ajax({
url: 'https://mylambdafunctionurl/',
type: 'POST',
crossDomain: true,
contentType: 'application/json',
data: JSON.stringify(data),
dataType: 'json',
success: function(data) {
    var path = "https://myregion.amazonaws.com/mybucket/";
    var filename = JSON.parse(data).filename;
    document.getElementById('finalimage').innerHTML = "<a href='"+path+filename+"'>Your image</a>";
},
error: function(xhr, ajaxOptions, thrownError) {
    if (xhr.status == 200) {
        console.log(ajaxOptions);
    } else {
        console.log("Error: ");
        console.log(xhr.status);
        console.log(thrownError);
    }
}
});

And here is the Lambda POST function (nodejs):

var AWS = require('aws-sdk');
var s3 = new AWS.S3();

exports.handler = function(event,context) {
    var s3 = new AWS.S3();
    var nowtime = new Date();
    var bodycontent = event.image;

    mykey = nowtime.getTime() + '.png';
    var param = {Bucket: 'mybucket', Key: mykey, Body: bodycontent};
    var successdata = {filename:mykey};
    s3.upload(param, function(e,data) {
        if (e) {
            console.log(e,e.stack);
        } else {
            console.log(event);
        }
        context.done(null,JSON.stringify(successdata));
    });
}

Caveat: using the current time for the filename is not production ready. It was just a quick way to produce a reasonably unique filename.

回答1:

as per this page example: http://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_examples.html you could set a IAM role policy like this

...
 {
  "Effect": "Allow",
  "Action": [
    "s3:PutObject",
    "s3:GetObject",
    "s3:DeleteObject"
  ],
  "Resource": "arn:aws:s3:::EXAMPLE-BUCKET-NAME/*"
}
...

So you could combine it with a folder like arn:aws:s3:::EXAMPLE-BUCKET-NAME/public/* I think

Or you could create a whole bucket with public access to all files just for uploading.

Or maybe you could use getSignedUrl from the sdk to the just uploaded file and return that url to the client, that will make the file accessible through that url for a given time(don't remember right now how long they are valid), like this:

s3.putObject({
  Bucket: bucket,  
  Key: key,
  Body: fs.createReadStream(path),
  ContentType: contentType,
}, function(err, data) {  
  if (err)
    return callback(err, null);  

  s3.getSignedUrl('getObject', {
    Bucket: 'mybucket',    
    Key: 'mypublicfiles/myfile.txt'
  }, function(err, url) {    
    if (err)
      return callback(err, null)
    else
      return callback(null, url)
  });
});


回答2:

In addition to @mithril_knight's answer, I also needed a bucket policy (set in the S3 console):

{
"Id": "Policyxxxxxxxxxx",
"Version": "2012-10-17",
"Statement": [
{
  "Sid": "Stmtxxxxxxxxxx",
  "Action": [
    "s3:GetObject"
  ],
  "Effect": "Allow",
  "Resource": "arn:aws:s3:::mybucket/*",
  "Principal": {
    "AWS": [
      "*"
    ]
  }
}
]
}

The mojo I needed was the "Principal" of "*", which means "Everyone".