Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chunked downloads #8

Open
TrevorHinesley opened this issue Dec 6, 2016 · 5 comments
Open

Chunked downloads #8

TrevorHinesley opened this issue Dec 6, 2016 · 5 comments

Comments

@TrevorHinesley
Copy link

For getting large files from a user's Dropbox, their API allows you to specify a range of bytes--is this possible in the current dropbox_api gem's DSL?

@TrevorHinesley TrevorHinesley changed the title Dropbox chunked downloads Chunked downloads Dec 6, 2016
@Jesus
Copy link
Owner

Jesus commented Dec 11, 2016

Sorry for the late reply.

I couldn't find the option to download a specific range of bytes anywhere in their API documentation. Could you link to any resource related to this?

If this is actually possible I'd be interested in implementing this feature.

@TrevorHinesley
Copy link
Author

https://www.dropbox.com/developers-v1/core/docs#files-GET Look at the Notes section

@Jesus
Copy link
Owner

Jesus commented Dec 12, 2016

That link is for Dropbox API v1, this library implements API v2.

However, I could find this in the docs from v2:

As with content-upload endpoints, arguments are passed in the Dropbox-API-Arg request header or arg URL parameter. The response body contains file content, so the result will appear as JSON in the Dropbox-API-Result response header. These endpoints are also on the content.dropboxapi.com domain.
These endpoints also support HTTP GET along with ETag-based caching (If-None-Match) and HTTP range requests.

Source: https://www.dropbox.com/developers/documentation/http/documentation#formats

So, you're right and it should be possible to download a range of bytes. I'm not sure yet about the details on how this is meant to work.

I'll see if we can have support for this feature soon.

@apurvis
Copy link

apurvis commented Apr 11, 2018

@gbt5
Copy link

gbt5 commented Jul 22, 2018

Here's my monkeypatch:

module DropboxApi::Endpoints
  class Base
    def process_response(raw_response)
      # Official Dropbox documentation for HTTP error codes:
      # https://www.dropbox.com/developers/documentation/http/documentation#error-handling
      case raw_response.status
      when 200, 206, 409
        # Status code 409 is "Endpoint-specific error". We need to look at
        # the response body to build an exception.
        build_result(raw_response.env[:api_result])
      when 429
        error = DropboxApi::Errors::TooManyRequestsError.build(
          raw_response.env[:api_result]["error_summary"],
          raw_response.env[:api_result]["error"]["reason"]
        )

        error.retry_after = raw_response.headers["retry-after"].to_i

        raise error
      else
        raise DropboxApi::Errors::HttpError,
          "HTTP #{raw_response.status}: #{raw_response.body}"
      end
    end
  end

  class ContentChunkedDownload < DropboxApi::Endpoints::Base
    def initialize(builder)
      @connection = builder.build("https://content.dropboxapi.com") do |c|
        c.response :decode_result
      end
    end

    def build_request(params)
      body = nil

      range_from = params.delete(:range_from)
      range_to   = params.delete(:range_to)
      range = "bytes=#{range_from}-#{range_to}" unless range_from.nil? || range_from.nil?

      headers = {
        'Dropbox-API-Arg' => JSON.dump(params),
        'Content-Type' => ''
      }

      headers.merge!({ 'Range' => range }) unless range.nil?

      return body, headers
    end

    def perform_request(params)
      response = get_response(params)
      api_result = process_response response

      # TODO: Stream response, current implementation will fail with very large
      #       files.
      yield response.body if block_given?

      api_result
    end
  end
end

module DropboxApi::Endpoints::Files
  class DownloadChunked < DropboxApi::Endpoints::ContentChunkedDownload
    Method      = :post
    Path        = "/2/files/download".freeze
    ResultType  = DropboxApi::Metadata::File
    ErrorType   = DropboxApi::Errors::DownloadError

    # Download a file from a users Dropbox.
    #
    # @param path [String] The path of the file to download.
    add_endpoint :download_chunk do |path, from, to, &block|
      perform_request({:path => path}.merge(range_from: from, range_to: to), &block)
    end
  end
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants