goddamnyouryan
goddamnyouryan

Reputation: 6896

Rails 3.0 Image Cropping with Paperclip and s3 on Heroku

I am having an issue I have yet to see elsewhere using the image cropping from this railscast

It's on my production app, and I am only getting this exception every once and a while, and I haven't been able to reproduce it myself locally.

The error:

PhotosController# (ActionView::Template::Error) "can't convert nil into String"

/app/.bundle/gems/ruby/1.9.1/gems/paperclip-2.3.15/lib/paperclip/storage/s3.rb:163:in `extname'
/app/.bundle/gems/ruby/1.9.1/gems/paperclip-2.3.15/lib/paperclip/storage/s3.rb:163:in `to_file'
/app/app/models/photo.rb:27:in `photo_geometry'
/app/app/views/photos/show.html.erb:17:in `block in _app_views_photos_show_html_erb__1949294035370253936_41955540__272030757437175302'

photo.rb

  def cropping?  
    !crop_x.blank? && !crop_y.blank? && !crop_w.blank? && !crop_h.blank?  
  end

  def photo_geometry(style = :original)  
    @geometry ||= {}  
    @geometry[style] ||= Paperclip::Geometry.from_file(photo.to_file(style))  # line #27
  end

show.html.erb

<% content_for(:head) do %>
    <%= stylesheet_link_tag "jquery.Jcrop" %>
    <%= javascript_include_tag "jquery.Jcrop.min" %>
    <script type="text/javascript" charset="utf-8">
        $(function() {
        $('#cropbox').Jcrop({
                onChange: update_crop,  
                onSelect: update_crop,  
                setSelect: [0, 0, 90, 90],  
                aspectRatio: 1
            });
            function update_crop(coords) {
                var rx = 100/coords.w;  
                  var ry = 100/coords.h;  
                  $('#preview').css({  
                    width: Math.round(rx * <%= @photo.photo_geometry(:large).width %>) + 'px',  // line #17
                    height: Math.round(ry * <%= @photo.photo_geometry(:large).height %>) + 'px',  
                    marginLeft: '-' + Math.round(rx * coords.x) + 'px',  
                    marginTop: '-' + Math.round(ry * coords.y) + 'px'  
                  });

              var ratio = <%= @photo.photo_geometry(:original).width %> / <%= @photo.photo_geometry(:large).width %>;   
                  $('#crop_x').val(Math.floor(coords.x * ratio));  
                  $('#crop_y').val(Math.floor(coords.y * ratio));  
                  $('#crop_w').val(Math.floor(coords.w * ratio));  
                  $('#crop_h').val(Math.floor(coords.h * ratio));  
            }
        });
    </script>
<% end %>

I'm guessing the issue has something to do with paperclip being unable to grab the dimensions of the uploaded photo, but frankly I don't understand the photo.rb code very well, I just copied it directly from the railscast.

Any ideas? If someone could explain what is going on in photo.rb a bit better I would appreciate it.

Thanks!

Upvotes: 2

Views: 865

Answers (2)

Coren
Coren

Reputation: 5637

It seems to be a problem about path resolution for this style.

As @ahmeij said, your problem is on those lines of lib/paperclip/storage/s3.rb:163

filename = path(style)
extname  = File.extname(filename)

Your error message says that filename is Nil, not a String. It comes from path method, which is implemented in lib/paperclip/attachment.rb:139

def path style_name = default_style
  original_filename.nil? ? nil : interpolate(@path, style_name)
end

This indicates that if you do not have a valid _original_filename_ attribute on your photo object, it's going to be a Nil value.

=> You'll need to understand why this attribute can be invalid

Upvotes: 1

knutsel
knutsel

Reputation: 1291

Could it be that the problem has to do with files without an extension?

For instance when you allow people to give an URL for an image to use on your service and you download that image for conversion and storage on S3 it could be the download does not have an extension, while it does have a content type and therefore is displayed and processed correctly as an image.

The problem occurs here: (See comments)

 def to_file style = default_style
    return @queued_for_write[style] if @queued_for_write[style]
    filename = path(style)
    extname  = File.extname(filename)           # Likely the Nil is returned here
    basename = File.basename(filename, extname)
    file = Tempfile.new([basename, extname])    # Ext name is used here
    file.binmode
    file.write(AWS::S3::S3Object.value(path(style), bucket_name))
    file.rewind
    return file
  end

You can try the following monkey patch if the extension indeed causes the Nil:

module Paperclip
  module Storage
    module S3
      def to_file style = default_style
        return @queued_for_write[style] if @queued_for_write[style]
        filename = path(style)
        extname  = File.extname(filename) || ""          # <==== Changed
        basename = File.basename(filename, extname)
        file = Tempfile.new([basename, extname])   
        file.binmode
        file.write(AWS::S3::S3Object.value(path(style), bucket_name))
        file.rewind
        return file
      end
    end
  end
end

Upvotes: 2

Related Questions