Okay, so you have a mobile app, or web client, that communicates with a server.

You want to save and retrieve images that a user can upload (for a profile picture, or any other number of uses). In our case, we’ll use a user’s profile picture as an example.

I am going to show you how to do this in a pretty cool way, that takes a bit of setup, but provides a lot of scalability. Part 2 of this tutorial will be using an Ionic mobile app client, and a NodeJS server, but Part 1 is completely language/framework independent.

What we will do:

We are going to upload the image directly from the client to S3, using a signed (AWS authenticated) request, which is generated by the server. Here is the outline of the interaction:

  • Client: “Hey server, I want to upload an image. Here is my API key, I’m legit. Can you give me a URL where I can upload this image to?”
  • Server: “Hey client. Looking good. I know this guy named S3 who will take your images. Here is a signed request you can use. And by the way, once you upload it, here is the image url that the file will be stored at.
  • Client: “Hey S3, I have this signed request, and and image file that I was hoping you could store for me.”
  • S3: “Cool. 200.”

Implementation Steps (1 of 3):


1. Configure an IAM role and an S3 Bucket:

1a. Configure S3 Bucket:


First, we need to create and configure an AWS S3 bucket. This will involve 5 steps:

  1. Creating the bucket, with a name and region
    • Allowing any user to read (view files) from it
    • Creating a policy to allow anyone to view files from it
    • Configuring CORS
    • (optionally) creating folders within the bucket.

Steps:

  • Log into your AWS console.
  • At the top left, go to “Services” and then find “S3”.
  • Click “Create Bucket”

Then you’ll see a popup:

  • On the popup, page 1:
    • Choose your bucket name
      • This needs to be unique across all S3 buckets, and bucket names have a bunch of rules that I forget. A good rule of thumb, use only “word” (digit/letter) characters, and dashes (“-“). I typically use the format <company>-<app-name>-<env>, so something like apple-app-store-dev.
    • Choose your region (and note which one it is. Mine is us-west-1 but you’ll need to change yours later if yours is different)
    • hit next, and next again, and again, until you’ve created the bucket.
  • Click on the bucket
  • Go to “permissions”. At this point you should be here, with the Access Control List button selected:

  • Under the section “Manage Group Permissions”, click “Everyone”. You’ll get a popup. Check “Read” for Object Access, and hit “Save”:
    • NOTE: This step will make your images publicly readable to anyone, even if they are not an AWS user.

Next, we create a bucket policy, that tells AWS to let people access the bucket in a read-only fashion from anywhere:

  • Still on the Permissions tab, hit the “Bucket Policy” button, and then paste this in (changing to your bucket name, of course):
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowPublicRead",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::tyler-dee-test-image-upload/*"
        }
    ]
}

… then hit “Save.”

Now, last thing on configuring the bucket! But this one is very important. We need to set up CORS configuration, basically what locations can do what procedures on the bucket. We are going to make the configuration pretty lenient here:

  • So, hit the “CORS Configuration” button, and paste this in:
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

Two things to note:

  • The “AllowedHeader” tag(s). By default, this will have ‘Authorization’ as the value. You are more than welcome to customize this (i.e. add more tags individually), but I ran into a LOT of headache with my client sending extra headers that the bucket didn’t “allow”, and the AWS errors are very unhelpful with this error.
  • The “AllowedOrigin” tag(s). If you have a website with a known origin, probably set it to that. But if you have a mobile client, I think it needs to be like that.

Whew! All done with setting up the bucket. You still with me?

1b. Configure IAM role:


Okay cool we created the bucket. Now, we need to create an AWS (IAM) user that has permissions to write files to the bucket (we will use this user’s credentials to upload files). You should definitely do this instead of using your root user’s credentials.

  • Log into your AWS console
  • At the top left, go to “Services” then type IAM and click on that
  • On the left panel, click “Users”, and then hit “Add user”

  • Add a name for your user (whatever is helpful for you to distinguish it, but I would associate it with the app name), and check “programmatic access”:

  • Add a policy to the user. Choose “Attach existing policies directly” and then click “Create policy” (this should open up a new tab):

  • On the new page, hit “Create Your Own Policy”:
  • Then, add a name and a description (make the name helpful because you’ll need to look it up again later), and the contents of the policy:

Full contents of policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1494010063000",
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:GetObjectAcl",
                "s3:ListBucket",
                "s3:PutObject",
                "s3:PutObjectAcl"
            ],
            "Resource": [
                "arn:aws:s3:::tyler-dav-s3-image-demo/*"
            ]
        }
    ]
}
  • Hit “Create”.
  • Go back to the original tab, with the user you are creating. Search for your policy (you probably need to hit “Refresh” first), add it with the checkbox, and continue to “Next: Review.”
  • Review, and create the user!

IMPORTANT: the next page has your access key ID and secret (not shown, because duh). Make sure you copy this down!!!

Conclusion:


Thank goodness, for now, you are done with AWS. Onto the code in part 2 (coming soon!)

The “code part” will vary depending on your client and server. I used (and so will write the tutorial for) Ionic/Angular as the client, with a NodeJS server. If you are using something else (and/or if I take a while to write part 2), here are a couple good tutorials put out by Heroku. I thought they were too light on the AWS part, but the actual app code part is stronger:

Also! Shout out to Jeff Rafter for getting me started, with his “How To” post.

Hope that was helpful

-tyguy