No longer buffer chunks/files when using resumable upload
This commit is contained in:
parent
e27109078a
commit
903e568a65
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue