Escaping Filename Spaces in Paperclip Attachment URLs

Escaping Filename Spaces in Paperclip Attachment URLs

October 14, 2011   ·   By Sheldon Conaty

As I've mentioned before we generally use Paperclip when we need to support file uploads. However, there are a couple of edge cases which Paperclip, out of the box, doesn't solve. One of these is the correct URL escaping of uploaded filenames (at least as of version 2.3.15).

The Problem

Imagine you have a model with a Paperclip "picture" attachment...

has_attached_file :picture, :styles => { :thumbnail => "300x200>" },
  :storage => :s3, :s3_credentials => "#{Rails.root}/config/s3.yml",
  :default_url => "/images/default_picture_:style.png",
  :path => "pictures/person/:id/:style_:filename"

This would be accessed in your view using...

@person.picture.url(:thumbnail)

All looks good. But Paperclip won't URL escape the URL it generates for this attachment. The first time people generally notice this is when the uploaded filename contains spaces. If you upload a file with a name like "covent garden.png", Paperclip will generate the invalid URL http://s3.amazonaws.com/bucket/pictures/person/141/thumb_convent garden.png

Drat that pesky space.

The Solution

There are two ways to solve this problem

Option 1: Rename the File on Upload

This option is generally the most popular. It involves writing a PaperClip before_post_process filter which renames the file after it has been uploaded. This new name can be URL safe. So even though the subsequent URLs Paperclip generates are not escaped it isn't a problem.

There are several articles on this mechanism already so I won't go into the implementation details here.

I've found there are several problems with this mechanism. The most obvious is that it needs to be in place before any images are uploaded.

Option 2: URL Escape the Filename When URL is Generated

My preferred option is to write a new Paperclip interpolation.

Create a new file in your ruby project..

config/initializers/paperclip_interpolates.rb

With the following content...

# Paperclip by default does not escape the filenames. This interpolated
# variables lets you access the URL escaped version of the filename.
# This should be used in the :url option of has_attached_file.
# For example...
#
# has_attached_file :picture, :styles => { :thumbnail => "300x200>" },
#   :path => "pictures/person/:id/:style_:filename",
#   :url => '/pictures/person/:id/:style_:escaped_filename'
#
# Note, this solution is not suitable for S3 based attachments.
#
Paperclip.interpolates('escaped_filename') do |attachment, style|
  "#{CGI.escape(basename(attachment, style))}.#{CGI.escape(extension(attachment, style))}"
end

# To escape the S3 URL takes a little more work. In this scenerio the url
# option should be...
#
# has_attached_file :picture, :styles => { :thumbnail => "300x200>" },
#   :storage => :s3, :s3_credentials => "#{Rails.root}/config/s3.yml",
#   :default_url => "/images/default_picture_:style.png",
#   :url => ':s3_escaped_path_url',
#   :path => "pictures/person/:id/:style_:filename"
#
# Note, paperclip's S3 storage component will ignore url options which don't start with ':s3'
# and end with 'url'.
#
Paperclip.interpolates('s3_escaped_path_url') do |attachment, style|
  original = "#{basename(attachment, style)}.#{extension(attachment, style)}"
  escaped = "#{CGI.escape(basename(attachment, style))}.#{CGI.escape(extension(attachment, style))}"

  "http://s3.amazonaws.com/#{attachment.bucket_name}/#{attachment.path(style).gsub(original, escaped).gsub(%r{^/}, "")}"
end

Now update the model's attachment to have an additional :url option which uses our new s3_escaped_path_url interpolation...

has_attached_file :picture, :styles => { :thumbnail => "300x200>" },
  :storage => :s3, :s3_credentials => "#{Rails.root}/config/s3.yml",
  :default_url => "/images/default_picture_:style.png",
  :url => ':s3_escaped_path_url',
  :path => "pictures/person/:id/:style_:filename"

That's all the changes you need to make. No file renaming, now all the URLs for that attachment are generated with the correct escaping.

 

Post Comments

blog comments powered by Disqus

© Copyright 2009-2012 Peer Assembly | All Rights Reserved.