Skip to main content

5 Uploading Files to DigitalOcean Spaces

Episode 213 · October 31, 2017

Learn how to upload files to DigitalOcean's Spaces which is an Amazon S3 compatible file storage API

File Uploading


Transcripts

What's up guys? This episode I wanted to introduce you to digital ocean spaces, this is Digital Ocean's Amazon s3 competitor, so if you ever want to upload images to your application, and you happen to already be using digital ocean, this is a really really awesome competitor to Amazon s3. Most of the time, if you're uploading files in your rails app, you're going to need some sort of storage that's outside of your server and so you can use Amazon s3 as the default or now you can choose Digital Ocean spaces. I have no affiliation with them, I just host my rails ap on Digital Ocean, and this is a feature I'm definitely going to be using, so I wanted to document that as I go set it up, so it's really easy to set up, you log into your account, you create a space, and as you give the name, it will create a URL, I've already got one set up, so I'm not going to go create a new one, but you choose your region, right now it's only in their NYC3 region,and ten you can manage access to it and say all of the files by default should either be private or public, in most cases, when you're building a rails app, and people are uploading avatars and pictures and things like that, it will be public files, so it makes sense to do "Public" here, but keep that in mind, you have the option to do whichever you like and you have the file by file permissions level, so you can actually specify if everything by default was private, you can say: This should be public, that should be public and just do it on a one-off basis. So I'm going to use "Public", and then pricing is free right now, until November 19th, and this should start to roll out, and it will only cost you five bucks for the equivalent s3, it's like 94 dollars a month, which is awesome, so they're definitely trying to be competitive here, but I believe they charge you a minimum of 5 dollars every month, whereas Amazon, you might be charged three cents if you're not really using it that much. So with your spaces configured and created, you will go over to the API section and towards the bottom here, there’s a “Spaces access keys” section, so you want to make sure that you have a key, and you grab the key name, this value here, and it will give you a secret as well, which I won’t show you, but you will take those two, and you will put them inside of your secrets.yml file, and I’ll show you that in a minute, I’m going to load those from the environment, but you can drop them in to your secrets.yml directly or put them into your environments, so whichever is easier to do development with, and test this out.

Now, also keep in mind that you will want to remember and use your spaces name inside your secrets.yml file, so ours is called gorails, all lowercase, and we want to put that as the bucket name for the AWS SDK, so let’s dive into our application and configure Shrine to do uploads to Digital Ocean spaces.

Our Digital Ocean here has nothing in it right now, I’ve just got bootstrap and navigation set up and that’s it, we’re going to go to the bottom of our Gemfile and we’ll add Shrine in here, and we’ll also add the AWS SDK for s3, and that is what we’re going to use to configure Shrine to talk to spaces, because spaces and s3 are pretty much API compatible, now spaces doesn’t have near the level of granularity and detail yet that Amazon s3 does, but in the future you’ll be able to go and configure cores, and do all the same stuff that you would want to do from AWS s3. With that said, we are going to install these two gems, and that is going to give us access to our Shrine configuration, which we’ll then configure to use the key, the secret and the bucket name or the space name that we created and so from here, we will go and say: rail g scaffold Photo image_data:text, and this is going to be the model that we’ll mount Shrine to, so let’s create that with Shrine real fast, we’re going to create the class and app models called

image_uploader.rb

class ImageUploader < Shrine
 end

Then you can configure your different cropping and sizing for images, and then in your Photos model, the only thing that you’ll have to do here Photo.rb is

app/models/photo.rb

class Photo < ApplicationRecord
  include ImageUploader[:image]
end

app/views/photos/_form


<div class="field">
    <%= form.label :image %>
    <%= form.file_field :image, :photo_image %>
</div>

app/controllers/photos_controller.rb

def photo_params 
  params.require(params[:id])
end 

That will configure everything from Shrine on the model side, but now we need to go into

config/initializers/shrine.rb

 require 'shrine/storage/s3'

s3_options = {
    access_key_id: Rails.application.secrets.digitalocean_spaces_key,
    secret_access_key: Rails.application.secrets.digitalocean_secret_key,
    bucket: Rails.application.secrets.digitalocean_bucket_key,
    endpoint: 'https://nyc3.digitaloceanspaces.com',
    region: 'nyc3'
}
Shrine.storages = {
    cache:
    store:
}

Shrine.plugin :activerecord

The key and the secret are the ones that came from the API section, so we’re going to drop those in here, or in your environment

development:
  secret_key_base: 
  digitalocean_spaces_key: <%= ENV["DIGITALOCEAN_SPACES_KEY"] %>
  digitalocean_secret_key: <%= ENV["DIGITALOCEAN_SECRET_SECRET"] %>
  digitalocean_bucket_key: <%= ENV["DIGITALOCEAN_BUCKET_BUCKET"] %>

Once you’ve got these configured in your environment or in your secrets.yml, then you can go ahead and close that up, and then we can begin to set up our shrine storages for s3.

config/initializers/shrine.rb

Shrine.storages = {
    cache: Shrine::Storage::S3.new( prefix: 'cache', upload_options: {acl: 'public-read'}, **s3_options),
    store: Shrine::Storage::S3.new( prefix: 'store', upload_options: {acl: 'public-read'}, **s3_options),
}

Let’s run rake db:migrate since we created that scaffold, and we’ll run our rails server, and get all of that up and running in our browser.

Here you can see our scaffolds for the photos, we can choose a file, we can grab a photo of St. Louis, create that photo, and this will go and post it up to Digital Ocean spaces. Now, I have some trouble while I was setting this up, you know, I deleted my space and then recreated it, so there are some bugs that you might run into, so keep that in mind, this is not officially out yet, but I wanted to walk you through this process, and here you can see the image data information that we get back from Shrine, which we need to convert to an actual image tag, so if we go to the

app/views/photos/show.html.erb

<p>
  <strong>Image data:</strong>
    <%= @photo.image_data %>
    <%= image_tag @photo.image_url %>
</p>

We can try that and see if that works, but what I noticed was when I do that, I just get this alternative text which is the file name, which isn’t exactly what we want, and what I noticed as well, we have to say <%= imag_tag @photo.image.url(public: true) %>, and if we do that, this time we get the image to embbed, and that will work, so I don’t know if that’s exactly one of the quirks of digital ocean spaces or not, but that is something I had to do to get the images to work. Now, as far as I can tell, there are no configuration options for core support yet, so I don’t believe you’ll be able to do direct uploads to digital ocean spaces just yet, but this is their very first iteration of their product, and there’s a lot more coming, so I expect that to be supported in the near future. Right now, if you just need some really good bulk storage for really really cheap and good bandwidth, then Digital Ocean Spaces is a perfect choice.

That’s it for this episode, Digital Ocean Spaces may not be perfect yet, but it’s on it’s way there, and I really really like the price point that it offers, plus the awesome configuration options here that literall use the Amazon s3 gem in order to talk to digital ocean instead. That is pretty awesome, because it means that anyone who is looking for maybe a cheaper bill would be able to just change some configuration options and copy over all of their s3 files to Digital Ocean and then have a cheaper bill, and that would be very little work from a developer perspective in comparison to writing a whole new gem or library to go connect to another service in it’s own special way.

I thought that was a good business move, and something that you can probably consider when building out your own APIs and figuring out a way to standardize that so that maybe you can easily get customer from other businesses by making your code very very similar to an implementation of a competitor or something like that. So keep that in mind as you’re building things, but that is all for this episode, I will talk to you next week. Peace v

Transcript written by Miguel

Discussion