Skip to content

Instantly share code, notes, and snippets.

@gmcinnes
Forked from technoweenie/gist:79664
Created July 7, 2009 23:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gmcinnes/142438 to your computer and use it in GitHub Desktop.
Save gmcinnes/142438 to your computer and use it in GitHub Desktop.
# http://www.mikeperham.com/2009/01/21/testing-multipart-upload-with-sinatra/
module FiveRuns
module Multipart
class FileUpload
BOUNDARY_ROOT = 'B0UND~F0R~UPL0AD'
attr_reader :file, :params, :rack_opts
def initialize(params={}, rack_opts = {})
@to_s = nil
@params = params
@key_stack = []
@name_stack = []
rack_opts[:content_type] ||= 'application/octet-stream'
rack_opts[:filename] ||= 'uploaded.file'
@rack_opts = rack_opts
end
def content_type
%(multipart/form-data, boundary="#{boundary}")
end
def to_s
@to_s ||= %(#{form}\r\n#{separator}--)
end
########
# private
########
def boundary
"#{BOUNDARY_ROOT}*#{nonce}"
end
def parts(context=params)
r = context.map do |name, value|
if value.is_a?(Hash)
@key_stack.push(name)
parts(value)
elsif value.nil?
nil
else
name = nest(name) unless @key_stack.empty?
boundarize(name, value)
end
end
@key_stack.pop
return r
end
def form
parts.flatten.compact.join(crlf)
end
def boundarize(name, value)
[separator,
headers_for(name, value)
].flatten.join(crlf) + crlf + crlf + content_of(value)
end
def nest(inner)
@name_stack = @key_stack.clone if @name_stack.empty?
name = "#{@name_stack.pop}[#{inner}]"
name = nest(name) unless @name_stack.empty?
return name
end
def separator
%(--#{boundary})
end
def crlf
@crlf ||= "\r\n"
end
def headers_for(name, value)
if value.respond_to?(:read)
[
%(Content-Disposition: form-data; name="#{name}"; filename="#{@rack_opts.delete(:filename)}"),
%(Content-Transfer-Encoding: binary),
%(Content-Type: #{@rack_opts.delete(:content_type)})
]
else
[ %(Content-Disposition: form-data; name="#{name}") ]
end
end
def nonce( time = Time.now.utc )
@nonce ||= (time.to_f * 1000).to_i
end
def content_of(value)
value.respond_to?(:read) ? value.read : value.to_s
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment