1
votes

I am using ng-file-upload to upload a JPG file to my S3 Bucket.

file.upload = Upload.upload({
    url: "https://<my-bucket-name>.s3.amazonaws.com/", 
    method: "POST",
    data: {
        key: "custom-filename.jpg", 
        AWSAccessKeyId: "<AWSAccessKeyId>",
        acl: "public-read", 
        policy: <policy>, 
        signature: <signature>, 
        "Content-Type": "image/jpeg", 
        filename: file.name, 
        file: file, 
        Metadata: {
            "x-amz-meta-hello": "Custom Metadata Value"
        }
    }
});

I have also tried the following (in the above code)

Metadata: {
    hello: "Custom Metadata Value"
}

& simply

"x-amz-meta-hello": "Custom Metadata Value"

I have included the Custom Metadata in my Policy file as

["starts-with", "x-amz-meta-hello", ""]

Also, the CORS Configuration under Bucket Permissions on S3 is

<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>HEAD</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <ExposeHeader>x-amz-meta-hello</ExposeHeader>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>

The above code and settings are working, the JPG file is successfully uploaded, but somehow the Custom Metadata value is not being set.

On successful upload, I am calling a Lambda function to resize the JPG and storing it in a separate bucket. Even this part is working but I am not able to read the Custom Metadata (x-amz-meta-hello) value in my Lambda Function. I need that value to assign a separate folder to the uploaded file.

To read the Custom Metadata in my Lambda Function

var s3 = new AWS.S3();
s3.headObject({
    Bucket: <BucketName>,
    Key: <S3ObjectKey>
}, function(err, data) {
    if (err) {
        console.log(err);
    }
    else
    {   
        console.log(data);
    }     
});

Not sure what am I missing here ... Please advise.

Thanks. (AngularJS version 1.5.0, ng-file-upload version 12.2.9, Google Chrome version 53.0.2785.113 on OSX 10.10.5)

1
Strictly from intuition: eliminate the Metadata: { and } construct, and place the metadata key above the file info: ... "Content-Type": "image/jpeg", "x-amz-meta-hello": "Custom Metadata Value", filename: file.name, ...Michael - sqlbot
Thanks ... Worked Perfectly ...SH59

1 Answers

2
votes

Remove the Metadata: { ... } construct, because you're building an HTML form, which has a flat keyspace. Elsewhere you may see metadata given special treatment, where something like Metadata: { "hello": "world" } magically becomes x-amz-meta-hello: world but that is not applicable, here. Instead, metadata needs to start with x-amz-meta-, and since we are making a POST request, it is provided in the form data, rather than as a header.

Because the file data needs to be the last form element, place the metadata key above the file info, e.g.:

...
"Content-Type": "image/jpeg", 
"x-amz-meta-hello": "Custom Metadata Value", 
filename: file.name, 
...

Your policy condition must reference the metadata name with a $ in order to work (["starts-with", "$x-amz-meta-hello", ""]). Note that having the policy is required for the field to be accepted, but this specific condition does not constrain the field to contain a specific value.

Note also that <ExposeHeader>x-amz-meta-hello</ExposeHeader> is not necessary in order to allow the upload. <ExposeHeader> allows the browser to return that response header to the calling code on an AJAX request. Headers not listed (other than the "simple" standard ones, like Content-Type) are otherwise hidden from calling code. CORS is initially counterintuitive, until you understand that it's based on the idea that the web browser itself is assumed to be well-intentioned but naïve. CORS gives the browser permission to do things that it would not otherwise do, such as returning specific headers back to a caller.