mix.exs
defps deps do
[
...
{:ex_aws, "~> 2.0"},
{:ex_aws_s3, "~> 2.0"},
{:hackney, "~> 1.9"},
{:sweet_xml, "~> 0.6"},
{:uuid, "~> 1.1"}
]
end
config.exs
config :ex_aws,
debug_request: true,
access_key_id: [{:system, "AWS_ACCESS_KEY_ID"}, :instance_role],
secret_access_key: [{:system, "AWS_SECRET_ACCESS_KEY"}, :instance_role],
s3: [
scheme: "https://",
host: "s3.ap-southeast-1.amazonaws.com",
region: "ap-southeast-1"
]
How I generate presign url
def get_presign_url(file_type) do
uuid = UUID.uuid4 |> UUID.uuid5(random_string(68))
bucket = "bucketname-#{Mix.env}"
config = %{region: "ap-southeast-1"}
file_ext = grep_file_type(file_type)
query_params = [{"ContentType", file_type}, {"ACL", "public-read"}]
presign_options =[virtual_host: false, query_params: query_params]
{:ok, url} = ExAws.Config.new(:s3, config)
|> ExAws.S3.presigned_url(:put, bucket, "#{uuid}.#{file_ext}", presign_options)
%{upload_url: url, url: get_image_url(bucket, uuid, file_ext)}
end
defp grep_file_type("image/" <> type), do: type
defp grep_file_type("audio/" <> type), do: type
defp get_image_url(bucket, uuid, ext), do: "https://s3.amazonaws.com/#{bucket}/#{uuid}.#{ext}"
list object from my bucket
iex> ExAws.S3.list_objects("bucketname-dev") |> ExAws.request()
:ok,
%{
body: %{
common_prefixes: [],
contents: [],
is_truncated: "false",
marker: "",
max_keys: "1000",
name: "bucketname-dev",
next_marker: "",
prefix: ""
},
headers: [
{"x-amz-id-2", "+3B7XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX="},
{"x-amz-request-id", "BDXXXXXXXXXXXXXXXXXXXX5"},
{"Date", "Fri, 06 Jul 2018 06:42:08 GMT"},
{"x-amz-bucket-region", "ap-southeast-1"},
{"Content-Type", "application/xml"},
{"Transfer-Encoding", "chunked"},
{"Server", "AmazonS3"}
],
status_code: 200
}}
inside postman
// presignUrl
POST http://localhost:4000/graphql
HEADERS Content-Type text/plain
BODY:
{
presignUrl(fileType: "image/png") {
url
uploadUrl
}
}
// uploadUrl return https://s3.ap-southeast-1.amazonaws.com/bucketname-dev/xxxxx-uuid.png/...
// uploadFile
POST https://s3.ap-southeast-1.amazonaws.com/bucketname-dev/xxxx-uuid.png/...
BODY:
// there is this option to choose binary file
// click send and this is what it returned
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
<AWSAccessKeyId>XXXXXXXXXXXXXXXXXX</AWSAccessKeyId>
<StringToSign>AWS4-HMAC-SHA256
20180706T064038Z
20180706/ap-southeast-1/s3/aws4_request XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</StringToSign><SignatureProvided>3d4d1e5cd41e758d7673e2e34fddd2c8879450d1b1ff6c86371d03703c159dd6</SignatureProvided>
<StringToSignBytes>41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 31 38 30 37 30 36 54 30 36 34 30 33 38 5a 0a 32 30 31 38 30 37 30 36 2f 61 70 2d 73 6f 75 74 68 65 61 73 74 2d 31 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 64 39 65 38 35 35 65 38 36 37 61 65 63 32 38 37 32 66 65 34 30 61 30 64 64 65 65 61 31 32 63 33 30 37 36 62 33 64 36 32 33 36 30 65 62 32 61 39 62 63 65 63 61 61 34 37 32 62 36 39 39 39 66 61</StringToSignBytes>
<CanonicalRequest>POST
/bucketname-dev/fc8a24c2-dc5d-5769-8dcb-aea847ed40d4.png
ACL=public-read&ContentType=image%2Fpng&X-Amz-Algorithm=AWS4-
HMAC-SHA256&X-Amz-
Credential=XXXXXXXXXXXXXXXXXXXXXXXXX%2XXXXXXXXXXX%2Fap-southeast-
1%2Fs3%2Faws4_request&X-Amz-Date=20180706T064038Z&X-Amz-
Expires=3600&X-Amz-SignedHeaders=acl%3Bcontenttype%3Bhost
acl:
contenttype:
host:s3.ap-southeast-1.amazonaws.com
acl;contenttype;host
UNSIGNED-PAYLOAD</CanonicalRequest>
<CanonicalRequestBytes>50 4f 53 54 0a 2f 6b 6f 6f 6d 70 69 2d 6c 65 61 72 6e 2d 73 65 72 76 65 72 2d 64 65 76 2f 66 63 38 61 32 34 63 32 2d 64 63 35 64 2d 35 37 36 39 2d 38 64 63 62 2d 61 65 61 38 34 37 65 64 34 30 64 34 2e 70 6e 67 0a 41 43 4c 3d 70 75 62 6c 69 63 2d 72 65 61 64 26 43 6f 6e 74 65 6e 74 54 79 70 65 3d 69 6d 61 67 65 25 32 46 70 6e 67 26 58 2d 41 6d 7a 2d 41 6c 67 6f 72 69 74 68 6d 3d 41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 26 58 2d 41 6d 7a 2d 43 72 65 64 65 6e 74 69 61 6c 3d 41 4b 49 41 49 47 41 4c 44 55 53 32 53 42 4b 4f 57 36 58 51 25 32 46 32 30 31 38 30 37 30 36 25 32 46 61 70 2d 73 6f 75 74 68 65 61 73 74 2d 31 25 32 46 73 33 25 32 46 61 77 73 34 5f 72 65 71 75 65 73 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 31 38 30 37 30 36 54 30 36 34 30 33 38 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 33 36 30 30 26 58 2d 41 6d 7a 2d 53 69 67 6e 65 64 48 65 61 64 65 72 73 3d 61 63 6c 25 33 42 63 6f 6e 74 65 6e 74 74 79 70 65 25 33 42 68 6f 73 74 0a 61 63 6c 3a 0a 63 6f 6e 74 65 6e 74 74 79 70 65 3a 0a 68 6f 73 74 3a 73 33 2e 61 70 2d 73 6f 75 74 68 65 61 73 74 2d 31 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 0a 0a 61 63 6c 3b 63 6f 6e 74 65 6e 74 74 79 70 65 3b 68 6f 73 74 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 4c 4f 41 44</CanonicalRequestBytes>
<RequestId>XXXXXXXXXXXXX</RequestId>
<HostId>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=</HostId>
on AWS S3 bucket config, Access control list, everyone has permission to list and write objects,
Bucket policy
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AddPerm",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::bucketname-dev/*"
}
]
}
Bucket CORS
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
How I run the server
// .env
export AWS_ACCESS_KEY_ID=XXXXXXXX
export AWS_SECRET_ACCESS_KEY=XXXXXXXX
// in shell a process
source .env
mix phx.server
I have my another project which is self learn and I am able to get the file upload to S3 working, but not this next project, also when I return to the previous project that has file upload to S3 working in order to test for the s3 upload, it does not work also and give me the same error