Stream data for S3 get/put object

This commit is contained in:
Wesley Beary 2009-09-09 20:44:28 -07:00
parent 09e952b665
commit 23ae9584a1
5 changed files with 47 additions and 16 deletions

View File

@ -23,7 +23,7 @@ unless Fog.mocking?
# * 'Content-Type'<~String> - MIME type of object # * 'Content-Type'<~String> - MIME type of object
# * 'ETag'<~String> - Etag of object # * 'ETag'<~String> - Etag of object
# * 'Last-Modified'<~String> - Last modified timestamp for object # * 'Last-Modified'<~String> - Last modified timestamp for object
def get_object(bucket_name, object_name, options = {}) def get_object(bucket_name, object_name, options = {}, &block)
unless bucket_name unless bucket_name
raise ArgumentError.new('bucket_name is required') raise ArgumentError.new('bucket_name is required')
end end
@ -39,7 +39,8 @@ unless Fog.mocking?
:headers => headers, :headers => headers,
:host => "#{bucket_name}.#{@host}", :host => "#{bucket_name}.#{@host}",
:method => 'GET', :method => 'GET',
:path => object_name :path => object_name,
:block => block
}) })
end end

View File

@ -9,7 +9,7 @@ unless Fog.mocking?
# ==== Parameters # ==== Parameters
# * bucket_name<~String> - Name of bucket to create object in # * bucket_name<~String> - Name of bucket to create object in
# * object_name<~String> - Name of object to create # * object_name<~String> - Name of object to create
# * object<~String> - File to create object from # * data<~File> - File or String to create object from
# * options<~Hash>: # * options<~Hash>:
# * 'Cache-Control'<~String> - Caching behaviour # * 'Cache-Control'<~String> - Caching behaviour
# * 'Content-Disposition'<~String> - Presentational information for the object # * 'Content-Disposition'<~String> - Presentational information for the object

View File

@ -87,20 +87,18 @@ module Fog
:headers => {} :headers => {}
} }
if data.respond_to?(:path) if data.is_a?(String)
metadata[:body] = data
metadata[:headers]['Content-Length'] = metadata[:body].size.to_s
else
filename = File.basename(data.path) filename = File.basename(data.path)
unless (mime_types = MIME::Types.of(filename)).empty? unless (mime_types = MIME::Types.of(filename)).empty?
metadata[:headers]['Content-Type'] = mime_types.first.content_type metadata[:headers]['Content-Type'] = mime_types.first.content_type
end end
end
if data.respond_to?(:read)
metadata[:body] = data.read metadata[:body] = data.read
else metadata[:headers]['Content-Length'] = File.size(data.path)
metadata[:body] = data
end end
metadata[:headers]['Content-Length'] = metadata[:body].size.to_s # metadata[:headers]['Content-MD5'] = Base64.encode64(Digest::MD5.digest(metadata[:body])).strip
metadata[:headers]['Content-MD5'] = Base64.encode64(Digest::MD5.digest(metadata[:body])).strip
metadata metadata
end end
@ -143,6 +141,7 @@ DATA
params[:headers]['Authorization'] = "AWS #{@aws_access_key_id}:#{signature}" params[:headers]['Authorization'] = "AWS #{@aws_access_key_id}:#{signature}"
response = @connection.request({ response = @connection.request({
:block => params[:block],
:body => params[:body], :body => params[:body],
:expects => params[:expects], :expects => params[:expects],
:headers => params[:headers], :headers => params[:headers],

View File

@ -34,15 +34,26 @@ unless Fog.mocking?
request = "#{params[:method]} #{params[:path]} HTTP/1.1\r\n" request = "#{params[:method]} #{params[:path]} HTTP/1.1\r\n"
params[:headers] ||= {} params[:headers] ||= {}
params[:headers]['Host'] = params[:host] params[:headers]['Host'] = params[:host]
if params[:body] if params[:body] && !params[:headers]['Content-Length']
params[:headers]['Content-Length'] = params[:body].length params[:headers]['Content-Length'] = params[:body].length
end end
for key, value in params[:headers] for key, value in params[:headers]
request << "#{key}: #{value}\r\n" request << "#{key}: #{value}\r\n"
end end
request << "\r\n#{params[:body]}" request << "\r\n"
@connection.write(request) @connection.write(request)
if params[:body]
if params[:body].is_a?(String)
body = StringIO.new(params[:body])
else
body = params[:body]
end
while chunk = body.read(1048576) # 1 megabyte
@connection.write(chunk)
end
end
response = Fog::Response.new response = Fog::Response.new
response.request = params response.request = params
response.status = @connection.readline[9..11].to_i response.status = @connection.readline[9..11].to_i
@ -65,19 +76,30 @@ unless Fog.mocking?
parser = params[:parser] parser = params[:parser]
end end
body = Nokogiri::XML::SAX::PushParser.new(parser) body = Nokogiri::XML::SAX::PushParser.new(parser)
elsif params[:block]
body = nil
else else
body = '' body = ''
end end
unless params[:method] == 'HEAD' unless params[:method] == 'HEAD'
if response.headers['Content-Length'] if response.headers['Content-Length']
body << @connection.read(response.headers['Content-Length'].to_i) content = @connection.read(response.headers['Content-Length'].to_i)
unless params[:block]
body << content
else
params[:block].call(content)
end
elsif response.headers['Transfer-Encoding'] == 'chunked' elsif response.headers['Transfer-Encoding'] == 'chunked'
while true while true
# 2 == "/r/n".length # 2 == "/r/n".length
chunk_size = @connection.readline.chomp!.to_i(16) + 2 chunk_size = @connection.readline.chomp!.to_i(16) + 2
chunk = @connection.read(chunk_size) chunk = @connection.read(chunk_size)[0...-2]
body << chunk[0...-2] unless params[:block]
body << chunk
else
params[:block].call(chunk)
end
if chunk_size == 2 if chunk_size == 2
break break
end end

View File

@ -26,6 +26,15 @@ describe 'S3.get_object' do
actual.headers['Last-Modified'].should be_a(String) actual.headers['Last-Modified'].should be_a(String)
end end
it 'should return chunks with optional block' do
file = File.open(File.dirname(__FILE__) + '/../../../lorem.txt', 'r')
data = ''
s3.get_object('foggetobject', 'fog_get_object') do |chunk|
data << chunk
end
data.should == file.read
end
end end
describe 'failure' do describe 'failure' do