Render Images from a Dropbox Folder
I take this approach when I am spinning up a Rails app quickly and want to get a quick first draft out the door. It’s works best when:
- I am the only user of the app
- The app is not image-centric or hugely dependent on images
If you app is not for either of those two situations, I don’t recommend this approach. Look into storing images with Rails ActiveStorage and Dropbox instead.
Add a url string to your model
rails g migration add_image_url_to_posts image_url:string
You’ll get a migration that looks like this:
class AddImageUrlToPosts < ActiveRecord::Migration[6.0]
def change
add_column :posts, :image_url, :string
end
end
Run the migration
rake db:migrate
Add the new image_url
attribute to your whitelisted params in your posts_controller
:
# app/controllers/posts_controller.rb
def post_params
params.require(:post).permit(:body, :title, :image_url)
end
Add the new field to your existing form:
<!-- app/views/posts/_form.html.erb -->
<div class="field">
<%= form.label 'Image URL' %>
<%= form.text_field :image_url %>
</div>
Make a dedicated dropbox folder
In my dropbox account, I set up a folder called app_storage/my_app_name
. When I have a new photo that I need to render for a Post
object, I upload it to this folder via the dropbox website, then right click it to get the dropbox link. I paste this url into my form field.
Reformat the URL so it will render images
You can’t render the image directly from that URL that you copied from dropbox.
# the url you copy from dropbox -- but won't render the image
https://www.dropbox.com/s/sample/sample.png?dl=0
# the url you need in order to render the image
https://dl.dropboxusercontent.com/s/sample/sample.png
In order to see an image, you need to modify it a little bit. I do this in a before_save
action in my Post
model:
# app/models/post.rb
class Post < ApplicationRecord
before_save :format_image_url
def format_image_url
self.image_url = image_url.present? ? DropboxService.format_url(self.image_url) : ''
end
end
Add tests for this method:
# spec/models/post_spec.rb
describe 'self.format_image_url' do
# NOTE: method runs before_save
it 'safely handles nils' do
post = build(:post, image_url: nil)
post.save
expect(post.image_url).to eq('')
end
it 'formats the url' do
post = build(:post, image_url: 'some_url')
allow(DropboxService).to receive(:format_url).and_return('fixed_url')
post.save
expect(post.image_url).to eq('fixed_url')
end
end
Create the DropboxService
object that does the formatting.
# app/services/dropbox_service.rb
class DropboxService
DOMAIN = 'www.dropbox.com'
READABLE_DOMAIN = 'dl.dropboxusercontent.com'
UNNECESSARY_PARAMS = '?dl=0'
def self.format_url(url)
# converts this: https://www.dropbox.com/s/sample/sample.png?dl=0
# to this: https://dl.dropboxusercontent.com/s/sample/sample.png
url.gsub(UNNECESSARY_PARAMS, '').gsub(DOMAIN, READABLE_DOMAIN)
end
end
Write spec for DropboxService
object
# spec/services/dropbox_service_spec.rb
require 'rails_helper'
RSpec.describe DropboxService, type: :model do
describe 'self.format_url' do
let(:raw_url) { 'https://www.dropbox.com/s/sample/sample.png?dl=0' }
let(:processed_url) { 'https://dl.dropboxusercontent.com/s/sample/sample.png' }
it 'removes unnecessary cruft from end of url' do
output = DropboxService.format_url(raw_url)
expect(output).to_not include(DropboxService::UNNECESSARY_PARAMS)
end
it 'switches out dropbox domain for usable one' do
output = DropboxService.format_url(raw_url)
expect(output).to_not include(DropboxService::DOMAIN)
expect(output).to include(DropboxService::READABLE_DOMAIN)
expect(output).to eq(processed_url)
end
it 'leaves non-dropbox urls as-is' do
raw_url = 'https://someotherdomain.com/sample1.png'
output = DropboxService.format_url(raw_url)
expect(output).to eq(raw_url)
end
end
end
Render the image in a view
<!-- app/views/posts/show.html.erb -->
<%= image_tag(post.image_url, class: 'image', title: post.date) if post.image_url.present? %>