View the complete source code for this post
Here’s how to get pictures from your webcam into Rails using the Paperclip plugin and jpegcam. It is an update of the Rails 2.3.x version of this post.
What we will do is create a Photo
model and a Photos
controller with actions new
, upload
and create
to take care of the image creation.
Start by creating a new Rails application:
$ rails new webcam_app $ cd webcam_app
Create the Photo
model:
$ rails g model Photo description:string $ rake db:migrate
Create the Photos
controller:
$ rails g controller Photos
Edit config/routes.rb
to contain a photos resource:
resources :photos, :only => [:index, :show, :new, :create] do post 'upload', :on => :collection end
Download jpegcam and put webcam.js
in your public/javascripts
folder and webcam.swf
and shutter.mp3
in your public
folder.
Edit the layout file app/views/layouts/application.html.erb
and insert the following HTML inside the <head>
-tag just below the other javascript_include_tag
:
<%= javascript_include_tag 'webcam' %>
Next, create app/views/photos/new.html.erb
and make a div for the webcam contents:
<div id="webcam"></div>
And the actual webcam javascript:
<script type="text/javascript"> webcam.set_swf_url('/webcam.swf'); webcam.set_api_url('<%= upload_photos_path %>'); webcam.set_quality(90); webcam.set_shutter_sound(true, '/shutter.mp3'); $('webcam').innerHTML = webcam.get_html(640, 480); </script>
That will run the actual webcam so you can see yourself :) But for taking a picture, we’ll need to add the following button:
<input type="button" value="Take picture" onclick="webcam.snap();" />
Now when you click the button, the webcam image will be posted to /photos/upload
. Try it out:
$ rails s
and go to http://localhost:3000/photos/new.
Let’s add some code for handling the upload.
The webcam.swf
just uploads a bunch of jpeg data so we’ll need to get a hold of the raw post data. That’s done with Rails’ request.raw_post
. In your PhotosController
:
def upload File.open(upload_path, 'w') do |f| f.write request.raw_post end render :text => "ok" end private def upload_path # is used in upload and create file_name = session[:session_id].to_s + '.jpg' File.join(RAILS_ROOT, 'public', 'uploads', file_name) end
Remember to create the public/uploads
folder.
Now, back to app/views/photos/new.html.erb
. Create the following at the bottom of the view:
<% form_for Photo.new, :html => { :style => "display: none;" } do |f| %> <p> <%= f.label :description %><br /> <%= f.text_field :description %> </p> <p> <%= f.submit "Save the photo" %> or <%= link_to "Take another", new_photo_path %> </p> <% end %>
In your javascript just below the webcam div, insert the following function:
function upload_complete(msg) { if (msg == 'ok') { $('new_photo').show(); $('photo_description').focus(); } else { alert('An error occured'); webcam.reset(); } }
And add the following webcam code:
webcam.set_hook('onComplete', 'upload_complete');
Now when you take a picture you will get a new photo form for entering a description. If something goes wrong, the webcam will be reset so you can try again. This might also be a good time to check your logs.
Now we have the upload (see for yourself in your public/uploads folder) but we still need to add Paperclip to the model.
Start by adding the plugin gem to your Gemfile
:
gem 'paperclip', '~> 2.3'
Then run:
$ bundle install
to install the gem.
In the Photo
model:
class Photo < ActiveRecord::Base has_attached_file :image, :styles => { :medium => "300x300>", :thumb => "100x100>" } end
For this to work, we need to add some necessary Paperclip columns to our Photo
model:
$ rails g paperclip photo image $ rake db:migrate
Now we can show photos like this:
<%= image_tag photo.image.url(:thumb) %>
We just need to save the photos first. Add the following to your PhotosController
somewhere above the private
keyword:
def create @photo = Photo.new(params[:photo]) @photo.image = File.new(upload_path) @photo.save redirect_to @photo end def show @photo = Photo.find(params[:id]) end def index @photos = Photo.all end
In app/views/photos/show.html.erb
:
<h1>Photo</h1> <p><%= image_tag @photo.image.url(:medium) %></p> <p> <strong>Description:</strong><br /> <%=h @photo.description %> </p> <p> <%= link_to "Take a new picture", new_photo_path %> | <%= link_to "See all pictures", photos_path %> </p>
And last but not least in app/views/photos/index.html.erb
:
<h1>All photos</h1> <p> <% @photos.each do |photo| %> <%= link_to image_tag(photo.image.url(:thumb)), photo %> <% end %> </p> <p> <%= link_to "Take a new picture", new_photo_path %> </p>
Now start your rails s
if it isn’t already, go to http://localhost:3000/photos – aaannnd.. it works!