Uploading Images to Digital Ocean Spaces with Elixir
Free Credit
I am assuming at the time of reading this, that you have a DigitalOcean account setup. If not, head over to digitalocean to sign up and receive $10 in free credit.
Environment
Mix it up
Lets create a new mix project! Everything that we walk through can be used just the same in a phoenix application.
mix new digital_ocean_spaces
Dependencies
For this write up we are going to be using an elixir library named Arc to handle object uploads. Lets add our dependencies to the file mix.exs and install.
arc: "~> 0.8.0",ex_aws: "~> 1.1",hackney: "~> 1.6",poison: "~> 3.1",sweet_xml: "~> 0.6"
mix deps.get
We don’t need to get too far into these for the purposes of this walk through. In short, :arc handles file uploads, :ex_aws deals with different AWS services, :hackney is an Erlang http library for making requests, :poison will deal with JSON and :sweet_xml, as you may have guessed, deals with XML.
Generation
Now we can generate our Arc boilerplate using the provided mix task. Arc assumes that we are using the pre phoenix 1.3 folder structure, so when we run the mix task, Arc will create a new web folder with the new module inside of it.
mix arc.g my_module
- For phoenix < 1.3 you should not need to move anything.
- For phoenix >=1.3 you can move this wherever you would like within lib/my_app/ or lib/my_app_web/ and delete the web/ folder
- For applications created using mix new, we can move the new module into the lib directory and delete the web/ folder.
Our Arc generated module should look like:
Configure it out
Our last step before creating a digitalocean space, is configuring our application for :ex_aws and :arc. Open the file config.exs (if using phoenix choose the appropriate environment config.exs) and add the following:
config :arc, storage: Arc.Storage.S3, bucket: System.get_env("AWS_S3_BUCKET")config :ex_aws, :s3, %{ access_key_id: System.get_env("AWS_ACCESS_KEY_ID"), secret_access_key: System.get_env("AWS_SECRET_ACCESS_KEY"), scheme: "https://", host: %{"nyc3" => System.get_env("AWS_HOST")}, region: "nyc3" }
You may have noticed by now that S3 and AWS are all up in our project…but Chris, you told us we were going to be uploading to Digital Ocean Spaces! Fortunately for us the first paragraph of this documentation states:
“The API is interoperable with Amazon’s AWS S3 API allowing you to interact with the service while using the tools you already know.”
- AWS_S3_BUCKET — The bucket name we create
- AWS_ACCESS_KEY_ID — Provided by digitalocean (we’ll get to it.)
- AWS_SECRET_ACCESS_KEY — The secret we create/provide for signing
- AWS_HOST — The host name created when we create our space
Give Me Some Space
Now we will create our space. Sign in to your digitalocean account and then navigate your browser to https://cloud.digitalocean.com/spaces/new
Choose a name for your space, select private, and choose the free or try free pricing option then create your new space. You should see something similar to:
Now we need our AWS_ACCESS_KEY_ID. Navigate your browser to https://cloud.digitalocean.com/settings/api/tokens and again you should see something similar to:
Copy the AWS_ACESS_KEY_ID from under the Key column (omitted) for doUser and lets move on!
Drinking the Elixir
Now we are going to upload the Elm logo to your newly created space!
Open the Arc generated module that we created earlier, mine is my_module.ex and lets add the following to the top of the file:
@acl :read_public
This changes the permissions of uploaded files to be read publicly, allowing us to retrieve an object. For more control, have a look at access_control_permissions
Lets start our project up. Copy the following and paste in your shell. Everything between ** needs to be filled in by you. YOUR SPACE NAME should NOT include http:// or https://
AWS_ACCESS_KEY_ID=*YOUR KEY* \AWS_SECRET_ACCESS_KEY=super_secret \AWS_S3_BUCKET=my_test_bucket \AWS_HOST=*YOUR SPACE NAME*.nyc3.digitaloceanspaces.com \iex -S mix
Lastly lets upload the object!
iex(1)> elm_logo = "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f3/Elm_logo.svg/2000px-Elm_logo.svg.png"iex(2)> DigitalOceanSpaces.MyModule.store(elm_logo)
That’s it! You should now be able to see the file in your bucket.
Bonus
Retrieve your stored object using ExAws
iex(1)> bucket = "my_test_bucket"iex(2)> object_path = "uploads/2000px-Elm_logo.svg.png"iex(3)> ExAws.S3.get_object(bucket, object_path) \iex(4)> |> ExAws.request