GridFS with Sinatra

March 13th 2011

One of the biggest problems that everyone creating a database-backed web application is where to store uploaded files. Most people settle on having files stored in the filesystem with a table of paths pointing to where the files are stored. But I bet everyone who has had to code up something like that has wished there was a good way to store files in their database along with all their other data.

Well, with MongoDB there is! It's called GridFS and it allows files of virtually any size to be stored inside the database. Using ruby it's really easy to save files:

# Saving string data
id = @grid.put("here's some string / binary data")

# Saving IO data and including the optional filename
image = File.open("me.jpg")
id2   = @grid.put(image, :filename => "me.jpg")

And super easy to access files:

# Get the string we saved
file = @grid.get(id)

# Get the file we saved
image = @grid.get(id2)

Using MongoMapper

John Nunemaker (the author of MongoMapper) has written a small library called Joint which makes it really easy to store and access files.

class Asset
  include MongoMapper::Document
  plugin Joint # add the plugin

  attachment :file
end

Which we can easily integrate with Sinatra/Rack:

# upload an asset
post '/upload' do
  asset = Asset.create(:file => params[:file][:tempfile])
  # this changes the name so that when downloading the 'proper' name is preserved
  asset.file_name = params[:file][:filename]
  asset.save
  partial :asset, :locals => {:asset => asset}
end
# access an asset
get '/assets/:id' do |id|
  asset = Asset.where(:id => id).first()
  file = asset.file

  [200, {'Content-Type' => file.content_type}, [file.read]]
end

Then you can simply create your partial view to return json or html to be included either in a view or accessed via ajax.

comments powered by Disqus