Ruby on Rails, programming, and SEO
Check out my articles about Ruby on Rails, programming, and SEO. Weekly updates? Subscribe to my feed.

Using Paperclip and jpegcam to get pictures from your webcam into Rails

Example application to make your webcam work with Ruby on Rails.

Update: This post is written for Rails 2.3.x. See Rails 3 version of this post.

Here’s how to get pictures from your webcam into Rails using the Paperclip plugin and jpegcam.

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 webcam_app
$ cd webcam_app

Create the Photo model:

$ script/generate model Photo description:string
$ rake db:migrate

Create the Photos controller:

$ script/generate controller Photos

Edit config/routes.rb to contain a photos resource:

map.resources :photos, :only => [:index, :show, :new, :create], :new => { :upload => :post }

Download jpegcam and put webcam.js in your public/javascripts folder and webcam.swf and shutter.mp3 in your public folder.

Create the layout file app/views/layouts/application.html.erb and insert the following HTML:

<html>
  <head>
    <%= javascript_include_tag :defaults %>
    <%= javascript_include_tag 'webcam' %>
  </head>
  <body>
	  <%= yield %>
  </body>
</html>

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_new_photo_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/new/upload. Try it out:

$ script/server

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 installing the plugin:

$ script/plugin install git://github.com/thoughtbot/paperclip.git

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:

$ script/generate 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 script/server if it isn’t already, go to http://localhost:3000/photos – aaannnd.. it works!

Related posts

Tags: , , ,

16 comments

  1. Josh M says:

    Hi there,

    Thanks for this blog post, it’s been helpful to me for a slightly different use case that involved uploading binary data from Flex to Rails.

    One problem I still have is that Rails is complaining about a missing authenticity token. In other instances, I’ve worked around this by supplying the token using POST params .. assuming this is not possible sending the data as a raw POST – is that right?

    Cheers
    Josh

  2. lassebunk says:

    Hi Josh,

    Strangely enough I didn’t have this problem.

    You could try the following in the controller containing your upload action:

    skip_before_filter :verify_authenticity_token, :only => :your_upload_action

    BR, Lasse

  3. vipul says:

    i hv captured the image and it is saved in uploades folder but i m not able to view it…….wat may be the problem

  4. jerry says:

    I followed the tutorial through and through yet I can’t seem to get the image uploaded. I’m getting this error when I go to photo/show:

    NoMethodError in Photo#show

    Showing app/views/photo/show.html.erb where line #3 raised:

    undefined method `image’ for nil:NilClass

    Extracted source (around line #3):

    1: Photo
    2:
    3:
    4:
    5:

    I also couldn’t get any image to upload into the uploads folder. Am I making some sort of fundamental rails-noob mistake? I’m running windows xp, rails 2.3.8, ruby 1.8.7. I got the paperclip.git from the github website as a zip file and had to install it manually just by adding it into my plugins folder. I did this because I couldn’t install it from the command line prompt. I kept getting the error “plugin not recognized as internal or external command.” But anyway, I assume my manual install worked because I was able to generate the paperclip and all the columns migrated just fine. The appropriate rake tasks also appear in the rake dropdown so I don’t think that’s the problem. Any help would be really appreciated. I really wanna get this working so I can build off of it. Please let me know if anyone out there has any ideas.

  5. jerry says:

    Sorry, instead of ()

    (h1)Photo(/h1)
    (p)
    (%= image_tag @photo.image.url(:medium) %)
    (%= link_to “Take a new picture”, new_photo_path %)
    (/p)

  6. Blake Fischer says:

    jerry and vipul,

    In your image upload method, change File.open(…path…, ‘w’) to File.open(…path…, ‘wb’).

    -Blake

  7. Jerry says:

    Thanks for the response Blake. I haven’t gotten the chance to try this yet. I’m currently working on a different part of the project. I’ll post back when I try it out. Thanks again.

  8. Fernando says:

    Hi There,

    What if the computer doesn’t have a webcam? How can I detect it, because I’d like to hide all things related to taking pictures …

  9. makaroni4 says:

    Hello there)

    Could you please help me with a little problem? The thing is, that I can`t open the uploaded image. Btw I`m using Ruby 2.3.5

    The code in the view is

    webcam.set_swf_url(‘/webcam.swf’);
    webcam.set_api_url(”);
    webcam.set_quality(90);
    webcam.set_shutter_sound(true, ‘/shutter.mp3′);
    document.getElementById(‘webcam’).innerHTML = webcam.get_html(320, 240);

    and in the controller I wrote:

    def upload
    File.open(upload_path, ‘w’) do |f|
    #f.write request.headers
    #f.write Net::HTTP.get_response(URI.parse(“php://input”)).body
    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

    After I execute webcam.snap(); function the image appears in the “uploads” folder and its size is 25Kb (seems like photo) but it couldn`t be opened in any photo viewer, browser and so on.

    I tried very hard to fix it, but it seems like only you can help me))

  10. makaroni4 says:

    Ooops)) Seems like I copied Ruby code

    *webcam.set_api_url(‘/photos/new/upload’);

  11. lassebunk says:

    @makaroni4: Maybe you could try opening the file in a text editor to see which format it is?

  12. makaroni4 says:

    I opened uploaded image in SciTE and compared it to several others *.jpeg files – it starts and ends with same chars and it seems like the uploaded file is *.jpeg but it is broken and it is impossible to view it.

  13. lassebunk says:

    Sounds like something’s wrong with jpegcam then… Maybe you could try your version of jpegcam with another backend solution?

  14. makaroni4 says:

    I`ve tried php version and it works great (I found file test.php in jpegcam archive). I`ve tried older version of jpegcam and it still doesn`t work with rails.

    What version of rails do you use? Mine is 2.3.5, may be this is a problem?

  15. [...] Here’s how to get pictures from your webcam into Rails using the Paperclip plugin and jpegcam. It is an update of my previous post: Using Paperclip and jpegcam to get pictures from your webcam into Rails. [...]

  16. lassebunk says:

    I have rewritten this post for Rails 3:
    http://lassebunk.dk/2011/02/19/paperclip-jpegcam-webcam-rails3/

    @makaroni4: Please try the new version – it is using the latest version of Rails and latest version of jpegcam.

Leave a comment

 
Fork me on GitHub