class Google::Apis::Core::DownloadCommand

Streaming/resumable media download support

Constants

OK_STATUS
RANGE_HEADER

Attributes

download_dest[RW]

File or IO to write content to @return [String, File, write]

Public Instance Methods

execute_once(client, &block) click to toggle source

Execute the upload request once. Overrides the default implementation to handle streaming/chunking of file content.

@private @param [HTTPClient] client

HTTP client

@yield [result, err] Result or error if block supplied @return [Object] @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification @raise [Google::Apis::AuthorizationError] Authorization is required

# File lib/google/apis/core/download.rb, line 67
def execute_once(client, &block)
  request_header = header.dup
  apply_request_options(request_header)
  download_offset = nil

  if @offset > 0
    logger.debug { sprintf('Resuming download from offset %d', @offset) }
    request_header[RANGE_HEADER] = sprintf('bytes=%d-', @offset)
  end

  http_res = client.get(url.to_s,
             query: query,
             header: request_header,
             follow_redirect: true) do |res, chunk|
    status = res.http_header.status_code.to_i
    next unless OK_STATUS.include?(status)

    download_offset ||= (status == 206 ? @offset : 0)
    download_offset  += chunk.bytesize

    if download_offset - chunk.bytesize == @offset
      next_chunk = chunk
    else
      # Oh no! Requested a chunk, but received the entire content
      chunk_index = @offset - (download_offset - chunk.bytesize)
      next_chunk = chunk.byteslice(chunk_index..-1)
      next if next_chunk.nil?
    end

    # logger.debug { sprintf('Writing chunk (%d bytes, %d total)', chunk.length, bytes_read) }
    @download_io.write(next_chunk)

    @offset += next_chunk.bytesize
  end

 @download_io.flush if @download_io.respond_to?(:flush)

  if @close_io_on_finish
    result = nil
  else
    result = @download_io
  end
  check_status(http_res.status.to_i, http_res.header, http_res.body)
  success(result, &block)
rescue => e
  @download_io.flush if @download_io.respond_to?(:flush)
  error(e, rethrow: true, &block)
end
prepare!() click to toggle source

Ensure the download destination is a writable stream.

@return [void]

Calls superclass method Google::Apis::Core::ApiCommand#prepare!
# File lib/google/apis/core/download.rb, line 34
def prepare!
  @state = :start
  @download_url = nil
  @offset = 0
  if download_dest.respond_to?(:write)
    @download_io = download_dest
    @close_io_on_finish = false
  elsif download_dest.is_a?(String)
    @download_io = File.open(download_dest, 'wb')
    @close_io_on_finish = true
  else
    @download_io = StringIO.new('', 'wb')
    @close_io_on_finish = false
  end
  super
end
release!() click to toggle source

Close IO stream when command done. Only closes the stream if it was opened by the command.

# File lib/google/apis/core/download.rb, line 52
def release!
  @download_io.close if @close_io_on_finish
end