Value driven web development

Paperclip has some good features over attachment_fu that "attached files don’t need to have a seperare model (thank god). Your attachments are treated just like any other atribute. Images aren’t saved until your model is saved" ( by Jim Neath ).

The fall back of paperclip is that it tries to create a thumbnail for any type of file, including pdf. It won't cause much problem if it cannot create a thumbnail to a certain file. But when it comes to pdf file, paperclip tries to generate a thumbnail for every page of the file, it becomes very slow when uploading the pdf file if the file has hundreds of pages. Sometimes it even times out! At the same time, attachment_fu does not have this problem.

attachment_fu.rb includes a class method #images? to distinguish if the file is an image.

1
2
3
4
129   # Returns true or false if the given content type is recognized as an image.
130       def image?(content_type)
131         content_types.include?(content_type)
132       end

Content types of images are initialized at the begin of the file:


5  @@content_types = ['image/jpeg', 'image/pjpeg', 'image/gif', 'image/png', 'image/x-png', 'image/jpg']

Then the #image? method is called in a instantial method #thumbnailable:

1
2
3
4
5
6
7
8
9
10
216       # Checks whether the attachment's content type is an image content type
217       def image?
218         self.class.image?(content_type)
219       end
220
221       # Returns true/false if an attachment is thumbnailable.  
            # A thumbnailable attachment has an image content type and the parent_id attribute.
222       def thumbnailable?
223         image? && respond_to?(:parent_id) && parent_id.nil?
224       end

In the #create_or_update_thumbnail it test if the content is thumbnailable? to determine going or not.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
243   # Creates or updates the thumbnail for the current attachment.
244       def create_or_update_thumbnail(temp_file, file_name_suffix, *size)
245         thumbnailable? || raise(ThumbnailError.new("Can't create a thumbnail if the content \
 type is not an image or there is no parent_id column"))
246         returning find_or_initialize_thumbnail(file_name_suffix) do |thumb|
247           thumb.attributes = {
248             :content_type             => content_type,
249             :filename                 => thumbnail_name_for(file_name_suffix),
250             :temp_path                => temp_file,
251             :thumbnail_resize_options => size
252           }
253           callback_with_args :before_thumbnail_saved, thumb
254           thumb.save!
255         end
256       end

Paperclip has a similar structure like this, it makes it easier to make this patch.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
From line #217
    def post_process #:nodoc:
      return if @queued_for_write[:original].nil?
      logger.info("[paperclip] Post-processing #{name}")
      @styles.each do |name, args|
        begin
          dimensions, format = args
          dimensions = dimensions.call(instance) if dimensions.respond_to? :call
          @queued_for_write[name] = Thumbnail.make(@queued_for_write[:original],
                                                   dimensions,
                                                   format,
                                                   @whiny_thumnails)
        rescue PaperclipError => e
          @errors << e.message if @whiny_thumbnails
        end
      end
    end

The trick here is to add the #thumbnailable? method in right after the #begin keyword and raise a PaperclipError if it fails:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
213     def post_process #:nodoc:
214       return if @queued_for_write[:original].nil?
215       @styles.each do |name, args|
216         begin
                # Test here
217           thumbnailable? || raise(PaperclipError.new("Can not create thumbnails \
 if the content type is not an image."))
218           dimensions, format = args
219           dimensions = dimensions.call(instance) if dimensions.respond_to? :call
220           @queued_for_write[name] = Thumbnail.make(@queued_for_write[:original],-
221                                                    dimensions,
222                                                    format,-
223                                                    @whiny_thumnails)
224         rescue PaperclipError => e
225           @errors << e.message if @whiny_thumbnails
226         end
227       end
228     end

Add the #image? and #thumbnailable? method to attachment.rb of paperclip, and initialize the thumbnailable content types at the beginning. That's all, now you can upload pdf files very fast.

About how to use paperclip, Jim Neath has a great tutorial Paperclip: Attaching Files in Rails. Enjoy it!

1 Response to “Patching paperclip to create thumbnails only for images”

  1. dave Says:
    paperclip is awesome! I have write a crop patch for it.

Sorry, comments are closed for this article.