Reputation: 6896
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
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
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