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!