oss-ruby-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +13 -0
- data/README.md +41 -0
- data/Rakefile +9 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/oss.rb +43 -0
- data/lib/oss/api.rb +342 -0
- data/lib/oss/bucket.rb +473 -0
- data/lib/oss/client.rb +206 -0
- data/lib/oss/cors.rb +106 -0
- data/lib/oss/error.rb +20 -0
- data/lib/oss/multipart.rb +225 -0
- data/lib/oss/object.rb +318 -0
- data/lib/oss/service.rb +63 -0
- data/lib/oss/util.rb +59 -0
- data/lib/oss/version.rb +3 -0
- data/oss-ruby-sdk.gemspec +26 -0
- metadata +119 -0
    
        data/lib/oss/client.rb
    ADDED
    
    | @@ -0,0 +1,206 @@ | |
| 1 | 
            +
            require 'rest-client'
         | 
| 2 | 
            +
            require 'digest/md5'
         | 
| 3 | 
            +
            require 'openssl'
         | 
| 4 | 
            +
            require 'base64'
         | 
| 5 | 
            +
            require 'nokogiri'
         | 
| 6 | 
            +
            require 'oss/error'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            module Oss
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              class Client
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                attr_reader :endpoint, :access_key_id, :access_key_secret
         | 
| 13 | 
            +
                attr_accessor :debug_mode
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def initialize(endpoint, access_key_id, access_key_secret, debug_mode = false)
         | 
| 16 | 
            +
                  @debug_mode        = debug_mode
         | 
| 17 | 
            +
                  @endpoint          = endpoint
         | 
| 18 | 
            +
                  @access_key_id     = access_key_id
         | 
| 19 | 
            +
                  @access_key_secret = access_key_secret
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                # http put request
         | 
| 23 | 
            +
                def put(options = {})
         | 
| 24 | 
            +
                  request_prepare(:put, options)
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                # http get request
         | 
| 28 | 
            +
                def get(options = {})
         | 
| 29 | 
            +
                  request_prepare(:get, options)
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                # http post request
         | 
| 33 | 
            +
                def post(options = {})
         | 
| 34 | 
            +
                  request_prepare(:post, options)
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                # http delete request
         | 
| 38 | 
            +
                def delete(options = {})
         | 
| 39 | 
            +
                  request_prepare(:delete, options)
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                # http delete request
         | 
| 43 | 
            +
                def head(options = {})
         | 
| 44 | 
            +
                  request_prepare(:head, options)
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                # http delete request
         | 
| 48 | 
            +
                def options(options = {})
         | 
| 49 | 
            +
                  request_prepare(:options, options)
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                def request_prepare(method, options)
         | 
| 53 | 
            +
                  # set header sign
         | 
| 54 | 
            +
                  options[:sign_configs] ||= Hash.new
         | 
| 55 | 
            +
                  options[:sign_configs][:date] = gmt_date
         | 
| 56 | 
            +
                  options[:sign_configs][:host] = options[:host]
         | 
| 57 | 
            +
                  options[:sign_configs][:path] = options[:path]
         | 
| 58 | 
            +
                  options[:sign_configs][:verb] = method.to_s.upcase
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  # content md5 check if need
         | 
| 61 | 
            +
                  if options[:content_md5_check]
         | 
| 62 | 
            +
                    # payload make md digest
         | 
| 63 | 
            +
                    md5 = Digest::MD5.digest(options[:payload])
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                    # then make base64 and trim \n
         | 
| 66 | 
            +
                    cm = Base64.encode64(md5).gsub("\n", '')
         | 
| 67 | 
            +
                    options[:sign_configs][:content_md5] = cm
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                    # set to header
         | 
| 70 | 
            +
                    options[:headers] ||= Hash.new
         | 
| 71 | 
            +
                    options[:headers]['Content-MD5'] = cm
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                  # set header content length if need
         | 
| 75 | 
            +
                  if options[:content_length_check]
         | 
| 76 | 
            +
                    options[:payload] ||= ''
         | 
| 77 | 
            +
                    options[:headers]['Content-Length'] = options[:payload].bytesize
         | 
| 78 | 
            +
                  end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                  # set get path query string
         | 
| 81 | 
            +
                  path = Util.set_query_string(options[:path], options[:query_string])
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                  # add query string to sign if need
         | 
| 84 | 
            +
                  if options[:sign_configs][:sign_query_string]
         | 
| 85 | 
            +
                    options[:sign_configs][:path] = path
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                  # create sign header
         | 
| 89 | 
            +
                  signed_header = set_header_sign(options[:headers], options[:sign_configs])
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                  # send request
         | 
| 92 | 
            +
                  request(options[:host], path, signed_header, options[:as] || :xml) do |url, header|
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                    # use RestClient send request
         | 
| 95 | 
            +
                    RestClient::Request.execute(url: url, method: method, headers: header, payload: options[:payload])
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                  end
         | 
| 98 | 
            +
                end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                def request(host, path, headers, as = :xml, &block)
         | 
| 101 | 
            +
                  url = "http://#{host}#{path}"
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                  # send request and rescue errors
         | 
| 104 | 
            +
                  begin
         | 
| 105 | 
            +
                    response = yield(url, headers)
         | 
| 106 | 
            +
                  rescue RestClient::ExceptionWithResponse => err
         | 
| 107 | 
            +
                    response = err.response
         | 
| 108 | 
            +
                  end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                  p "DEBUG: response body =======>\n#{response.body}\n" if debug_mode
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                  if response.code/100 != 2
         | 
| 113 | 
            +
                    # error response
         | 
| 114 | 
            +
                    error_handler(response)
         | 
| 115 | 
            +
                  else
         | 
| 116 | 
            +
                    # success response
         | 
| 117 | 
            +
                    case as
         | 
| 118 | 
            +
                      when :xml
         | 
| 119 | 
            +
                        # parse as xml object
         | 
| 120 | 
            +
                        Nokogiri::XML(response.body)
         | 
| 121 | 
            +
                      when :raw
         | 
| 122 | 
            +
                        # return raw response
         | 
| 123 | 
            +
                        response
         | 
| 124 | 
            +
                      else
         | 
| 125 | 
            +
                        raise 'Undefined request as param!'
         | 
| 126 | 
            +
                    end
         | 
| 127 | 
            +
                  end
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                end
         | 
| 130 | 
            +
             | 
| 131 | 
            +
             | 
| 132 | 
            +
                private
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                def error_handler(response)
         | 
| 135 | 
            +
                  xml_doc  = Nokogiri::XML(response.body)
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                  # parse all error infos
         | 
| 138 | 
            +
                  error_attrs = {
         | 
| 139 | 
            +
                      http_status: response.code,
         | 
| 140 | 
            +
                      code:        xml_doc.xpath('Error/Code').inner_text,
         | 
| 141 | 
            +
                      message:     xml_doc.xpath('Error/Message').inner_text,
         | 
| 142 | 
            +
                      header:      xml_doc.xpath('Error/Header').inner_text,
         | 
| 143 | 
            +
                      request_id:  xml_doc.xpath('Error/RequestId').inner_text,
         | 
| 144 | 
            +
                      host_id:     xml_doc.xpath('Error/HostId').inner_text
         | 
| 145 | 
            +
                  }
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                  # error raise format
         | 
| 148 | 
            +
                  status     = "HttpStatus: #{error_attrs[:http_status]}\n"
         | 
| 149 | 
            +
                  code       = "Code: #{error_attrs[:code]}\n"
         | 
| 150 | 
            +
                  message    = "Message: #{error_attrs[:message]}\n"
         | 
| 151 | 
            +
                  header     = error_attrs[:header] == '' ? '' : "Header: #{error_attrs[:header]}\n"
         | 
| 152 | 
            +
                  request_id = "RequestId: #{error_attrs[:request_id]}\n"
         | 
| 153 | 
            +
                  host       = "HostId: #{error_attrs[:host_id]}\n"
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                  error_info = "\n#{status}#{code}#{message}#{header}#{request_id}#{host}\n"
         | 
| 156 | 
            +
             | 
| 157 | 
            +
                  # raise error
         | 
| 158 | 
            +
                  raise ResponseError.new(error_attrs, error_info)
         | 
| 159 | 
            +
                end
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                def set_header_sign(origin_header, configs = {})
         | 
| 162 | 
            +
                  # new hash if not exist
         | 
| 163 | 
            +
                  origin_header = Hash.new if origin_header.nil?
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                  # set authorization signature header
         | 
| 166 | 
            +
                  origin_header['Authorization'] = signature(
         | 
| 167 | 
            +
                      configs[:verb],
         | 
| 168 | 
            +
                      configs[:date],
         | 
| 169 | 
            +
                      "#{configs[:resource]}#{configs[:path]}",
         | 
| 170 | 
            +
                      configs[:content_md5],
         | 
| 171 | 
            +
                      configs[:content_type],
         | 
| 172 | 
            +
                      configs[:oss_headers]
         | 
| 173 | 
            +
                  )
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                  # set server headers
         | 
| 176 | 
            +
                  origin_header['Host'] = configs[:host]
         | 
| 177 | 
            +
                  origin_header['Date'] = configs[:date]
         | 
| 178 | 
            +
                  origin_header
         | 
| 179 | 
            +
                end
         | 
| 180 | 
            +
             | 
| 181 | 
            +
                def signature(verb, date, resource = '', content_md5 = '', content_type = '', oss_headers = '')
         | 
| 182 | 
            +
                  # set oss headers like x-oss-something into sign
         | 
| 183 | 
            +
                  oss_headers << "\n" if oss_headers != nil and oss_headers != ''
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                  # string ready to sign
         | 
| 186 | 
            +
                  string_to_sign = "#{verb}\n#{content_md5}\n#{content_type}\n#{date}\n#{oss_headers}#{resource}"
         | 
| 187 | 
            +
             | 
| 188 | 
            +
                  p "DEBUG: client sign string =======>\n#{string_to_sign}\n" if debug_mode
         | 
| 189 | 
            +
             | 
| 190 | 
            +
                  # use hmac-sha1 then base64
         | 
| 191 | 
            +
                  digest = OpenSSL::Digest.new('sha1')
         | 
| 192 | 
            +
                  h = OpenSSL::HMAC.digest(digest, access_key_secret, string_to_sign)
         | 
| 193 | 
            +
                  b = Base64.encode64(h)
         | 
| 194 | 
            +
             | 
| 195 | 
            +
                  # final signature Authorization value
         | 
| 196 | 
            +
                  "OSS #{access_key_id}:#{b.gsub("\n", '')}"
         | 
| 197 | 
            +
                end
         | 
| 198 | 
            +
             | 
| 199 | 
            +
                # create GMT date format
         | 
| 200 | 
            +
                def gmt_date
         | 
| 201 | 
            +
                  Time.now.gmtime.strftime("%a, %d %b %Y %H:%M:%S GMT")
         | 
| 202 | 
            +
                end
         | 
| 203 | 
            +
             | 
| 204 | 
            +
              end
         | 
| 205 | 
            +
             | 
| 206 | 
            +
            end
         | 
    
        data/lib/oss/cors.rb
    ADDED
    
    | @@ -0,0 +1,106 @@ | |
| 1 | 
            +
            require 'oss/client'
         | 
| 2 | 
            +
            require 'oss/util'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Oss
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              class Cors
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                include Util
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                attr_accessor :client, :xml_obj
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def initialize(client)
         | 
| 13 | 
            +
                  @client = client
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def put_bucket_cors(bucket_name, rules)
         | 
| 17 | 
            +
                  # sign configs
         | 
| 18 | 
            +
                  sign_configs = Hash.new
         | 
| 19 | 
            +
                  sign_configs[:resource]             = "/#{bucket_name}"
         | 
| 20 | 
            +
                  sign_configs[:content_type]         = 'application/x-www-form-urlencoded'
         | 
| 21 | 
            +
                  sign_configs[:content_length_check] = true
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  # build payload xml
         | 
| 24 | 
            +
                  payload = Nokogiri::XML::Builder.new do |xml|
         | 
| 25 | 
            +
                    xml.CORSConfiguration do
         | 
| 26 | 
            +
                      rules.each do |rule|
         | 
| 27 | 
            +
                        xml.CORSRule do
         | 
| 28 | 
            +
                          rule.each do |a_rule|
         | 
| 29 | 
            +
                            xml.send(a_rule[:key], a_rule[:value])
         | 
| 30 | 
            +
                          end
         | 
| 31 | 
            +
                        end
         | 
| 32 | 
            +
                      end
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  @xml_obj = client.put(
         | 
| 37 | 
            +
                      host: "#{bucket_name}.#{client.endpoint}",
         | 
| 38 | 
            +
                      path: '/?cors',
         | 
| 39 | 
            +
                      sign_configs: sign_configs,
         | 
| 40 | 
            +
                      payload: payload.to_xml
         | 
| 41 | 
            +
                  )
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  true
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                def get_bucket_cors(bucket_name)
         | 
| 47 | 
            +
                  @xml_obj = client.get(
         | 
| 48 | 
            +
                      host:         "#{bucket_name}.#{client.endpoint}",
         | 
| 49 | 
            +
                      sign_configs: {resource: "/#{bucket_name}"},
         | 
| 50 | 
            +
                      path:         '/?cors',
         | 
| 51 | 
            +
                  )
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  rules = Array.new
         | 
| 54 | 
            +
                  @xml_obj.xpath('CORSConfiguration/CORSRule').each do |rule|
         | 
| 55 | 
            +
                    rules << rule
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  rules
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                def delete_bucket_cors(bucket_name)
         | 
| 62 | 
            +
                  client.delete(
         | 
| 63 | 
            +
                      host:         "#{bucket_name}.#{client.endpoint}",
         | 
| 64 | 
            +
                      path:         '/?cors',
         | 
| 65 | 
            +
                      sign_configs: {resource: "/#{bucket_name}"},
         | 
| 66 | 
            +
                  )
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  true
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                def option_object(bucket_name, object_name, origin, request_method, request_headers)
         | 
| 72 | 
            +
                  # sign configs
         | 
| 73 | 
            +
                  sign_configs = Hash.new
         | 
| 74 | 
            +
                  sign_configs[:resource] = "/#{bucket_name}"
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                  headers = Hash.new
         | 
| 77 | 
            +
                  headers['Origin'] = origin
         | 
| 78 | 
            +
                  headers['Access-Control-Request-Method']  = request_method
         | 
| 79 | 
            +
                  headers['Access-Control-Request-Headers'] = request_headers
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                  resp = client.options(
         | 
| 82 | 
            +
                      host:         "#{bucket_name}.#{client.endpoint}",
         | 
| 83 | 
            +
                      path:         "/#{object_name}",
         | 
| 84 | 
            +
                      sign_configs: sign_configs,
         | 
| 85 | 
            +
                      headers:      headers,
         | 
| 86 | 
            +
                      as:           :raw
         | 
| 87 | 
            +
                  )
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                  # return response header
         | 
| 90 | 
            +
                  resp.headers
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                def method_missing(method)
         | 
| 94 | 
            +
                  if @xml_obj.nil?
         | 
| 95 | 
            +
                    super
         | 
| 96 | 
            +
                  else
         | 
| 97 | 
            +
                    camel = Util.camelize(method)
         | 
| 98 | 
            +
                    value = @xml_obj.xpath(camel)
         | 
| 99 | 
            +
                    raise "missing xml attribute #{camel}" if value.length == 0
         | 
| 100 | 
            +
                    value.inner_text
         | 
| 101 | 
            +
                  end
         | 
| 102 | 
            +
                end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
              end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
            end
         | 
    
        data/lib/oss/error.rb
    ADDED
    
    | @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            module Oss
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              class ResponseError < StandardError
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                attr_reader :http_status, :error_code, :error_message, :header, :request_id, :host_id
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def initialize(err_attrs, err_msg)
         | 
| 8 | 
            +
                  @error_code    = err_attrs[:code]
         | 
| 9 | 
            +
                  @error_message = err_attrs[:message]
         | 
| 10 | 
            +
                  @http_status   = err_attrs[:http_status]
         | 
| 11 | 
            +
                  @header        = err_attrs[:header]
         | 
| 12 | 
            +
                  @request_id    = err_attrs[:request_id]
         | 
| 13 | 
            +
                  @host_id       = err_attrs[:host_id]
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  super err_msg
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            end
         | 
| @@ -0,0 +1,225 @@ | |
| 1 | 
            +
            require 'oss/client'
         | 
| 2 | 
            +
            require 'oss/util'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Oss
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              class Multipart
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                include Util
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                attr_accessor :client, :xml_obj
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def initialize(client)
         | 
| 13 | 
            +
                  @client = client
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def initiate_multipart_upload(bucket_name, object_name, options = {})
         | 
| 17 | 
            +
                  # set header
         | 
| 18 | 
            +
                  headers = Util.hash_filter(options, {
         | 
| 19 | 
            +
                                                        expires:             'Expires',
         | 
| 20 | 
            +
                                                        content_control:     'Cache-Control',
         | 
| 21 | 
            +
                                                        content_encoding:    'Content-Encoding',
         | 
| 22 | 
            +
                                                        content_disposition: 'Content-Disposition',
         | 
| 23 | 
            +
                                                        encryption:          'x-oss-server-side-encryption',
         | 
| 24 | 
            +
                                                    })
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  # sign configs
         | 
| 27 | 
            +
                  sign_configs = {
         | 
| 28 | 
            +
                      resource:     "/#{bucket_name}",
         | 
| 29 | 
            +
                      content_type: 'application/x-www-form-urlencoded',
         | 
| 30 | 
            +
                      oss_headers:  Util.oss_headers_to_s(options, {
         | 
| 31 | 
            +
                                                                     encryption: 'x-oss-server-side-encryption',
         | 
| 32 | 
            +
                                                                 })
         | 
| 33 | 
            +
                  }
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  @xml_obj = client.post(
         | 
| 36 | 
            +
                      host:         "#{bucket_name}.#{client.endpoint}",
         | 
| 37 | 
            +
                      path:         "/#{object_name}?uploads",
         | 
| 38 | 
            +
                      headers:      headers,
         | 
| 39 | 
            +
                      sign_configs: sign_configs,
         | 
| 40 | 
            +
                  ).xpath('InitiateMultipartUploadResult')
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  # return init info
         | 
| 43 | 
            +
                  {
         | 
| 44 | 
            +
                    bucket:    @xml_obj.xpath('Bucket').text,
         | 
| 45 | 
            +
                    key:       @xml_obj.xpath('Key').text,
         | 
| 46 | 
            +
                    upload_id: @xml_obj.xpath('UploadId').text,
         | 
| 47 | 
            +
                  }
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                def upload_part(bucket_name, object_name, upload_id, file, part_number = 1, options = {})
         | 
| 51 | 
            +
                  # sign configs
         | 
| 52 | 
            +
                  sign_configs = {
         | 
| 53 | 
            +
                      resource:              "/#{bucket_name}",
         | 
| 54 | 
            +
                      content_type:          'application/x-www-form-urlencoded',
         | 
| 55 | 
            +
                      content_length_check:  true,
         | 
| 56 | 
            +
                      content_md5_check:     options[:content_md5_check],
         | 
| 57 | 
            +
                      sign_query_string:     true
         | 
| 58 | 
            +
                  }
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  resp = client.put(
         | 
| 61 | 
            +
                      host:         "#{bucket_name}.#{client.endpoint}",
         | 
| 62 | 
            +
                      path:         "/#{object_name}",
         | 
| 63 | 
            +
                      sign_configs: sign_configs,
         | 
| 64 | 
            +
                      query_string: {'partNumber' => part_number, 'uploadId' => upload_id},
         | 
| 65 | 
            +
                      payload:      file,
         | 
| 66 | 
            +
                      as:           :raw
         | 
| 67 | 
            +
                  )
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                  {etag: resp.headers[:etag],  part_number: part_number}
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                def upload_part_copy(bucket_name, object_name, upload_id, old_bucket, old_object, part_number = 1, options = {})
         | 
| 73 | 
            +
                  # copy source format
         | 
| 74 | 
            +
                  options[:copy_source] = "/#{old_bucket}/#{old_object}"
         | 
| 75 | 
            +
                  options[:copy_source_range] = "bytes=#{options[:copy_source_begin]}-#{options[:copy_source_end]}" unless options[:copy_source_begin].nil?
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                  # set header
         | 
| 78 | 
            +
                  headers = Util.hash_filter(options, {
         | 
| 79 | 
            +
                                                        if_modified_since:   'x-oss-copy-source-if-modified-since',
         | 
| 80 | 
            +
                                                        if_unmodified_since: 'x-oss-copy-source-if-unmodified-since',
         | 
| 81 | 
            +
                                                        if_match:            'x-oss-copy-source-if-match',
         | 
| 82 | 
            +
                                                        if_none_match:       'x-oss-copy-source-if-none-match',
         | 
| 83 | 
            +
                                                        copy_source:         'x-oss-copy-source',
         | 
| 84 | 
            +
                                                        copy_source_range:   'x-oss-copy-source-range'
         | 
| 85 | 
            +
                                                    })
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                  # sign configs
         | 
| 88 | 
            +
                  sign_configs = {
         | 
| 89 | 
            +
                      resource:          "/#{bucket_name}",
         | 
| 90 | 
            +
                      sign_query_string: true,
         | 
| 91 | 
            +
                      content_type:      'application/x-www-form-urlencoded',
         | 
| 92 | 
            +
                      oss_headers:       Util.oss_headers_to_s(options, {
         | 
| 93 | 
            +
                                                                    if_modified_since:   'x-oss-copy-source-if-modified-since',
         | 
| 94 | 
            +
                                                                    if_unmodified_since: 'x-oss-copy-source-if-unmodified-since',
         | 
| 95 | 
            +
                                                                    if_match:            'x-oss-copy-source-if-match',
         | 
| 96 | 
            +
                                                                    if_none_match:       'x-oss-copy-source-if-none-match',
         | 
| 97 | 
            +
                                                                    copy_source:         'x-oss-copy-source',
         | 
| 98 | 
            +
                                                                    copy_source_range:   'x-oss-copy-source-range'
         | 
| 99 | 
            +
                                                                })
         | 
| 100 | 
            +
                  }
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                  @xml_obj = client.put(
         | 
| 103 | 
            +
                      host:         "#{bucket_name}.#{client.endpoint}",
         | 
| 104 | 
            +
                      path:         "/#{object_name}",
         | 
| 105 | 
            +
                      headers:      headers,
         | 
| 106 | 
            +
                      sign_configs: sign_configs,
         | 
| 107 | 
            +
                      query_string: {'partNumber' => part_number, 'uploadId' => upload_id},
         | 
| 108 | 
            +
                  )
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                  {
         | 
| 111 | 
            +
                      last_modify: @xml_obj.xpath('CopyPartResult/LastModified').text,
         | 
| 112 | 
            +
                      etag:        @xml_obj.xpath('CopyPartResult/ETag').text,
         | 
| 113 | 
            +
                  }
         | 
| 114 | 
            +
                end
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                def complete_multipart_upload(bucket_name, object_name, upload_id, parts)
         | 
| 117 | 
            +
                  # build payload xml
         | 
| 118 | 
            +
                  payload = Nokogiri::XML::Builder.new do
         | 
| 119 | 
            +
                    CompleteMultipartUpload do
         | 
| 120 | 
            +
                      parts.each do |part|
         | 
| 121 | 
            +
                        Part do
         | 
| 122 | 
            +
                          PartNumber part[:part_number]
         | 
| 123 | 
            +
                          ETag part[:etag]
         | 
| 124 | 
            +
                        end
         | 
| 125 | 
            +
                      end
         | 
| 126 | 
            +
                    end
         | 
| 127 | 
            +
                  end
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                  @xml_obj = client.post(
         | 
| 130 | 
            +
                      host:         "#{bucket_name}.#{client.endpoint}",
         | 
| 131 | 
            +
                      path:         "/#{object_name}",
         | 
| 132 | 
            +
                      sign_configs: {resource: "/#{bucket_name}", sign_query_string: true, content_type: 'application/x-www-form-urlencoded',},
         | 
| 133 | 
            +
                      query_string: {'uploadId' => upload_id},
         | 
| 134 | 
            +
                      payload:      payload.to_xml
         | 
| 135 | 
            +
                  ).xpath('CompleteMultipartUploadResult')
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                  # return object info
         | 
| 138 | 
            +
                  {
         | 
| 139 | 
            +
                      location: @xml_obj.xpath('Location').text,
         | 
| 140 | 
            +
                      bucket:   @xml_obj.xpath('Bucket').text,
         | 
| 141 | 
            +
                      key:      @xml_obj.xpath('Key').text,
         | 
| 142 | 
            +
                      etag:     @xml_obj.xpath('ETag').text,
         | 
| 143 | 
            +
                  }
         | 
| 144 | 
            +
                end
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                def abort_multipart_upload(bucket_name, object_name, upload_id)
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                  client.delete(
         | 
| 149 | 
            +
                      host:         "#{bucket_name}.#{client.endpoint}",
         | 
| 150 | 
            +
                      path:         "/#{object_name}",
         | 
| 151 | 
            +
                      sign_configs: {resource: "/#{bucket_name}", sign_query_string: true},
         | 
| 152 | 
            +
                      query_string: {'uploadId' => upload_id},
         | 
| 153 | 
            +
                  )
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                  true
         | 
| 156 | 
            +
                end
         | 
| 157 | 
            +
             | 
| 158 | 
            +
                def list_multipart_upload(bucket_name, options = {})
         | 
| 159 | 
            +
                  @xml_obj = client.get(
         | 
| 160 | 
            +
                      host:         "#{bucket_name}.#{client.endpoint}",
         | 
| 161 | 
            +
                      sign_configs: {resource: "/#{bucket_name}"},
         | 
| 162 | 
            +
                      path:         '/?uploads',
         | 
| 163 | 
            +
                      query_string: options
         | 
| 164 | 
            +
                  ).xpath('ListMultipartUploadsResult')
         | 
| 165 | 
            +
             | 
| 166 | 
            +
                  self
         | 
| 167 | 
            +
                end
         | 
| 168 | 
            +
             | 
| 169 | 
            +
                def uploads
         | 
| 170 | 
            +
                  uploads = @xml_obj.xpath('Upload')
         | 
| 171 | 
            +
                  results = Array.new
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                  # parse as array
         | 
| 174 | 
            +
                  uploads.each do |upload|
         | 
| 175 | 
            +
                    results << {
         | 
| 176 | 
            +
                        key:       upload.xpath('Key').text,
         | 
| 177 | 
            +
                        upload_id: upload.xpath('UploadId').text,
         | 
| 178 | 
            +
                        initiated: upload.xpath('Initiated').text,
         | 
| 179 | 
            +
                    }
         | 
| 180 | 
            +
                  end
         | 
| 181 | 
            +
                  results
         | 
| 182 | 
            +
                end
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                def list_parts(bucket_name, object_name, options = {})
         | 
| 185 | 
            +
                  @xml_obj = client.get(
         | 
| 186 | 
            +
                      host:         "#{bucket_name}.#{client.endpoint}",
         | 
| 187 | 
            +
                      path:         "/#{object_name}",
         | 
| 188 | 
            +
                      query_string: options,
         | 
| 189 | 
            +
                      sign_configs: {resource: "/#{bucket_name}", sign_query_string: true},
         | 
| 190 | 
            +
                  ).xpath('ListPartsResult')
         | 
| 191 | 
            +
             | 
| 192 | 
            +
                  self
         | 
| 193 | 
            +
                end
         | 
| 194 | 
            +
             | 
| 195 | 
            +
                def parts
         | 
| 196 | 
            +
                  parts = @xml_obj.xpath('Part')
         | 
| 197 | 
            +
                  results = Array.new
         | 
| 198 | 
            +
             | 
| 199 | 
            +
                  # parse as array
         | 
| 200 | 
            +
                  parts.each do |part|
         | 
| 201 | 
            +
                    results << {
         | 
| 202 | 
            +
                        part_number:   part.xpath('PartNumber').text,
         | 
| 203 | 
            +
                        last_modified: part.xpath('LastModified').text,
         | 
| 204 | 
            +
                        etag:          part.xpath('ETag').text,
         | 
| 205 | 
            +
                        size:          part.xpath('Size').text,
         | 
| 206 | 
            +
                    }
         | 
| 207 | 
            +
                  end
         | 
| 208 | 
            +
                  results
         | 
| 209 | 
            +
                end
         | 
| 210 | 
            +
             | 
| 211 | 
            +
             | 
| 212 | 
            +
                def method_missing(method)
         | 
| 213 | 
            +
                  if @xml_obj.nil?
         | 
| 214 | 
            +
                    super
         | 
| 215 | 
            +
                  else
         | 
| 216 | 
            +
                    camel = Util.camelize(method)
         | 
| 217 | 
            +
                    value = @xml_obj.xpath(camel)
         | 
| 218 | 
            +
                    raise "missing xml attribute #{camel}" if value.length == 0
         | 
| 219 | 
            +
                    value.inner_text
         | 
| 220 | 
            +
                  end
         | 
| 221 | 
            +
                end
         | 
| 222 | 
            +
             | 
| 223 | 
            +
              end
         | 
| 224 | 
            +
             | 
| 225 | 
            +
            end
         |