Uploading Images to a Rails App via JSON API

The whole process is pretty simple (if you do it rarely enough not to care about performance or data transmitted). Unfortunately, the error messages you might get along the road (both from Paperclip and Carrierwave) aren’t particularly helpful. So here’s a short explanation of a couple possible issues. I’ll assume you’re trying to upload a new avatar image for your User model.

If you use Grape for your API you’ll get the params as a Hashie::Mash. When you just pass the params along to the User model, three things might happen (depending on which gem you’re using to handle the images and the phase of the moon): error message that a Hashie::Mash isn’t supported, error message about an unexpected nil (somewhere, don’t know where) or it can just silently fail.

It’s not really Hashie::Mash’s fault. JSON’s got no support for passing files around. The simplest thing that could possibly work is encoding this image file of yours using Base64 before uploading and pass the resulting string as one of the parameters. Then decode it on the server side into a binary tempfile, which you’ll pass into the User model.

When you do that, you might hit another little bump on this road. If you close and unlink the temporary file to early, neither Paperclip, nor Carrierwave will let you know that something’s wrong. Just another silent fail.

So, here’s some example code that handles that. Good luck!

#  user_api_request_spec.rb
require 'spec_helper'

describe Users do

  describe 'Update user' do
    let(:user) { FactoryGirl.create(:user) }
    let(:image) {
      Rack::Test::UploadedFile.new('spec/fixtures/images/treme.jpg', 'image/jpeg')

    before do
      post "/api/users/#{user.id}", {
          avatar: {
            :content_type => image.content_type,
            :filename => image.original_filename,
            :file_data => Base64.encode64(image.read)
        }.to_json, "Content-Type" => "application/json"

    it 'returns avatar URL' do
      JSON.parse(response.body)['avatar_url'].should_not be_nil

# user_update_service.rb
class UserUpdateService

  def initialize(user, params)
    @user = user
    @params = params

  def process
    @params[:avatar] = parse_image_data(@params[:avatar]) if @params[:avatar]


  def parse_image_data(image_data)
    @tempfile = Tempfile.new('user-avatar')
    @tempfile.write Base64.decode64(image_data[:file_data])

      :tempfile => @tempfile,
      :content_type => image_data[:content_type],
      :filename => image_data[:filename]

  def clean_tempfile
    if @tempfile

Share on Twitter, Facebook
Prev Next