# `Waffle.Storage.S3`
[🔗](https://github.com/elixir-waffle/waffle/blob/v2.0.0/lib/waffle/storage/s3.ex#L2)

The module to facilitate integration with S3 through ExAws.S3

    config :waffle,
      storage: Waffle.Storage.S3,
      bucket: {:system, "AWS_S3_BUCKET"}

Along with any configuration necessary for ExAws.

[ExAws](https://ex-aws.hexdocs.pm) is used to support Amazon S3.

First, add the ExAws dependencies to `mix.exs`:

    {:ex_aws, "~> 2.7"},
    {:ex_aws_s3, "~> 2.1"},
    {:req, "~> 0.7"},
    {:sweet_xml, "~> 0.6"}

Configure ExAws to use Req as its HTTP adapter:

    config :ex_aws,
      http_client: ExAws.Request.Req

Then follow the [ExAws installation instructions](https://ex-aws.hexdocs.pm/readme.html#getting-started)
to configure credentials, a JSON codec, and any other required options.

To store your attachments in Amazon S3, you'll need to provide a
bucket destination in your application config:

    config :waffle,
      bucket: "uploads"

You may also set the bucket from an environment variable:

    config :waffle,
      bucket: {:system, "S3_BUCKET"}

In addition, ExAws must be configured with the appropriate Amazon S3
credentials.

ExAws has by default the following configuration (which you may
override if you wish):

    config :ex_aws,
      json_codec: Jason,
      access_key_id: [{:system, "AWS_ACCESS_KEY_ID"}, :instance_role],
      secret_access_key: [{:system, "AWS_SECRET_ACCESS_KEY"}, :instance_role]

This means it will first look for the AWS standard
`AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment
variables, and fall back using instance meta-data if those don't
exist. You should set those environment variables to your
credentials, or configure an instance that this library runs on to
have an iam role.

## Specify multiple buckets

Waffle lets you specify a bucket on a per definition basis. In case
you want to use multiple buckets, you can specify a bucket in the
definition module like this:

    def bucket, do: :some_custom_bucket_name

You can also use the current scope to define a target bucket

    def bucket({_file, scope}), do: scope.bucket || bucket()

## Access Control Permissions

Waffle defaults all uploads to `private`.  In cases where it is
desired to have your uploads public, you may set the ACL at the
module level (which applies to all versions):

    @acl :public_read

Or you may have more granular control over each version.  As an
example, you may wish to explicitly only make public a thumbnail
version of the file:

    def acl(:thumb, _), do: :public_read

Supported access control lists for Amazon S3 are:

| ACL                          | Permissions Added to ACL                                                        |
|------------------------------|---------------------------------------------------------------------------------|
| `:private`                   | Owner gets `FULL_CONTROL`. No one else has access rights (default).             |
| `:public_read`               | Owner gets `FULL_CONTROL`. The `AllUsers` group gets READ access.               |
| `:public_read_write`         | Owner gets `FULL_CONTROL`. The `AllUsers` group gets `READ` and `WRITE` access. |
|                              | Granting this on a bucket is generally not recommended.                         |
| `:authenticated_read`        | Owner gets `FULL_CONTROL`. The `AuthenticatedUsers` group gets `READ` access.   |
| `:bucket_owner_read`         | Object owner gets `FULL_CONTROL`. Bucket owner gets `READ` access.              |
| `:bucket_owner_full_control` | Both the object owner and the bucket owner get `FULL_CONTROL` over the object.  |

For more information on the behavior of each of these, please
consult Amazon's documentation for [Access Control List (ACL)
Overview](https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html).

## S3 Object Headers

The definition module may specify custom headers to pass through to
S3 during object creation.  The available custom headers include:

  *  `:cache_control`
  *  `:content_disposition`
  *  `:content_encoding`
  *  `:content_length`
  *  `:content_type`
  *  `:expect`
  *  `:expires`
  *  `:storage_class`
  *  `:website_redirect_location`
  *  `:encryption` (set to "AES256" for encryption at rest)

As an example, to explicitly specify the content-type of an object,
you may define a `s3_object_headers/2` function in your definition,
which returns a Keyword list, or Map of desired headers.

    def s3_object_headers(version, {file, scope}) do
      [content_type: MIME.from_path(file.file_name)] # for "image.png", would produce: "image/png"
    end

## Alternate S3 configuration example

If you are using a region other than US-Standard, it is necessary to
specify the correct configuration for `ex_aws`.  A full example
configuration for both waffle and ex_aws is as follows:

    config :waffle,
      bucket: "my-frankfurt-bucket"

    config :ex_aws,
      json_codec: Jason,
      access_key_id: "my_access_key_id",
      secret_access_key: "my_secret_access_key",
      region: "eu-central-1",
      s3: [
        scheme: "https://",
        host: "s3.eu-central-1.amazonaws.com",
        region: "eu-central-1"
      ]

> For your host configuration, please examine the approved [AWS Hostnames](http://docs.aws.amazon.com/general/latest/gr/rande.html).  There are often multiple hostname formats for AWS regions, and it will not work unless you specify the correct one.

# `s3_key`

---

*Consult [api-reference.md](api-reference.md) for complete listing*
