In part 1 we created a simple photo gallery application which provided a good introduction to Arc. Today we’ll expand on our application by adding the ability to upload multiple files at a time and swap out local storage for AWS S3.
If you’d rather grab the completed source code directly rather than follow along, it’s available on GitHub, otherwise let’s get started!
If you’ve been following along you can continue with the code from part 1, or you can grab the part 1 code from GitHub.
Clone the Repo
Let’s create a branch for today’s work:
And then let’s get our dependencies and database set-up.
Now we’ll run
ecto.setup to create our database. Note: before running
ecto.setup you may need to update the username / password database settings in
dev.config to match your local postgres settings, i.e.
/config/dev.config …line 69
With all that out of the way… let’s see where we are starting from.
If we navigate to http://localhost:4000/, sign in and navigate to our photos page we’ll see our basic application.
Nothing fancy, but it gets the job done. The first thing we’ll tackle today is adding the ability to upload multiple files at a time.
Uploading multiple files at a time
One minor annoyance with our current implementation is that users need to upload their files one photo at a time. What if someone has 20 or 30 photos to upload… it’s going to get very tedious if they are required to upload these files one by one.
So let’s implement multiple uploads.
The first change we need to make is to update
form.html.eex to accept multiple files. This is very simple, we just need to add
multiple: :true to the
We’ve also updated the
form_for tag to include an
as attribute. This is due to the fact that our multiple files aren’t backed by an actual model field.
Next we need to update our controller code. In the controller we’ll simply call into a new context method
create_photos that we will be creating shortly. We’ll also update the success message.
/lib/photo_gallery_web/controllers/photo_controller.ex …line 19
So now for that context method. This will be simple; we will just iterate over the photos and call out to the existing
create_photo method passing in each photo.
/lib/photo_gallery/gallery/gallery.ex …line 16
Sweet! We can now select and upload multiple images.
Handling large file sizes
Let’s see what happens if we select a bunch of fairly large files.
We get an error! So we want to do a few things when this scenario presents itself. We’ll want to provide a decent message to the user, and we also want to increase the maximum request size (the default is 8MB).
Improving the error message
Let’s start with the error message. To view what our end users are going to see, we need to switch
/config/dev.config …line 9
With any configuration change we need to restart the server.
If we now attempt our file upload again, we’ll see the following.
Not exactly a friendly error! We won’t spend time creating a pretty error page, but let’s at least improve on things a bit.
We can customize the error shown for a particular status code by creating a template in
/templates/error with a name of the status we want to customize.
So in our case we’ll create a
We’re just going to create a very simple page.
And with that, we end up with a bit more user friendly situation.
We should revert our
dev.config and restart the server now that we’ve got our error message figured.
/config/dev.config …line 9
Increasing the maximum request size
To facilitate large files and multiple uploads we’re also going to increase the maximum request size. This is done in
/lib/photo_gallery_web/endpoint.ex …line 34
length: 200_000_000, this will increase our request size to 200 MB. With this change we can upload our previously too large photos all in one go… fantastic!
That does it for implementing multiple file uploads, next let’s swap out our local storage for
Storing our files on AWS S3
Often in production we’re going to want to use something like
AWS S3 for file storage. It is pretty easy to implement this using
This section assumes you have already set up a bucket on
S3 and a
CloudFront distribution. If you haven’t yet done so, check out this post for instructions on doing so.
You might also want to delete any images you have currently uploaded as these will not be valid after we switch over to
Adding dependencies and updating the Arc configuration
The first step to getting things setup is to add a few new dependencies in
/lib/mix.exs …line 34
We’ve added 5 new dependencies starting with
ex_aws, let’s update our dependencies.
We now need to update our
/config/config.exs …line 39
Note you may need to change the
region value above depending on the region of your bucket. See this document for the various region codes.
We’re using a number of environment variables in the configuration, so we need to set these somewhere.
We’ll accomplish this via an
.env file. Since this file will contain sensitve information we don’t want it checked into source control, it’s very important we add it to our .gitignore file. So let’s do that first.
/.gitignore …line 44
Now let’s create the actual file. When creating configuration files that won’t be checked into source control, I like to also create a
template version of the file. This means when grabbing the source code it should be fairly easy for someone to see what configuration values need to be set. So we’ll start by creating the template file.
Now we’ll copy the file to create the actual
Now replace the values in the
.env file with your actual bucket name, key, secret and cloudfront distribution. This might look something like:
Whenever we make a change or addition to our
.env file we need to run
source .env to load the values, so let’s do that now.
Updating the uploader module
One final change to get
S3 working is to change our uploader module to use
public_read as the ACL (access control list). This will allow our application to access the images and display them.
With all the above in place when we fire up our application it should work as before… but we’ll see our images are being uploaded to
Likewise when we delete an image it will be removed from
Also note that our image URL’s are using our CloudFront distribution.
So that’s it! AWS… done!
Changing up the Arc configuration
One final task before ending for the day. We likely don’t want to be uploading and downloading files from
S3 when we’re developing. Therefore I think we should move the
Arc configuration into environment specific configuration files.
So remove the current
Arc configuration from
config.exs and place it in
/config/prod.exs …line 69
And then in
test.exs we’ll add an
Arc configuration pointing at local storage.
/config/dev.exs …line 77
/config/test.exs …line 20
Now when we run our application in development or against a test suite our file uploads will be handled locally.
So this concludes our tour of
Arc. I feel it provides a convenient way of handling file uploads and is pretty easy to setup and integrate into a Phoenix application.
In terms of the application we built; it is sufficient for demonstration purposes but would require some changes for a real world application. A better upload control or at the very least a spinner widget would be desirable as would a better gallery page. Maybe I’ll write up a follow-up post in the future addressing these items… but for now we’re done and dusted!
Thanks for reading and I hope you enjoyed the post!