class Google::Apis::Core::StorageDownloadCommand

Streaming/resumable media download support specifically for storage API so that we can respond with response headers too.

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. Note: This method is overriden from DownloadCommand in order to respond back with http header. All changes made to ‘execute_once` of DownloadCommand, should be made here too.

@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/storage_download.rb, line 40
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)
  # In case of file download in storage, we need to respond back with http
  # header along with the actual object.
  success([result, http_res], &block)
rescue => e
  @download_io.flush if @download_io.respond_to?(:flush)
  error(e, rethrow: true, &block)
end