We’re now going to give users the ability to attach images to their posts, and we’re going to need to store these images somewhere. That somewhere is going to be Amazon Simple Storage Service (S3). First sign up for an account with Amazon Web Services at http://aws.amazon.com/. This is a bit painful – you will need to enter credit card details and answer an automated phone call from AWS to confirm your phone number. For now, your S3 usage should fit within AWS’s free tier pricing, and you won’t be billed anything.

Be careful; if your site becomes exceptionally popular you can rack up a large bill quite quickly. I once had users eat up 2TB of bandwidth in a month, which adds up to a lot when you’re getting charged 9c per GB.

Amazon S3

Gemfile

We need one new gem in our gemfile, so that Paperclip can use Amazon S3 to store files:

### Gemfile

...
gem 'aws-sdk-v1'

As always, run bundle install to install the gem.

Configuring S3

We’re going to use local storage for images in development and S3 for images in production. We’ll need a configuration file for Paperclip specifically for production. Add the following to the bottom of config/environments/production.rb:

### config/environments/production.rb

...
  config.paperclip_defaults = {
    :storage => :s3,
    :s3_credentials => {
      :bucket => ENV['S3_BUCKET_NAME'],
      :access_key_id => ENV['AWS_ACCESS_KEY_ID'],
      :secret_access_key => ENV['AWS_SECRET_ACCESS_KEY']
    } 
  }
end

And another config file:

### config/initializers/paperclip.rb

Paperclip::Attachment.default_options[:s3_host_name] = 's3-us-west-2.amazonaws.com'

You can see in config/environments/production.rb that we’re using environment variables again to store our AWS keys. We need to set them on Heroku. We get our AWS secret keys by selecting the Security Credentials option from the drop down menu in the top right of the AWS console.

AWS Keys

You’ll be greeted by the following message; one day you’ll want to Get Started with IAM Users, but not today.

IAM Users Modal

Now select Create New Access Key as shown here:

Create New Secret Key

Download your key file and open it up in a text editor or excel.

We also need to create a new S3 bucket to store our images. Navigate to http://aws.amazon.com/s3 and select Create Bucket. Give it the same name as your app. Now we can set our keys and bucket name in Heroku environment variables.

heroku config:set S3_BUCKET_NAME=memespace
heroku config:set AWS_ACCESS_KEY_ID=<your_access_key_id>
heroku config:set AWS_SECRET_ACCESS_KEY=<your_secret_access_key>

A great article I always reference is Uploading Files to S3 in Ruby with Paperclip from the Heroku devcenter. Check there if you’re having trouble (then contact me).

Views

We need to add an image field to our post form and display the images on the news feed. Both of these changes are in the same file, so we’ll make both changes at once here:

### app/views/shared/_feed.html.erb

<% if current_user %>
    <div class="panel panel-default">
      <div class="panel-body">
        <% if @user %>
          <%= form_for(@post, url: receive_post_path(@user.id), :html => { :multipart => true }) do |f| %>
            <div class="form-group">
              <input type="text" name="post[message]" class="form-control" placeholder="What's on your mind?"/>
            </div>
            <div class="form-group">
              <%= f.file_field :image %>
            </div>
            <input type="submit" value="Submit" class="btn btn-primary"/>
          <% end %>
        <% else %>
          <%= form_for(@post, :html => { :multipart => true }) do |f| %>
            <div class="form-group">
              <input type="text" name="post[message]" class="form-control" placeholder="What's on your mind?"/>
            </div>
            <div class="form-group">
              <%= f.file_field :image %>
            </div>
            <input type="submit" value="Submit" class="btn btn-primary"/>
          <% end %>
        <% end %>
      </div>
    </div>
<% end %>

<% @posts.each do |post| %>
    <div class="panel panel-default">
      <div class="panel-body">
        <h4 style="margin-top: 0px;">
          <a href="<%=user_path(post.user)%>"><%= post.user.name %></a>
          <% if post.receiver %>
            > <a href="<%=user_path(post.receiver)%>"><%= post.receiver.name %></a>
          <% end %>
          <% if current_user == post.user %>
            | <%= link_to 'Delete', delete_post_path(post.id), method: :delete, data: { confirm: 'Are you sure?' } %>
          <% end %>
        </h4>
        <% if post.image.url != '/images/original/missing.png' %>
          <div align="center" style="margin-bottom: 1em;">
            <%= image_tag post.image.url, style: "max-width: 100%; max-height: 500px;" %>
          </div>
        <% end %>
        <%= post.message %>
      </div>
    </div>
<% end %>

We need to pass  :html => { :multipart => true } to form_for if we’re attaching an image.

Model

Our Post table has some attributes related to the image, but we still need to add some methods to our Post model to attach images.

### app/models/post.rb

class Post < ActiveRecord::Base
  belongs_to :user
  belongs_to :receiver, class_name: 'User'
  default_scope { order(created_at: 'DESC') }
  
  has_attached_file :image, styles: { medium: "500x500>" }, default_url: "/images/:style/missing.png"
  validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
end

Paperclip uses a command-line utility called imagemagick to manipulate and gain information about images, so we need to install imagemagick to our Cloud9 machine using the aptitude package manager, which manages package installation on Ubuntu for us.

sudo apt-get update
sudo apt-get install imagemagick

Type Y and press enter when prompted. Imagemagick is already installed on Heroku machines.

Now we’re all done! You should be able to upload images alongside your posts.

Before we deploy, we need to stop git from pushing all of our locally stored images to the production repo, because that’s a waste of time and space on our server. We tell git to ignore files using a .gitignore file. You should already have a file named .gitignore in the root of your project alongside Gemfile. Open it up and add the following line to the end:

### .gitignore

...
public/system/*

public/system is actually where Paperclip has been putting images uploaded to our app when it’s been running locally in development mode. Now we can push up our code:

git add -A
git commit -am "Added image uploads!"
git push heroku master 
heroku run rake db:migrate

And you should find yourself capable of posting images to the newsfeed and others’ profiles.

Image Post

Awesome. In the next chapter we’re going to be adding flash boxes which give feedback to the user when they perform an action or submit an invalid form.