Class: Baidubce::Services::BosClient

Inherits:
BceBaseClient show all
Defined in:
lib/baidubce/services/bos/bos_client.rb

Constant Summary

Constants included from Http

Http::AUTHORIZATION, Http::BCE_ACL, Http::BCE_CONTENT_CRC32, Http::BCE_CONTENT_SHA256, Http::BCE_COPY_METADATA_DIRECTIVE, Http::BCE_COPY_SOURCE, Http::BCE_COPY_SOURCE_IF_MATCH, Http::BCE_COPY_SOURCE_IF_MODIFIED_SINCE, Http::BCE_COPY_SOURCE_IF_NONE_MATCH, Http::BCE_COPY_SOURCE_IF_UNMODIFIED_SINCE, Http::BCE_COPY_SOURCE_RANGE, Http::BCE_DATE, Http::BCE_PREFIX, Http::BCE_REQUEST_ID, Http::BCE_USER_METADATA_PREFIX, Http::BOS_DEBUG_ID, Http::BOS_STORAGE_CLASS, Http::CACHE_CONTROL, Http::CONTENT_DISPOSITION, Http::CONTENT_ENCODING, Http::CONTENT_LENGTH, Http::CONTENT_MD5, Http::CONTENT_RANGE, Http::CONTENT_TYPE, Http::DATE, Http::DELETE, Http::ETAG, Http::EXPIRES, Http::GET, Http::HEAD, Http::HOST, Http::JSON_TYPE, Http::LAST_MODIFIED, Http::OCTET_STREAM_TYPE, Http::POST, Http::PUT, Http::RANGE, Http::SERVER, Http::STS_SECURITY_TOKEN, Http::USER_AGENT

Instance Method Summary collapse

Methods inherited from BceBaseClient

#compute_endpoint, #initialize

Constructor Details

This class inherits a constructor from Baidubce::BceBaseClient

Instance Method Details

#abort_multipart_upload(bucket_name, key, upload_id) ⇒ Object

Abort upload a part which is being uploading.



377
378
379
380
# File 'lib/baidubce/services/bos/bos_client.rb', line 377

def abort_multipart_upload(bucket_name, key, upload_id)
    params = { uploadId: upload_id }
    send_request(DELETE, bucket_name, params, key)
end

#append_object(bucket_name, key, data, offset, content_md5, content_length, options = {}) ⇒ Object

Put an appendable object to BOS or add content to an appendable object.



179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/baidubce/services/bos/bos_client.rb', line 179

def append_object(bucket_name, key, data, offset, content_md5, content_length, options={})
    if content_length > MAX_APPEND_OBJECT_LENGTH
        raise BceClientException.new("Object length should be less than #{MAX_APPEND_OBJECT_LENGTH}. Use multi-part upload instead.")
    end
    params = { append: "" }
    params[:offset] = offset unless offset.nil?
    headers = {
        CONTENT_MD5 => content_md5,
        CONTENT_LENGTH => content_length,
    }
    headers.merge! options
    (headers) unless headers['user-metadata'].nil?
    send_request(POST, bucket_name, params, key, headers, data)
end

#append_object_from_string(bucket_name, key, data, options = {}) ⇒ Object

Create an appendable object and put content of string to the object or add content of string to an appendable object.



196
197
198
199
# File 'lib/baidubce/services/bos/bos_client.rb', line 196

def append_object_from_string(bucket_name, key, data, options={})
    data_md5 = Digest::MD5.base64digest(data)
    append_object(bucket_name, key, data, options['offset'], data_md5, data.bytesize, options)
end

#complete_multipart_upload(bucket_name, key, upload_id, part_list, options = {}) ⇒ Object

After finish all the task, complete multi_upload_file.



366
367
368
369
370
371
372
373
374
# File 'lib/baidubce/services/bos/bos_client.rb', line 366

def complete_multipart_upload(bucket_name, key,upload_id, part_list, options={})
    headers = options
    params = { uploadId: upload_id }

    (headers) unless headers['user-metadata'].nil?
    part_list.each { |part| part['eTag'].gsub!("\"", "") }
    body = { parts: part_list }.to_json
    send_request(POST, bucket_name, params, key, headers, body)
end

#copy_object(source_bucket_name, source_key, target_bucket_name, target_key, options = {}) ⇒ Object

Copy one object to another object.



276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/baidubce/services/bos/bos_client.rb', line 276

def copy_object(source_bucket_name, source_key, target_bucket_name, target_key, options={})
    headers = options
    headers[BCE_COPY_SOURCE_IF_MATCH] = headers['etag'] unless headers['etag'].nil?
    if headers['user-metadata'].nil?
        headers[] = 'copy'
    else
        headers[] = 'replace'
        (headers)
    end

    headers[BCE_COPY_SOURCE] =
        Utils.url_encode_except_slash("/#{source_bucket_name}/#{source_key}")

    send_request(PUT, target_bucket_name, {}, target_key, headers)
end

#create_bucket(bucket_name) ⇒ Object

Create bucket with specific name.



32
33
34
# File 'lib/baidubce/services/bos/bos_client.rb', line 32

def create_bucket(bucket_name)
    send_request(PUT, bucket_name)
end

#delete_bucket(bucket_name) ⇒ Object

Delete bucket with specific name.



37
38
39
# File 'lib/baidubce/services/bos/bos_client.rb', line 37

def delete_bucket(bucket_name)
    send_request(DELETE, bucket_name)
end

#delete_bucket_cors(bucket_name) ⇒ Object

Delete Bucket Cors.



131
132
133
134
# File 'lib/baidubce/services/bos/bos_client.rb', line 131

def delete_bucket_cors(bucket_name)
    params = { cors: "" }
    send_request(DELETE, bucket_name, params)
end

#delete_bucket_lifecycle(bucket_name) ⇒ Object

Delete Bucket Lifecycle.



96
97
98
99
# File 'lib/baidubce/services/bos/bos_client.rb', line 96

def delete_bucket_lifecycle(bucket_name)
    params = { lifecycle: "" }
    send_request(DELETE, bucket_name, params)
end

#delete_bucket_logging(bucket_name) ⇒ Object

Delete Bucket Logging.



151
152
153
154
# File 'lib/baidubce/services/bos/bos_client.rb', line 151

def delete_bucket_logging(bucket_name)
    params = { logging: "" }
    send_request(DELETE, bucket_name, params)
end

#delete_multiple_objects(bucket_name, key_list) ⇒ Object

Delete Multiple Objects.



298
299
300
301
302
303
304
305
# File 'lib/baidubce/services/bos/bos_client.rb', line 298

def delete_multiple_objects(bucket_name, key_list)
    params = { delete: "" }
    key_arr = []
    key_list.each { |item| key_arr << { key: item } }
    body = { objects: key_arr }.to_json
    ret = send_request(POST, bucket_name, params, "", {}, body, nil, true)
    return ret.empty? ? {} : JSON.parse(ret)
end

#delete_object(bucket_name, key) ⇒ Object

Delete Object.



293
294
295
# File 'lib/baidubce/services/bos/bos_client.rb', line 293

def delete_object(bucket_name, key)
    send_request(DELETE, bucket_name, {}, key)
end

#delete_object_acl(bucket_name, key) ⇒ Object

Delete object acl.



418
419
420
421
# File 'lib/baidubce/services/bos/bos_client.rb', line 418

def delete_object_acl(bucket_name, key)
    params = { acl: "" }
    send_request(DELETE, bucket_name, params, key)
end

#does_bucket_exist(bucket_name) ⇒ Object

Check whether there is a bucket with specific name.



42
43
44
45
46
47
48
49
50
# File 'lib/baidubce/services/bos/bos_client.rb', line 42

def does_bucket_exist(bucket_name)
    begin
        send_request(HEAD, bucket_name)
    rescue BceServerException => e
        return false if e.status_code == 404
        return true if e.status_code == 403
    end
    true
end

#generate_pre_signed_url(bucket_name, key, options = {}) ⇒ Object

Get an authorization url with expire time.



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/baidubce/services/bos/bos_client.rb', line 248

def generate_pre_signed_url(bucket_name, key, options={})
    headers = options['headers'].nil? ? {} : options['headers']
    params = options['params'].nil? ? {} : options['params']

    path = Utils.append_uri("/", key)
    url, headers[HOST] = Utils.parse_url_host(@config)
    url.insert(url.index('/') + 2, bucket_name + '.')
    headers[HOST] = bucket_name + '.' + headers[HOST]
    params[AUTHORIZATION.downcase] = @signer.sign(@config.credentials,
                                                        GET,
                                                        path,
                                                        headers,
                                                        params,
                                                        options['timestamp'],
                                                        options['expiration_in_seconds'] || 1800,
                                                        options['headers_to_sign'])
    url += Utils.url_encode_except_slash(path)
    query_str = Utils.get_canonical_querystring(params, false)
    url += "?#{query_str}" unless query_str.to_s.empty?
    url
end

#get_bucket_acl(bucket_name) ⇒ Object

Get Access Control Level of bucket.



61
62
63
64
# File 'lib/baidubce/services/bos/bos_client.rb', line 61

def get_bucket_acl(bucket_name)
    params = { acl: "" }
    send_request(GET, bucket_name, params)
end

#get_bucket_cors(bucket_name) ⇒ Object

Get Bucket Cors.



125
126
127
128
# File 'lib/baidubce/services/bos/bos_client.rb', line 125

def get_bucket_cors(bucket_name)
    params = { cors: "" }
    send_request(GET, bucket_name, params)
end

#get_bucket_lifecycle(bucket_name) ⇒ Object

Gut Bucket Lifecycle.



90
91
92
93
# File 'lib/baidubce/services/bos/bos_client.rb', line 90

def get_bucket_lifecycle(bucket_name)
    params = { lifecycle: "" }
    send_request(GET, bucket_name, params)
end

#get_bucket_location(bucket_name) ⇒ Object

Get the region which the bucket located in. returns region of the bucket(bj/gz/sz).



54
55
56
57
58
# File 'lib/baidubce/services/bos/bos_client.rb', line 54

def get_bucket_location(bucket_name)
    params = { location: "" }
    resp = send_request(GET, bucket_name, params)
    resp['locationConstraint']
end

#get_bucket_logging(bucket_name) ⇒ Object

Get Bucket Logging.



145
146
147
148
# File 'lib/baidubce/services/bos/bos_client.rb', line 145

def get_bucket_logging(bucket_name)
    params = { logging: "" }
    send_request(GET, bucket_name, params)
end

#get_bucket_storageclass(bucket_name) ⇒ Object

Get Bucket Storageclass.



110
111
112
113
114
# File 'lib/baidubce/services/bos/bos_client.rb', line 110

def get_bucket_storageclass(bucket_name)
    params = { storageClass: "" }
    resp = send_request(GET, bucket_name, params)
    resp['storageClass']
end

#get_object(bucket_name, key, range, save_path = nil, return_body = true) ⇒ Object



163
164
165
166
# File 'lib/baidubce/services/bos/bos_client.rb', line 163

def get_object(bucket_name, key, range, save_path=nil, return_body=true)
    headers = range.nil? ? {} : get_range_header_dict(range)
    send_request(GET, bucket_name, {}, key, headers, "", save_path, return_body)
end

#get_object_acl(bucket_name, key) ⇒ Object

Get object acl.



398
399
400
401
# File 'lib/baidubce/services/bos/bos_client.rb', line 398

def get_object_acl(bucket_name, key)
    params = { acl: "" }
    send_request(GET, bucket_name, params, key)
end

#get_object_as_string(bucket_name, key, range = nil) ⇒ Object

Get Content of Object and Put Content to String.



169
170
171
# File 'lib/baidubce/services/bos/bos_client.rb', line 169

def get_object_as_string(bucket_name, key, range=nil)
    get_object(bucket_name, key, range)
end

#get_object_meta_data(bucket_name, key) ⇒ Object

Get meta of object.



271
272
273
# File 'lib/baidubce/services/bos/bos_client.rb', line 271

def (bucket_name, key)
    send_request(HEAD, bucket_name, {}, key)
end

#get_object_to_file(bucket_name, key, save_path, range = nil) ⇒ Object

Get Content of Object and Put Content to File.



174
175
176
# File 'lib/baidubce/services/bos/bos_client.rb', line 174

def get_object_to_file(bucket_name, key, save_path, range=nil)
    get_object(bucket_name, key, range, save_path, false)
end

#get_range_header_dict(range) ⇒ Object

Raises:



430
431
432
433
434
435
# File 'lib/baidubce/services/bos/bos_client.rb', line 430

def get_range_header_dict(range)
    raise BceClientException.new("range type should be a array") unless range.is_a? Array
    raise BceClientException.new("range should have length of 2") unless range.length == 2
    raise BceClientException.new("range all element should be integer") unless range.all? { |i| i.is_a?(Integer) }
    { RANGE => "bytes=#{range[0]}-#{range[1]}" }
end

#initiate_multipart_upload(bucket_name, key, options = {}) ⇒ Object

Initialize multi_upload_file.



308
309
310
311
# File 'lib/baidubce/services/bos/bos_client.rb', line 308

def initiate_multipart_upload(bucket_name, key, options={})
    params = { uploads: "" }
    send_request(POST, bucket_name, params, key, options)
end

#list_bucketsObject

List buckets of user. returns all buckets owned by the user.



27
28
29
# File 'lib/baidubce/services/bos/bos_client.rb', line 27

def list_buckets()
    send_request(GET)
end

#list_multipart_uploads(bucket_name, options = {}) ⇒ Object

List all Multipart upload task which haven’t been ended.(Completed Init_MultiPartUpload but not completed Complete_MultiPartUpload or Abort_MultiPartUpload).



391
392
393
394
395
# File 'lib/baidubce/services/bos/bos_client.rb', line 391

def list_multipart_uploads(bucket_name, options={})
    params = { uploads: "" }
    params.merge! options
    send_request(GET, bucket_name, params)
end

#list_objects(bucket_name, options = {}) ⇒ Object

Get Object Information of bucket.



157
158
159
160
161
# File 'lib/baidubce/services/bos/bos_client.rb', line 157

def list_objects(bucket_name, options={})
    params = { maxKeys: 1000 }
    params.merge! options
    send_request(GET, bucket_name, params)
end

#list_parts(bucket_name, key, upload_id, options = {}) ⇒ Object

List all the parts that have been upload success.



383
384
385
386
387
# File 'lib/baidubce/services/bos/bos_client.rb', line 383

def list_parts(bucket_name, key, upload_id, options={})
    params = { uploadId: upload_id }
    params.merge! options
    send_request(GET, bucket_name, params, key)
end

#populate_headers_with_user_metadata(headers) ⇒ Object

Raises:



437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
# File 'lib/baidubce/services/bos/bos_client.rb', line 437

def (headers)
    meta_size = 0
     = headers['user-metadata']
    raise BceClientException.new("user_metadata should be of type hash.") unless .is_a? Hash

    .each do |k, v|
        k = k.encode(DEFAULT_ENCODING)
        v = v.encode(DEFAULT_ENCODING)
        normalized_key =  + k
        headers[normalized_key] = v
        meta_size += normalized_key.length
        meta_size += v.length
    end

    if meta_size > 
        raise BceClientException.new("Metadata size should not be greater than #{MAX_USER_METADATA_SIZE}")
    end
    headers.delete('user-metadata')
end

#put_bucket_cors(bucket_name, cors_configuration) ⇒ Object

Put Bucket Cors.



117
118
119
120
121
122
# File 'lib/baidubce/services/bos/bos_client.rb', line 117

def put_bucket_cors(bucket_name, cors_configuration)
    params = { cors: "" }
    headers = { CONTENT_TYPE => JSON_TYPE }
    body = { corsConfiguration: cors_configuration }.to_json
    send_request(PUT, bucket_name, params, "", headers, body)
end

#put_bucket_lifecycle(bucket_name, rules) ⇒ Object

Put Bucket Lifecycle.



82
83
84
85
86
87
# File 'lib/baidubce/services/bos/bos_client.rb', line 82

def put_bucket_lifecycle(bucket_name, rules)
    params = { lifecycle: "" }
    headers = { CONTENT_TYPE => JSON_TYPE }
    body = { rule: rules }.to_json
    send_request(PUT, bucket_name, params, "", headers, body)
end

#put_bucket_logging(source_bucket, target_bucket, target_prefix = "") ⇒ Object

Put Bucket Logging.



137
138
139
140
141
142
# File 'lib/baidubce/services/bos/bos_client.rb', line 137

def put_bucket_logging(source_bucket, target_bucket, target_prefix="")
    params = { logging: "" }
    headers = { CONTENT_TYPE => JSON_TYPE }
    body = { targetBucket: target_bucket, targetPrefix: target_prefix }.to_json
    send_request(PUT, source_bucket, params, "", headers, body)
end

#put_bucket_storageclass(bucket_name, storage_class) ⇒ Object

Put Bucket Storageclass.



102
103
104
105
106
107
# File 'lib/baidubce/services/bos/bos_client.rb', line 102

def put_bucket_storageclass(bucket_name, storage_class)
    params = { storageClass: "" }
    headers = { CONTENT_TYPE => JSON_TYPE }
    body = { storageClass: storage_class }.to_json
    send_request(PUT, bucket_name, params, "", headers, body)
end

#put_object(bucket_name, key, data, content_md5, content_length, options, &block) ⇒ Object

Put object to BOS.



202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/baidubce/services/bos/bos_client.rb', line 202

def put_object(bucket_name, key, data, content_md5, content_length, options, &block)
    if content_length > MAX_PUT_OBJECT_LENGTH
        raise BceClientException.new("Object length should be less than #{MAX_PUT_OBJECT_LENGTH}. Use multi-part upload instead.")
    end

    headers = {
        CONTENT_MD5 => content_md5,
        CONTENT_LENGTH => content_length,
    }
    headers.merge! options
    headers[CONTENT_TYPE] = OCTET_STREAM_TYPE if headers[CONTENT_TYPE].nil?
    (headers) unless headers['user-metadata'].nil?
    send_request(PUT, bucket_name, {}, key, headers, data, &block)
end

#put_object_from_file(bucket_name, key, file_name, options = {}) ⇒ Object

Put object and put content of file to the object.



224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/baidubce/services/bos/bos_client.rb', line 224

def put_object_from_file(bucket_name, key, file_name, options={})
    mime = MimeMagic.by_path(file_name)
    options[CONTENT_TYPE] = mime.type if options[CONTENT_TYPE].nil? && !mime.nil?
    buf_size = @config.recv_buf_size
    if options[CONTENT_LENGTH].nil?
        data = File.open(file_name, "rb")
        data_md5 = Utils.get_md5_from_file(file_name, data.size, buf_size)
        put_object(bucket_name, key, data, data_md5, data.size, options)
    else
        left_size = options[CONTENT_LENGTH]
        data_md5 = Utils.get_md5_from_file(file_name, left_size, buf_size)
        put_object(bucket_name, key, "", data_md5, left_size, options) do |buf_writer|
            File.open(file_name, "rb") do |part_fp|
                bytes_to_read = left_size > buf_size ? buf_size : left_size
                until left_size <= 0
                    buf_writer << part_fp.read(bytes_to_read)
                    left_size -= bytes_to_read
                end
            end
        end
    end
end

#put_object_from_string(bucket_name, key, data, options = {}) ⇒ Object

Create object and put content of string to the object.



218
219
220
221
# File 'lib/baidubce/services/bos/bos_client.rb', line 218

def put_object_from_string(bucket_name, key, data, options={})
    data_md5 = Digest::MD5.base64digest(data)
    put_object(bucket_name, key, data, data_md5, data.length, options)
end

#send_request(http_method, bucket_name = "", params = {}, key = "", headers = {}, body = "", save_path = nil, return_body = false, &block) ⇒ Object



423
424
425
426
427
428
# File 'lib/baidubce/services/bos/bos_client.rb', line 423

def send_request(http_method, bucket_name="", params={}, key="", headers={}, body="", save_path=nil, return_body=false, &block)
    path = Utils.append_uri("/", bucket_name, key)
    body, headers = @http_client.send_request(@config, @signer, http_method, path, params, headers, body, save_path, &block)
    # Generate result from headers and body
    Utils.generate_response(headers, body, return_body)
end

#set_bucket_acl(bucket_name, acl) ⇒ Object

Set Access Control Level of bucket by body.



67
68
69
70
71
72
# File 'lib/baidubce/services/bos/bos_client.rb', line 67

def set_bucket_acl(bucket_name, acl)
    params = { acl: "" }
    headers = { CONTENT_TYPE => JSON_TYPE }
    body = { accessControlList: acl }.to_json
    send_request(PUT, bucket_name, params, "", headers, body)
end

#set_bucket_canned_acl(bucket_name, canned_acl) ⇒ Object

Set Access Control Level of bucket by headers.



75
76
77
78
79
# File 'lib/baidubce/services/bos/bos_client.rb', line 75

def set_bucket_canned_acl(bucket_name, canned_acl)
    params = { acl: "" }
    headers = {BCE_ACL => canned_acl}
    send_request(PUT, bucket_name, params, "", headers)
end

#set_object_acl(bucket_name, key, acl) ⇒ Object

Set object acl by body.



404
405
406
407
408
409
# File 'lib/baidubce/services/bos/bos_client.rb', line 404

def set_object_acl(bucket_name, key, acl)
    params = { acl: "" }
    headers = { CONTENT_TYPE => JSON_TYPE }
    body = { accessControlList: acl }.to_json
    send_request(PUT, bucket_name, params, key, headers, body)
end

#set_object_canned_acl(bucket_name, key, canned_acl = {}) ⇒ Object

Set object acl by headers.



412
413
414
415
# File 'lib/baidubce/services/bos/bos_client.rb', line 412

def set_object_canned_acl(bucket_name, key, canned_acl={})
    params = { acl: "" }
    send_request(PUT, bucket_name, params, key, canned_acl)
end

#upload_part(bucket_name, key, upload_id, part_number, part_size, options = {}, &block) ⇒ Object

Upload a part.



314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/baidubce/services/bos/bos_client.rb', line 314

def upload_part(bucket_name, key, upload_id, part_number, part_size, options={}, &block)
    headers = options
    params={ partNumber: part_number, uploadId: upload_id }
    if part_number < MIN_PART_NUMBER || part_number > MAX_PART_NUMBER
            raise BceClientException.new(sprintf("Invalid part_number %d. The valid range is from %d to %d.",
                                                 part_number, MIN_PART_NUMBER, MAX_PART_NUMBER))
    end
    if part_size > MAX_PUT_OBJECT_LENGTH
            raise BceClientException.new(sprintf("Single part length should be less than %d.",
                                                 MAX_PUT_OBJECT_LENGTH))
    end

    headers[CONTENT_LENGTH] = part_size
    headers[CONTENT_TYPE] = OCTET_STREAM_TYPE

    send_request(POST, bucket_name, params, key, headers, &block)
end

#upload_part_copy(source_bucket_name, source_key, target_bucket_name, target_key, upload_id, part_number, part_size, offset, options = {}) ⇒ Object

Copy part.



351
352
353
354
355
356
357
358
359
360
361
362
363
# File 'lib/baidubce/services/bos/bos_client.rb', line 351

def upload_part_copy(source_bucket_name, source_key, target_bucket_name, target_key, upload_id,
                     part_number, part_size, offset, options={})
    headers = options
    params={ partNumber: part_number, uploadId: upload_id }

    (headers) unless headers['user-metadata'].nil?
    headers[BCE_COPY_SOURCE_IF_MATCH] = headers['etag'] unless headers['etag'].nil?
    headers[BCE_COPY_SOURCE_RANGE] = sprintf("bytes=%d-%d", offset, offset + part_size - 1)
    headers[BCE_COPY_SOURCE] =
        Utils.url_encode_except_slash("/#{source_bucket_name}/#{source_key}")
    send_request(PUT, target_bucket_name, params, target_key, headers)

end

#upload_part_from_file(bucket_name, key, upload_id, part_number, part_size, file_name, offset = 0, options = {}) ⇒ Object

Upload a part from file.



333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# File 'lib/baidubce/services/bos/bos_client.rb', line 333

def upload_part_from_file(bucket_name, key, upload_id, part_number,
                          part_size, file_name, offset=0, options={})

    left_size = part_size
    buf_size = @config.send_buf_size
    upload_part(bucket_name, key, upload_id, part_number, part_size, options) do |buf_writer|
        File.open(file_name, "rb") do |part_fp|
            part_fp.seek(offset)
            bytes_to_read = left_size > buf_size ? buf_size : left_size
            until left_size <= 0
                buf_writer << part_fp.read(bytes_to_read)
                left_size -= bytes_to_read
            end
        end
    end
end