5
$\begingroup$

I'm tring to use the ExternalStorageUpload function with AmazonS3. It works fine, but I couldn't find a way to control ACL permissions. I would like to make the file publicly accessible, and the default is private.

ExternalStorageUpload[
  File["my_file_path.png"],
  ExternalStorageObject[
    <|
      "ExternalStorageBase" -> "AmazonS3",
      "ACL" -> "public-read",
      "Path" -> "s3://my_s3_path/"
    |>]
]

The file is uploaded to S3, but the permission is still private.

Any clue?

$\endgroup$
1
  • $\begingroup$ Any idea if SetPermissions will work? $\endgroup$
    – Carl Lange
    Feb 3, 2022 at 14:51

2 Answers 2

6
$\begingroup$

ExternalStorage does not provide options for specifying ACLs or other metadata for uploaded objects.

You could use the AWS service connection to update the ACL of your object after you've uploaded it. First, upload an object using ExternalStorage:

In[1]:= file = FindFile["ExampleData/turtle.jpg"];

In[2]:= obj = 
 ExternalStorageUpload[File[file], "my-bucket", 
  ExternalStorageBase -> "AmazonS3"]
Out[2]= ExternalStorageObject["my-bucket/turtle.jpg", \
<|"BucketName" -> "my-bucket", "Path" -> "turtle.jpg", 
  "ExternalStorageBase" -> "AmazonS3"|>]

Get a ServiceObject for the S3 service:

In[3]:= s3 = ServiceExecute["AWS", "GetService", {"Name" -> "S3"}]
Out[3]= ServiceObject["AWS-S3", 
 "ID" -> "connection-7ec0edb3b66bbafdf5418222f653774f"]

Check the object's current ACL:

In[4]:= s3["GetObjectAcl", "Bucket" -> obj["BucketName"], 
   "Key" -> obj["Path"]]["Grants"] // Normal
Out[4]= {<|"Grantee" -> <|"DisplayName" -> "jesse", 
    "ID" -> "3f2e23b2ee1991da34bdc689a44a229d1b64f3b617d6569ffb6e19f760acfded", "Type" -> "CanonicalUser"|>, 
  "Permission" -> "FULL_CONTROL"|>}

Update object to use "public-read" canned ACL:

In[5]:= s3["PutObjectAcl", "Bucket" -> obj["BucketName"], 
 "Key" -> obj["Path"], "ACL" -> "public-read"]
Out[5]= Success["AWS-SuccessResponse", <|"MessageTemplate" -> "An \
empty response with status code `1` was received.", 
  "MessageParameters" -> {200}, "Request" -> "PutObjectAcl", 
  "StatusCodeDescription" -> "OK", "Service" -> "AWS-S3"|>]

Check the object's new ACL:

In[6]:= s3["GetObjectAcl", "Bucket" -> obj["BucketName"], 
   "Key" -> obj["Path"]]["Grants"] // Normal
Out[6]= {<|"Grantee" -> <|"DisplayName" -> "jesse", 
    "ID" -> "3f2e23b2ee1991da34bdc689a44a229d1b64f3b617d6569ffb6e19f760acfded", "Type" -> "CanonicalUser"|>, 
  "Permission" -> 
   "FULL_CONTROL"|>, <|"Grantee" -> <|"Type" -> "Group", 
    "URI" -> "http://acs.amazonaws.com/groups/global/AllUsers"|>, 
  "Permission" -> "READ"|>}

The AWS service connection is a general client for AWS's service APIs, and is useful for accessing S3 features beyond the basic functionality covered by ExternalStorage. ExternalStorage and the AWS service connection share mostly the same mechanism for specifying credentials, but are otherwise separate systems.

$\endgroup$
0
$\begingroup$

Follows my function to solve the case, using ServiceExecute.

saveImageToS3 // ClearAll
saveImageToS3[data:{__File}]:= Module[{aws, s3, r},

    aws = ServiceConnect["AWS"];
    s3 = ServiceExecute[aws, "GetService", {"Name" -> "S3"}];
    
    r = Map[
        s3["PutObject",
            <|
            "Bucket" -> "my_bucket_name"
            , "ACL" -> "public-read"
            , "RequestRegion"-> "sa-east-1"
            , "Body" -> ExportByteArray[Import[#], "PNG"]
            , "Key"-> "my/bucket/path/" <> FileNameTake[#]
            |>
        ]&,
        data
    ];
        
    ServiceDisconnect@aws;
    
    r
]

You can use It as:

saveImageToS3[{File["A"],File["B"], ...}]
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.