Created
October 29, 2008 13:54
-
-
Save bschwartz/20696 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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