lib/aws/s3/s3_object.rb in aws-sdk-1.9.5 vs lib/aws/s3/s3_object.rb in aws-sdk-1.10.0

- old
+ new

@@ -61,21 +61,21 @@ # # # also works this way # obj.write(:file => path_to_file) # # # Also accepts an open file object - # file = File.open(path_to_file, 'r') + # file = File.open(path_to_file, 'rb') # obj.write(file) # # All three examples above produce the same result. The file # will be streamed to S3 in chunks. It will not be loaded # entirely into memory. # # ## Streaming Uploads # - # When you call {#write} with any IO-like object (must respond to - # #read and #eof?), it will be streamed to S3 in chunks. + # When you call {#write} with an IO-like object, it will be streamed + # to S3 in chunks. # # While it is possible to determine the size of many IO objects, you may # have to specify the :content_length of your IO object. # If the exact size can not be known, you may provide an # `:estimated_content_length`. Depending on the size (actual or @@ -102,11 +102,11 @@ # ## Streaming Downloads # # If you want to stream an object from S3, you can pass a block # to {#read}. # - # File.open('output', 'w') do |file| + # File.open('output', 'wb') do |file| # large_object.read do |chunk| # file.write(chunk) # end # end # @@ -458,11 +458,11 @@ # # # files (by path) # obj.write(Pathname.new('path/to/file.txt')) # # # file objects - # obj.write(File.open('path/to/file.txt', 'r')) + # obj.write(File.open('path/to/file.txt', 'rb')) # # # IO objects (must respond to #read and #eof?) # obj.write(io) # # ### Multipart Uploads vs Single Uploads @@ -836,10 +836,13 @@ # # @option options [Boolean] :client_side_encrypted (false) Set to true # when the object being copied was client-side encrypted. This # is important so the encryption metadata will be copied. # + # @option options [Boolean] :use_multipart_copy (false) Set this to + # `true` if you need to copy an object that is larger than 5GB. + # # @option options :cache_control [String] Can be used to specify # caching behavior. See # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9 # # @option options [String] :expires The date and time at which the @@ -887,11 +890,15 @@ 'REDUCED_REDUNDANCY' : 'STANDARD' options[:bucket_name] = bucket.name options[:key] = key - client.copy_object(options) + if use_multipart_copy?(options) + multipart_copy(options) + else + resp = client.copy_object(options) + end nil end @@ -988,11 +995,11 @@ # When downloading large objects it is recommended to pass a block # to #read. Data will be yielded to the block as it is read off # the HTTP response. # # # read an object from S3 to a file - # File.open('output.txt', 'w') do |file| + # File.open('output.txt', 'wb') do |file| # bucket.objects['key'].read do |chunk| # file.write(chunk) # end # end # @@ -1171,10 +1178,12 @@ # This option defaults to one hour after the current time. # # @option options [Boolean] :secure (true) Whether to generate a # secure (HTTPS) URL or a plain HTTP url. # + # @option options [String] :content_type + # @option options [String] :content_md5 # @option options [String] :endpoint Sets the hostname of the # endpoint. # # @option options [Integer] :port Sets the port of the # endpoint (overrides config.s3_port). @@ -1206,27 +1215,27 @@ # @option options [String] :response_content_encoding Sets the # Content-Encoding header of the response when performing an # HTTP GET on the returned URL. # @return [URI::HTTP, URI::HTTPS] def url_for(method, options = {}) - + options = options.dup options[:secure] = config.use_ssl? unless options.key?(:secure) + options[:expires] = expiration_timestamp(options[:expires]) req = request_for_signing(options) - - method = http_method(method) - expires = expiration_timestamp(options[:expires]) - req.add_param("AWSAccessKeyId", - config.credential_provider.access_key_id) + req.http_method = http_method(method) + req.add_param("AWSAccessKeyId", config.credential_provider.access_key_id) req.add_param("versionId", options[:version_id]) if options[:version_id] - req.add_param("Signature", signature(method, expires, req)) - req.add_param("Expires", expires) - req.add_param("x-amz-security-token", - config.credential_provider.session_token) if - config.credential_provider.session_token + req.add_param("Signature", signature(req, options)) + req.add_param("Expires", options[:expires]) + if config.credential_provider.session_token + req.add_param( + "x-amz-security-token", + config.credential_provider.session_token + ) + end - secure = options.fetch(:secure, config.use_ssl?) build_uri(req, options) end # Generates a public (not authenticated) URL for the object. # @@ -1270,10 +1279,38 @@ value end private + # Used to determine if the data needs to be copied in parts + def use_multipart_copy? options + options[:use_multipart_copy] + end + + def multipart_copy options + + unless options[:content_length] + msg = "unknown content length, must set :content_length " + + "to use multi-part copy" + raise ArgumentError, msg + end + + part_size = compute_part_size(options) + clean_up_options(options) + source_length = options.delete(:content_length) + + multipart_upload(options) do |upload| + pos = 0 + # We copy in part_size chunks until we read the + until pos >= source_length + last_byte = (pos + part_size >= source_length) ? source_length - 1 : pos + part_size - 1 + upload.copy_part(options[:copy_source], options.merge({:copy_source_range => "bytes=#{pos}-#{last_byte}"})) + pos += part_size + end + end + end + # @return [Boolean] def should_decrypt? options options[:encryption_key] or config.s3_encryption_key end @@ -1345,26 +1382,24 @@ :port => request.port, :path => request.path, :query => request.querystring) end - def signature(method, expires, request) - + def signature request, options parts = [] - parts << method - parts << "" - parts << "" - parts << expires + parts << request.http_method + parts << options[:content_md5].to_s + parts << options[:content_type].to_s + parts << options[:expires] if token = config.credential_provider.session_token parts << "x-amz-security-token:#{token}" end parts << request.canonicalized_resource string_to_sign = parts.join("\n") secret = config.credential_provider.secret_access_key Core::Signer.sign(secret, string_to_sign, 'sha1') - end def expiration_timestamp(input) input = input.to_int if input.respond_to?(:to_int) case input