From: Joe Van Dyk Date: 2005-08-04T13:43:46+09:00 Subject: Re: RMagick problem On 8/3/05, Joe Van Dyk wrote: > On 8/3/05, Joe Van Dyk wrote: > > On 8/3/05, Tim Hunter wrote: > > > Joe Van Dyk wrote: > > > > > > > The following Rails/Ruby code gives me this error: "Zero-length blob > > > > not permitted". > > > > > > > > @magick_image = Magick::Image.from_blob(@file.read).first > > > > > > > > Where @file is a StringIO object or a File object, depending on the > > > > size of the uploaded file. > > > > > > > > @file.read.size returns the expected file size (in bytes). > > > > > > > > Any ideas? > > > > > > Well, it works for me. Here's my attempt to reproduce the problem. > > > > > > require 'RMagick' > > > require 'stringio' > > > > > > file = File.open('Flower_Hat.jpg') > > > img = Magick::Image.from_blob(file.read).first > > > p img > > > > > > blob = IO.read('Flower_Hat.jpg') > > > sio = StringIO.new(blob) > > > img = Magick::Image.from_blob(sio.read).first > > > p img > > > > > > > > > The output is: > > > tim: ~> ruby test.rb > > > JPEG 200x250 DirectClass 8-bit 9kb > > > JPEG 200x250 DirectClass 8-bit 9kb > > > > Yeah, I tried to do that test last night and it worked fine. > > > > So I have no idea why the code is failing inside my Image model. > > Here's my image model. Perhaps that will give someone some insight. > > Say, I'm doing the Magick::Image.from_blob call in #after_save. > Perhaps something weird is happening with @file's lifetime or > something? > > class Image < ActiveRecord::Base > belongs_to :house > belongs_to :community > belongs_to :contractor > > FileLocation = "#{RAILS_ROOT}/public/media/" > DisplayLocation = "/media" > > def file=(file) > if file.size > 0 > self.content_type = file.content_type.strip > @file = file > end > end > > # Runs after Image is saved, saves the image to the file system. > def after_save > if @file > @magick_image = Magick::Image.from_blob(@file.read).first > save_full_image > save_thumbnail_image > save_rounded_thumbnail_image > save_medium_image > save_large_image > end > end > > # If image is already a jpeg, then just write it to a file. > # If it's not, convert it to a jpeg and then write. > def save_full_image > @magick_image.write(full_file) > end > > # Write a thumbnail to the filesystem > def save_thumbnail_image > thumbnailed_magick_image = resize_image_to_exact_size(180,135) > thumbnailed_magick_image.write(thumbnail_file) > end > > def save_rounded_thumbnail_image > thumbnail_image = resize_image_to_exact_size(180, 135) > color = "#000033" > corner_width = 15 > corners = Magick::Draw.new > corners.stroke(color) > corners.fill_opacity(0) > corners.stroke_width(15) > corners.roundrectangle(0, 0, > thumbnail_image.columns, thumbnail_image.rows, > corner_width, corner_width) > corners.draw(thumbnail_image) > thumbnail_image.write(rounded_thumbnail_file) > end > > # Resizes a Magick image to an exact size, keeping the same aspect ratio > def resize_image_to_exact_size(width, height) > new_aspect_ratio = width.to_f / height.to_f > old_aspect_ratio = @magick_image.columns.to_f / @magick_image.rows.to_f > > if old_aspect_ratio < new_aspect_ratio > # Image too tall, geometry string should restrict height > geometry_string = "#{width}" > else > # Image too wide or just right, geometry string should > # restrict width > geometry_string = "x#{height}" > end > > # Get the resized image > resized_image = resize_image(geometry_string) > > # Return the resized, crop image > resized_image.crop(Magick::CenterGravity, width, height) > end > > def resize_image(geometry_string) > logger.debug "resizing image to #{geometry_string}" > @magick_image.change_geometry(geometry_string) do |w, h, img| > img.resize(w, h) > end > end > > def save_medium_image > medium_image = resize_image("275") > medium_image.write(medium_file) > end > > def save_large_image > large_image = resize_image("500") > large_image.write(large_file) > end > > > > # Delete images from filesystem > def after_destroy > File.delete(full_file) if File.exist? "#{full_file}" > File.delete(thumbnail_file) if File.exist? "#{thumbnail_file}" > File.delete(rounded_thumbnail_file) if File.exist? > "#{rounded_thumbnail_file}" > File.delete(medium_file) if File.exist? "#{medium_file}" > File.delete(large_file) if File.exist? "#{large_file}" > end > > # Returns the filesystem location of the image file > def full_file > "#{FileLocation}/full/#{id}.jpg" > end > def thumbnail_file > "#{FileLocation}/thumbnail/#{id}.jpg" > end > def rounded_thumbnail_file > "#{FileLocation}/rounded_thumbnail/#{id}.jpg" > end > def medium_file > "#{FileLocation}/medium/#{id}.jpg" > end > def large_file > "#{FileLocation}/large/#{id}.jpg" > end > > > # Returns the location of the image to the browser > def full > "#{DisplayLocation}/full/#{id}.jpg" > end > def thumbnail > "#{DisplayLocation}/thumbnail/#{id}.jpg" > end > def medium > "#{DisplayLocation}/medium/#{id}.jpg" > end > def large > "#{DisplayLocation}/large/#{id}.jpg" > end > def rounded_thumbnail > "#{DisplayLocation}/rounded_thumbnail/#{id}.jpg" > end def resize > if File.exist? full_file > @file = File.open full_file > save > end > end > end > I'll be damned. Moving @magick_image = Magick::Image.from_blob(@file.read).first from Image#after_save to Image#file= fixed the problem. So, apparently, something weird happens to @file before Image#after_save is called. Any ideas?