No longer buffer chunks/files when using resumable upload

This commit is contained in:
Steven Bazyl 2013-06-18 12:15:02 -07:00
parent e27109078a
commit 903e568a65
3 changed files with 129 additions and 6 deletions

View File

@ -21,7 +21,11 @@ module Google
# @see Faraday::UploadIO # @see Faraday::UploadIO
# @example # @example
# media = Google::APIClient::UploadIO.new('mymovie.m4v', 'video/mp4') # media = Google::APIClient::UploadIO.new('mymovie.m4v', 'video/mp4')
class UploadIO < Faraday::UploadIO class UploadIO < Faraday::UploadIO
# @return [Fixnum] Size of chunks to upload. Default is nil, meaning upload the entire file in a single request
attr_accessor :chunk_size
## ##
# Get the length of the stream # Get the length of the stream
# #
@ -32,6 +36,77 @@ module Google
end end
end end
##
# Wraps an input stream and limits data to a given range
#
# @example
# chunk = Google::APIClient::RangedIO.new(io, 0, 1000)
class RangedIO
##
# Bind an input stream to a specific range.
#
# @param [IO] io
# Source input stream
# @param [Fixnum] offset
# Starting offset of the range
# @param [Fixnum] length
# Length of range
def initialize(io, offset, length)
@io = io
@offset = offset
@length = length
self.rewind
end
##
# @see IO#read
def read(amount = nil, buf = nil)
buffer = buf || ''
if amount.nil?
size = @length - @pos
done = ''
elsif amount == 0
size = 0
done = ''
else
size = [@length - @pos, amount].min
done = nil
end
if size > 0
result = @io.read(size)
result.force_encoding("BINARY") if result.respond_to?(:force_encoding)
buffer << result if result
@pos = @pos + size
end
if buffer.length > 0
buffer
else
done
end
end
##
# @see IO#rewind
def rewind
self.pos = 0
end
##
# @see IO#pos
def pos
@pos
end
##
# @see IO#pos=
def pos=(pos)
@pos = pos
@io.pos = @offset + pos
end
end
## ##
# Resumable uploader. # Resumable uploader.
# #
@ -124,11 +199,11 @@ module Google
'Content-Range' => "bytes */#{media.length}" }) 'Content-Range' => "bytes */#{media.length}" })
else else
start_offset = @offset start_offset = @offset
self.media.io.pos = start_offset remaining = self.media.length - start_offset
chunk = self.media.io.read(chunk_size) chunk_size = self.media.chunk_size || self.chunk_size || self.media.length
content_length = chunk.bytesize content_length = [remaining, chunk_size].min
chunk = RangedIO.new(self.media.io, start_offset, content_length)
end_offset = start_offset + content_length - 1 end_offset = start_offset + content_length - 1
self.headers.update({ self.headers.update({
'Content-Length' => "#{content_length}", 'Content-Length' => "#{content_length}",
'Content-Type' => self.media.content_type, 'Content-Type' => self.media.content_type,

View File

@ -165,7 +165,7 @@ module Google
# Resumamble slightly different than other upload protocols in that it requires at least # Resumamble slightly different than other upload protocols in that it requires at least
# 2 requests. # 2 requests.
if result.status == 200 && self.upload_type == 'resumable' if result.status == 200 && self.upload_type == 'resumable'
upload = result.resumable_upload upload = result.resumable_upload
unless upload.complete? unless upload.complete?
logger.debug { "#{self.class} Sending upload body" } logger.debug { "#{self.class} Sending upload body" }
result = upload.send(connection) result = upload.send(connection)

View File

@ -57,6 +57,54 @@ describe Google::APIClient::UploadIO do
end end
end end
describe Google::APIClient::RangedIO do
before do
@source = StringIO.new("1234567890abcdef")
@io = Google::APIClient::RangedIO.new(@source, 1, 5)
end
it 'should return the correct range when read entirely' do
@io.read.should == "23456"
end
it 'should maintain position' do
@io.read(1).should == '2'
@io.read(2).should == '34'
@io.read(2).should == '56'
end
it 'should allow rewinds' do
@io.read(2).should == '23'
@io.rewind()
@io.read(2).should == '23'
end
it 'should allow setting position' do
@io.pos = 3
@io.read.should == '56'
end
it 'should not allow position to be set beyond range' do
@io.pos = 10
@io.read.should == ''
end
it 'should return empty string when read amount is zero' do
@io.read(0).should == ''
end
it 'should return empty string at EOF if amount is nil' do
@io.read
@io.read.should == ''
end
it 'should return nil at EOF if amount is positive int' do
@io.read
@io.read(1).should == nil
end
end
describe Google::APIClient::ResumableUpload do describe Google::APIClient::ResumableUpload do
CLIENT = Google::APIClient.new(:application_name => 'API Client Tests') unless defined?(CLIENT) CLIENT = Google::APIClient.new(:application_name => 'API Client Tests') unless defined?(CLIENT)