Skip to content

Instantly share code, notes, and snippets.

@bschwartz
Created October 29, 2008 13:54
Show Gist options
  • Save bschwartz/20696 to your computer and use it in GitHub Desktop.
Save bschwartz/20696 to your computer and use it in GitHub Desktop.
# If you've ever worked with file uploads in Rails, you've probably realized that Rails
# handles uploads with a filesize under 10K differently than files over 10K.
# I prefer things to be as similar as possible so I wrote this quick monkey patch.
# Drop it in your config/initializers directory and you're off to the races.
#
# You should also check out Paperclip, acts_as_attachment, file_column, etc. because
# they take care of all this business for you.
# Monkey patch so we don't ever have to deal with small file uploads being represented as
# UploadedStringIO objects.
# Only UploadedTempfile's from here on out, baby!
module ActionController
class AbstractRequest
class << self
UPLOADED_STRING_IO_MAX_SIZE = -1 # never user UploadedStringIO, only UploadedTempfile
#UPLOADED_STRING_IO_MAX_SIZE = 10240 # the previous value
def read_multipart(body, boundary, content_length, env)
params = Hash.new([])
boundary = "--" + boundary
quoted_boundary = Regexp.quote(boundary)
buf = ""
bufsize = 10 * 1024
boundary_end=""
# start multipart/form-data
body.binmode if defined? body.binmode
boundary_size = boundary.size + EOL.size
content_length -= boundary_size
status = body.read(boundary_size)
if nil == status
raise EOFError, "no content body"
elsif boundary + EOL != status
raise EOFError, "bad content body"
end
loop do
head = nil
content =
if UPLOADED_STRING_IO_MAX_SIZE < content_length
UploadedTempfile.new("CGI")
else
UploadedStringIO.new
end
content.binmode if defined? content.binmode
until head and /#{quoted_boundary}(?:#{EOL}|--)/n.match(buf)
if (not head) and /#{EOL}#{EOL}/n.match(buf)
buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do
head = $1.dup
""
end
next
end
if head and ( (EOL + boundary + EOL).size < buf.size )
content.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)]
buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = ""
end
c = if bufsize < content_length
body.read(bufsize)
else
body.read(content_length)
end
if c.nil? || c.empty?
raise EOFError, "bad content body"
end
buf.concat(c)
content_length -= c.size
end
buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do
content.print $1
if "--" == $2
content_length = -1
end
boundary_end = $2.dup
""
end
content.rewind
head =~ /Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;]*))/ni
if filename = $1 || $2
if /Mac/ni.match(env['HTTP_USER_AGENT']) and
/Mozilla/ni.match(env['HTTP_USER_AGENT']) and
(not /MSIE/ni.match(env['HTTP_USER_AGENT']))
filename = CGI.unescape(filename)
end
content.original_path = filename.dup
end
head =~ /Content-Type: ([^\r]*)/ni
content.content_type = $1.dup if $1
head =~ /Content-Disposition:.* name="?([^\";]*)"?/ni
name = $1.dup if $1
if params.has_key?(name)
params[name].push(content)
else
params[name] = [content]
end
break if buf.size == 0
break if content_length == -1
end
raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/
begin
body.rewind if body.respond_to?(:rewind)
rescue Errno::ESPIPE
# Handles exceptions raised by input streams that cannot be rewound
# such as when using plain CGI under Apache
end
params
end
end
end # end AbstractRequest
end # end ActionController
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment