aliyunoss 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +92 -0
- data/Rakefile +8 -0
- data/aliyunoss.gemspec +24 -0
- data/lib/aliyunoss/api.rb +222 -0
- data/lib/aliyunoss/bucket.rb +131 -0
- data/lib/aliyunoss/config.rb +47 -0
- data/lib/aliyunoss/extension.rb +34 -0
- data/lib/aliyunoss/helper.rb +27 -0
- data/lib/aliyunoss/http_response_extension.rb +66 -0
- data/lib/aliyunoss/oss_request.rb +143 -0
- data/lib/aliyunoss/version.rb +5 -0
- data/lib/aliyunoss.rb +12 -0
- data/spec/aliyunoss/aliyun-config.yml.sample +2 -0
- data/spec/aliyunoss/api_spec.rb +123 -0
- data/spec/aliyunoss/bucket_spec.rb +109 -0
- data/spec/aliyunoss/config_spec.rb +47 -0
- data/spec/aliyunoss/incorrect-config.yml +4 -0
- data/spec/aliyunoss/multipart_api_spec.rb +103 -0
- data/spec/aliyunoss/oss_request_spec.rb +51 -0
- data/spec/aliyunoss/test-1.png +0 -0
- data/spec/aliyunoss/test-2.png +0 -0
- data/spec/aliyunoss/test-3.png +0 -0
- metadata +121 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
module Net
|
2
|
+
|
3
|
+
class HTTPResponse
|
4
|
+
|
5
|
+
def raise_if_not(status)
|
6
|
+
raise message unless status === self
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_buckets
|
10
|
+
raise message unless Net::HTTPOK === self
|
11
|
+
nodes = Nokogiri::XML(self.body).xpath('//Bucket') rescue []
|
12
|
+
nodes.map do |node|
|
13
|
+
bucket = Aliyun::Oss::Bucket.new
|
14
|
+
node.elements.each {|e| bucket.send("#{e.name.underscore}=".to_sym, e.content)}
|
15
|
+
bucket
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_objects
|
20
|
+
raise message unless Net::HTTPOK === self
|
21
|
+
nodes = Nokogiri::XML(self.body).xpath('//Contents') rescue []
|
22
|
+
nodes.map do |node|
|
23
|
+
hash = Hash.new
|
24
|
+
node.elements.each {|e| hash[e.name.underscore] = e.content unless e.name == 'Owner' }
|
25
|
+
hash
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_logging_status
|
30
|
+
raise message unless Net::HTTPOK === self
|
31
|
+
node = Nokogiri::XML(self.body).xpath('//LoggingEnabled') rescue []
|
32
|
+
hash = Hash.new
|
33
|
+
node[0].elements.each {|e| hash[e.name.underscore] = e.content }
|
34
|
+
hash
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_website_status
|
38
|
+
raise message unless Net::HTTPOK === self
|
39
|
+
xml = Nokogiri::XML(self.body)
|
40
|
+
{'index_page' => xml.xpath('//Suffix')[0].content, 'error_page' => xml.xpath('//Key')[0].content } rescue {}
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_acl_status
|
44
|
+
raise message unless Net::HTTPOK === self
|
45
|
+
Nokogiri::XML(self.body).xpath('//Grant')[0].content
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_multipart_id
|
49
|
+
raise message unless Net::HTTPOK === self
|
50
|
+
Nokogiri::XML(body).xpath('//UploadId').first.content
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_mutlipart_task
|
54
|
+
raise message unless Net::HTTPOK === self
|
55
|
+
tasks = Array.new
|
56
|
+
nodes = Nokogiri::XML(response.body).xpath('//Upload')
|
57
|
+
nodes.each do |node|
|
58
|
+
path = '/' + node.xpath('Key').first.content
|
59
|
+
id = node.xpath('UploadId').first.content
|
60
|
+
tasks << {'path'=> path, 'upload_id' => id}
|
61
|
+
end
|
62
|
+
tasks
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'base64'
|
3
|
+
require 'openssl'
|
4
|
+
|
5
|
+
module Aliyun
|
6
|
+
module Oss
|
7
|
+
|
8
|
+
class OssRequest
|
9
|
+
|
10
|
+
include ConfigHelper
|
11
|
+
attr_accessor :bucket, :path, :body, :queris
|
12
|
+
|
13
|
+
def initialize(bucket, path, queries = {}, headers = {})
|
14
|
+
@bucket = bucket
|
15
|
+
@path = path
|
16
|
+
@queries = queries
|
17
|
+
@headers = {"Content-Type" => "", "Content-MD5" => "", "Date" => Time.now.utc.strftime('%a, %d %b %Y %H:%M:%S GMT')}.merge(headers)
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_uri
|
21
|
+
if @bucket
|
22
|
+
uri = URI("http://#{bucket.name}.#{bucket.location}.#{host}")
|
23
|
+
else
|
24
|
+
uri = URI("http://oss.#{host}")
|
25
|
+
end
|
26
|
+
uri.path = @path
|
27
|
+
uri.query = @queries.to_query_string if @queries.count > 0
|
28
|
+
uri
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.add_operation(verb)
|
32
|
+
define_method(verb) do
|
33
|
+
uri = get_uri
|
34
|
+
|
35
|
+
request = Net::HTTP.send(:const_get, verb.to_s.capitalize).new(uri)
|
36
|
+
|
37
|
+
@headers.each_pair {|k,v| request[k] = v}
|
38
|
+
|
39
|
+
if @body
|
40
|
+
request.body = @body
|
41
|
+
digest = OpenSSL::Digest::MD5.digest(@body)
|
42
|
+
request['Content-MD5'] = Base64.encode64(digest).strip
|
43
|
+
request['Content-Length'] = @body.bytesize
|
44
|
+
end
|
45
|
+
|
46
|
+
request['Authorization'] = 'OSS ' + access_key_id + ':' +
|
47
|
+
signature(request)
|
48
|
+
|
49
|
+
logger.info(verb.to_s.upcase + ' ' + uri.to_s + ' ' + request.to_hash.to_s)
|
50
|
+
|
51
|
+
response = nil
|
52
|
+
Net::HTTP.start(uri.host, uri.port) do |http|
|
53
|
+
response = http.request(request)
|
54
|
+
logger.info(response.code.to_s + ' ' + response.message)
|
55
|
+
logger.debug(response.body)
|
56
|
+
end
|
57
|
+
response
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
add_operation :get
|
62
|
+
add_operation :put
|
63
|
+
add_operation :delete
|
64
|
+
add_operation :head
|
65
|
+
add_operation :post
|
66
|
+
|
67
|
+
def url_for_sharing(expires)
|
68
|
+
raise "not implemented"
|
69
|
+
end
|
70
|
+
|
71
|
+
def [](key)
|
72
|
+
@headers[key]
|
73
|
+
end
|
74
|
+
|
75
|
+
def []=(key, value)
|
76
|
+
@headers[key] = value
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
def signature(req)
|
81
|
+
verb = req.class.to_s.gsub(/Net::HTTP::/, '').upcase
|
82
|
+
data = verb + "\n" + req["Content-MD5"] + "\n" +
|
83
|
+
req["Content-Type"] + "\n" + req["Date"] + "\n" +
|
84
|
+
canonicalized_oss_headers(req) +
|
85
|
+
canonicalized_resource()
|
86
|
+
|
87
|
+
digest = OpenSSL::Digest.new('sha1')
|
88
|
+
hmac = OpenSSL::HMAC.digest(digest, access_key_secret, data)
|
89
|
+
Base64.encode64(hmac).strip
|
90
|
+
end
|
91
|
+
|
92
|
+
# oss api 20140828.pdf - 4.2
|
93
|
+
# 1) 将所有以“x-oss-”为前缀的 HTTP 请求头的名字转换成小写字母。如 ’X-OSS-Meta-Name: TaoBao’
|
94
|
+
# 转换成 ’x-oss-meta-name: TaoBao’ 。
|
95
|
+
# 2) 将上一步得到的所有 HTTP 请求头按照字典序进行升序排列。
|
96
|
+
# 3) 如果有相同名字的请求头,则根据标准 RFC 2616, 4.2 章进行合并(两个值之间只用逗号分隔)。
|
97
|
+
# 如有两个名为’x-oss-meta-name’的请求头,对应的值分别为’TaoBao’和’Alipay’,则合并后
|
98
|
+
# 为: ’x-oss-meta-name:TaoBao,Alipay’ 。
|
99
|
+
# 4) 删除请求头和内容之间分隔符两端出现的任何空格。
|
100
|
+
# 如 ’x-oss-meta-name: TaoBao,Alipay’ 转换成: ’x-oss-meta-name:TaoBao,Alipay’ 。
|
101
|
+
# 5) 将所有的头和内容用 ’\n’ 分隔符分隔拼成最后的 CanonicalizedOSSHeader 。
|
102
|
+
def canonicalized_oss_headers(req)
|
103
|
+
hash = Hash.new
|
104
|
+
req.each_header do |header|
|
105
|
+
header = header.downcase
|
106
|
+
next unless header.start_with?('x-oss-')
|
107
|
+
if hash.has_key?(header)
|
108
|
+
hash[header] = hash[header] + "," + req[header].strip
|
109
|
+
else
|
110
|
+
hash[header] = req[header].strip
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
return "" if hash.count == 0
|
115
|
+
|
116
|
+
array = Array.new
|
117
|
+
hash.each_pair {|k,v| array << k + ":" + v }
|
118
|
+
array.sort.join("\n") << "\n"
|
119
|
+
end
|
120
|
+
|
121
|
+
# oss api 20140828.pdf - 4.2
|
122
|
+
# 1) 将 CanonicalizedResource 置成空字符串( “” );
|
123
|
+
# 2) 放入要访问的 OSS 资源:“ /BucketName/ObjectName ”(无 ObjectName 则不填)
|
124
|
+
# 3) 如果请求的资源包括子资源 (sub-resource) ,那么将所有的子资源按照字典序,从小到大排列并
|
125
|
+
# 以 ’&’ 为分隔符生成子资源字符串。在 CanonicalizedResource 字符串尾添加“?”和子资源字
|
126
|
+
# 符串。此时的 CanonicalizedResource 例子如: /BucketName/ObjectName?acl &uploadId=UploadId
|
127
|
+
# 4) 如果用户请求在查询字符串 (query string) 中指定了要重写 (override) 返回请求的 header,那么将这
|
128
|
+
# 些查询字符串及其请求值按照字典序,从小到大排列,以 ’&’ 为分隔符,按参数的字典序添加到
|
129
|
+
# CanonicalizedResource 中。此时的 CanonicalizedResource 例子:
|
130
|
+
# /BucketName/ObjectName?acl&response-content-type=ContentType & uploadId =UploadId
|
131
|
+
def canonicalized_resource()
|
132
|
+
return @path unless @bucket
|
133
|
+
return "/#{@bucket.name}#{@path}" if @queries.count == 0
|
134
|
+
|
135
|
+
array = Array.new
|
136
|
+
@queries.each_pair {|k,v| array << (if v then k+"="+v else k end)}
|
137
|
+
"/#{@bucket.name}#{@path}?#{array.sort.join('&')}"
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|
data/lib/aliyunoss.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# dependency
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'net/http'
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
require 'aliyunoss/helper'
|
7
|
+
require 'aliyunoss/config'
|
8
|
+
require 'aliyunoss/extension'
|
9
|
+
require 'aliyunoss/http_response_extension'
|
10
|
+
require 'aliyunoss/oss_request'
|
11
|
+
require 'aliyunoss/api'
|
12
|
+
require 'aliyunoss/bucket'
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'aliyunoss'
|
3
|
+
|
4
|
+
describe Aliyun::Oss::API do
|
5
|
+
|
6
|
+
before :all do
|
7
|
+
Aliyun::Oss.configure_with('spec/aliyunoss/aliyun-config.yml')
|
8
|
+
# Aliyun::Oss.configure(:log_level=> 'debug')
|
9
|
+
bucket_name = 'aliyun-oss-gem-api-test'
|
10
|
+
response = Aliyun::Oss::API.put_bucket(bucket_name)
|
11
|
+
expect(response).to be_a(Net::HTTPOK)
|
12
|
+
end
|
13
|
+
|
14
|
+
before :each do
|
15
|
+
@api = Aliyun::Oss::API
|
16
|
+
@bucket = Aliyun::Oss::Bucket.new(name: 'aliyun-oss-gem-api-test', location: 'oss-cn-hangzhou')
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should get bucket list using GetService api' do
|
20
|
+
response = @api.list_bucket
|
21
|
+
expect(response).to be_a(Net::HTTPOK)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should disable bucket logging' do
|
25
|
+
expect(@api.delete_logging(@bucket)).to be_a(Net::HTTPNoContent)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should disable bucket website access' do
|
29
|
+
expect(@api.delete_website(@bucket)).to be_a(Net::HTTPNoContent)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should list objects in bucket' do
|
33
|
+
expect(@api.list_object(@bucket)).to be_a(Net::HTTPOK)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should get bucket acl' do
|
37
|
+
response = @api.get_bucket_acl(@bucket)
|
38
|
+
expect(response).to be_a(Net::HTTPOK)
|
39
|
+
xml = Nokogiri::XML(response.body).xpath('//Grant')
|
40
|
+
expect(xml.count).to be >= 1
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should get bucket location' do
|
44
|
+
response = @api.get_bucket_location(@bucket)
|
45
|
+
expect(response).to be_a(Net::HTTPOK)
|
46
|
+
node = Nokogiri::XML(response.body).xpath('//LocationConstraint')[0]
|
47
|
+
expect(node.content).to eq('oss-cn-hangzhou')
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should get bucket logging status' do
|
51
|
+
response = @api.get_bucket_logging(@bucket)
|
52
|
+
expect(response).to be_a(Net::HTTPOK)
|
53
|
+
expect(response.body).to include('BucketLoggingStatus')
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should get bucket logging status' do
|
57
|
+
response = @api.get_bucket_website(@bucket)
|
58
|
+
expect(response).to be_a(Net::HTTPNotFound)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should correctly set bucket acl permission' do
|
62
|
+
expect(@api.set_bucket_acl(@bucket, 'public-read')).to be_a(Net::HTTPOK)
|
63
|
+
expect(@api.set_bucket_acl(@bucket, 'public-read-write')).to be_a(Net::HTTPOK)
|
64
|
+
expect(@api.set_bucket_acl(@bucket, 'private')).to be_a(Net::HTTPOK)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should enable and disable bucket logging func' do
|
68
|
+
expect(@api.disable_bucket_logging(@bucket)).to be_a(Net::HTTPOK)
|
69
|
+
expect(@api.enable_bucket_logging(@bucket, @bucket.name, 'logtest-')).to be_a(Net::HTTPOK)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should set bucket website access configuration' do
|
73
|
+
expect(@api.set_bucket_website(@bucket, 'index.html','error.html')).to be_a(Net::HTTPOK)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should upload file' do
|
77
|
+
files = ['test-1.png','test-2.png','test-3.png'].map {|f| File.join(__dir__, f)}
|
78
|
+
files.each do |f|
|
79
|
+
data = IO.read(f)
|
80
|
+
response = @api.put_object(@bucket, "/" + f[/test-\d\.png/], data, 'Content-Type'=>'image/png')
|
81
|
+
expect(response).to be_a(Net::HTTPOK)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should copy object' do
|
86
|
+
sources = ['/test-1.png','/test-2.png','/test-3.png']
|
87
|
+
sources.each do |source_path|
|
88
|
+
response = @api.copy_object(@bucket, source_path, @bucket, "/copy-" + source_path[1,source_path.length-1])
|
89
|
+
expect(response).to be_a(Net::HTTPOK)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should get object meta data' do
|
94
|
+
expect(@api.head_object(@bucket, '/test-1.png')).to be_a(Net::HTTPOK)
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should get object from oss' do
|
98
|
+
response = @api.get_object(@bucket, '/copy-test-1.png')
|
99
|
+
file_name = File.join(__dir__, 'test-1.png')
|
100
|
+
expect(response['Content-Length']).to eq(File.size(file_name).to_s)
|
101
|
+
expect(response['Content-Type']).to eq('image/png')
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'should delete object' do
|
105
|
+
sources = ['/test-1.png','/test-2.png','/test-3.png']
|
106
|
+
sources.each do |path|
|
107
|
+
expect(@api.delete_object(@bucket,path)).to be_a(Net::HTTPNoContent)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'should delete multiple objects' do
|
112
|
+
response = @api.delete_multiple_objects(@bucket, ['copy-test-2.png','copy-test-3.png', 'copy-test-1.png'])
|
113
|
+
puts response.body
|
114
|
+
expect(response).to be_a(Net::HTTPOK)
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'should delete this bucket' do
|
118
|
+
response = @api.delete_bucket(@bucket)
|
119
|
+
expect(response).to be_a(Net::HTTPNoContent)
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'aliyunoss'
|
3
|
+
|
4
|
+
describe Aliyun::Oss::Bucket do
|
5
|
+
|
6
|
+
before :all do
|
7
|
+
Aliyun::Oss.configure_with('spec/aliyunoss/aliyun-config.yml')
|
8
|
+
end
|
9
|
+
|
10
|
+
before :each do
|
11
|
+
@bucket_name = 'aliyun-oss-gem-api-test'
|
12
|
+
@location = 'oss-cn-hangzhou'
|
13
|
+
@bucket = Aliyun::Oss::Bucket.all.select {|b| b.name == @bucket_name} .first
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should create a bucket' do
|
17
|
+
bucket = Aliyun::Oss::Bucket.create(@bucket_name, @location)
|
18
|
+
expect(bucket).to_not be_nil
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should list all buckets' do
|
22
|
+
buckets = Aliyun::Oss::Bucket.all
|
23
|
+
selected = buckets.select {|b| b.name == @bucket_name}
|
24
|
+
expect(selected.size).to eq(1)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should list files on server' do
|
28
|
+
expect(@bucket.list_files).to_not be_nil
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should upload data to server' do
|
32
|
+
files = ['test-1.png','test-2.png','test-3.png'].map {|f| File.join(__dir__, f)}
|
33
|
+
files.each do |f|
|
34
|
+
data = IO.read(f)
|
35
|
+
@bucket.upload(data, "/" + f[/test-\d\.png/], 'Content-Type'=>'image/png')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should upload data to server in multipart way' do
|
40
|
+
path = '/multi-part-test.dat'
|
41
|
+
@bucket.multipart_upload_initiate(path)
|
42
|
+
|
43
|
+
10.times do
|
44
|
+
@bucket.multipart_upload( Random.new.bytes(1024 * rand(100..300)) )
|
45
|
+
end
|
46
|
+
|
47
|
+
@bucket.multipart_upload_complete
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should copy data in multipart way' do
|
51
|
+
pending 'I get Forbidden using x-oss-copy-source-range'
|
52
|
+
source_path = '/multi-part-test.dat'
|
53
|
+
source_object = @bucket.list_files.select {|f| '/' + f['key'] == source_path} .first
|
54
|
+
source_size = source_object['size'].to_i
|
55
|
+
|
56
|
+
target_path = '/multi-part-copy.dat'
|
57
|
+
@bucket.multipart_upload_initiate(target_path)
|
58
|
+
@bucket.multipart_copy_from(@bucket, source_path, 1024*100, "bytes=0-#{1024*100-1}")
|
59
|
+
@bucket.multipart_upload_complete
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should download file from server' do
|
63
|
+
['/test-1.png','/test-2.png','/test-3.png'].each do |path|
|
64
|
+
remote_data = @bucket.download(path)
|
65
|
+
local_data = IO.read(File.join(__dir__, path))
|
66
|
+
md5 = OpenSSL::Digest::MD5
|
67
|
+
expect(md5.digest(remote_data)).to eq(md5.digest(local_data))
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should delete file on server' do
|
72
|
+
['/test-1.png','/test-2.png','/test-3.png', '/multi-part-test.dat', '/multi-part-copy.dat'].each do |path|
|
73
|
+
@bucket.delete(path)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should get and modify logging status' do
|
78
|
+
@bucket.enable_logging('aliyun-oss-gem-api-test', 'access_log')
|
79
|
+
sleep(2)
|
80
|
+
expect(@bucket.logging_status['target_bucket']).to eq('aliyun-oss-gem-api-test')
|
81
|
+
expect(@bucket.logging_status['target_prefix']).to eq('access_log')
|
82
|
+
@bucket.disable_logging
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should get and modify website access status' do
|
86
|
+
@bucket.enable_website_access('index.html','error.html')
|
87
|
+
sleep(2)
|
88
|
+
expect(@bucket.website_access_status['index_page']).to eq('index.html')
|
89
|
+
expect(@bucket.website_access_status['error_page']).to eq('error.html')
|
90
|
+
@bucket.disable_website_access
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should get and set access control list' do
|
94
|
+
@bucket.set_acl('public-read')
|
95
|
+
sleep(2)
|
96
|
+
expect(@bucket.get_acl).to eq('public-read')
|
97
|
+
@bucket.set_acl('private')
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'should delete specified bucket' do
|
101
|
+
@bucket.multipart_pending.each do |task|
|
102
|
+
@bucket.multipart_abort(task['path'], task['upload_id'])
|
103
|
+
end
|
104
|
+
|
105
|
+
@bucket.delete!
|
106
|
+
expect {Bucket.new(:name=> 'bucket_not_exist').delete!}.to raise_error
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'aliyunoss'
|
3
|
+
|
4
|
+
describe 'Aliyun::Oss Configuration' do
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
config = Aliyun::Oss.config
|
8
|
+
config[:log_level] = 'info'
|
9
|
+
config[:host] = 'aliyuncs.com'
|
10
|
+
config[:access_key_id] = nil
|
11
|
+
config[:access_key_secret] = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should load default options' do
|
15
|
+
default_config = Aliyun::Oss.config
|
16
|
+
expect(default_config).to include(:log_level)
|
17
|
+
expect(default_config).to include(:host)
|
18
|
+
expect(default_config).to include(:access_key_id)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should accept configuration from #configure' do
|
22
|
+
url = "http://bucket_name.region.aliyuncs.com"
|
23
|
+
access_key = "access_key_from_aliyun"
|
24
|
+
Aliyun::Oss.configure(:log_level => 'debug', :host => url, :access_key_id => access_key, :not_used_para => "not used")
|
25
|
+
config = Aliyun::Oss.config
|
26
|
+
expect(config.keys).not_to include(:not_used_para)
|
27
|
+
expect(config[:host]).to eq(url)
|
28
|
+
expect(config[:access_key_id]).to eq(access_key)
|
29
|
+
expect(config[:log_level]).to eq('debug')
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should load yaml config file if specified' do
|
33
|
+
Aliyun::Oss.configure_with('spec/aliyunoss/aliyun-config.yml.sample')
|
34
|
+
config = Aliyun::Oss.config
|
35
|
+
expect(config[:host]).to eq('bucket_name.region.aliyuncs.com')
|
36
|
+
expect(config[:access_key_id]).to eq('access key _id from aliyun')
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should load default configuration if incorrect yaml file specified' do
|
40
|
+
Aliyun::Oss.configure_with('spec/aliyunoss/incorrect-config.yml')
|
41
|
+
config = Aliyun::Oss.config
|
42
|
+
expect(config[:host]).not_to eq('aliyun-yaml.com')
|
43
|
+
expect(config[:access_key_id]).not_to eq('1234567890')
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should load configuration from RAILS_ROOT/config/aliyun-oss.yml in Rails project'
|
47
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'aliyunoss'
|
3
|
+
|
4
|
+
describe Aliyun::Oss::API do
|
5
|
+
|
6
|
+
before :all do
|
7
|
+
Aliyun::Oss.configure_with('spec/aliyunoss/aliyun-config.yml')
|
8
|
+
Aliyun::Oss.configure(:log_level=> 'debug')
|
9
|
+
bucket_name = 'aliyun-oss-gem-api-test'
|
10
|
+
response = Aliyun::Oss::API.put_bucket(bucket_name)
|
11
|
+
expect(response).to be_a(Net::HTTPOK)
|
12
|
+
end
|
13
|
+
|
14
|
+
before :each do
|
15
|
+
@api = Aliyun::Oss::API
|
16
|
+
@bucket = Aliyun::Oss::Bucket.new(name: 'aliyun-oss-gem-api-test', location: 'oss-cn-hangzhou')
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should upload data in multipart way' do
|
20
|
+
path = '/multi-part-test.dat'
|
21
|
+
response = @api.multipart_upload_initiate(@bucket, path)
|
22
|
+
expect(response).to be_a(Net::HTTPOK)
|
23
|
+
|
24
|
+
upload_id = Nokogiri::XML(response.body).xpath('//UploadId')[0].content
|
25
|
+
expect(upload_id).to_not be_nil
|
26
|
+
|
27
|
+
part_list = {}
|
28
|
+
(1..10).each do |no|
|
29
|
+
data = Random.new.bytes(1024 * rand(100..300))
|
30
|
+
response = @api.multipart_upload_part(@bucket, path,upload_id, data, no)
|
31
|
+
expect(response).to be_a(Net::HTTPOK)
|
32
|
+
expect(response['ETag']).to_not be_nil
|
33
|
+
expect(response['ETag']).to be_a(String)
|
34
|
+
part_list[no] = response['ETag']
|
35
|
+
end
|
36
|
+
|
37
|
+
response = @api.multipart_upload_finished_parts(@bucket, path, upload_id)
|
38
|
+
expect(response).to be_a(Net::HTTPOK)
|
39
|
+
expect(response.body.include?('ListPartsResult')).to be true
|
40
|
+
|
41
|
+
response = @api.multipart_upload_complete(@bucket, path, upload_id, part_list)
|
42
|
+
expect(response).to be_a(Net::HTTPOK)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should aboart uploading' do
|
46
|
+
path = '/multi-part-test-aborted.dat'
|
47
|
+
response = @api.multipart_upload_initiate(@bucket, path)
|
48
|
+
expect(response).to be_a(Net::HTTPOK)
|
49
|
+
|
50
|
+
upload_id = Nokogiri::XML(response.body).xpath('//UploadId')[0].content
|
51
|
+
expect(upload_id).to_not be_nil
|
52
|
+
|
53
|
+
data = Random.new.bytes(1024 * rand(100..300))
|
54
|
+
|
55
|
+
response = @api.multipart_upload_part(@bucket, path, upload_id, data, 1)
|
56
|
+
expect(response).to be_a(Net::HTTPOK)
|
57
|
+
|
58
|
+
response = @api.multipart_upload_abort(@bucket, path, upload_id)
|
59
|
+
expect(response).to be_a(Net::HTTPNoContent)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should copy object from bucket in multipart way' do
|
63
|
+
path = '/copy-multi-part-test.dat'
|
64
|
+
response = @api.multipart_upload_initiate(@bucket, path)
|
65
|
+
expect(response).to be_a(Net::HTTPOK)
|
66
|
+
|
67
|
+
upload_id = Nokogiri::XML(response.body).xpath('//UploadId')[0].content
|
68
|
+
expect(upload_id).to_not be_nil
|
69
|
+
|
70
|
+
data = Random.new.bytes(1024 * rand(100..300))
|
71
|
+
|
72
|
+
response = @api.multipart_upload_from_copy(upload_id, @bucket, '/multi-part-test.dat',
|
73
|
+
@bucket, path, 1, 1024*500)
|
74
|
+
expect(response).to be_a(Net::HTTPOK)
|
75
|
+
|
76
|
+
response = @api.multipart_upload_abort(@bucket, path, upload_id)
|
77
|
+
expect(response).to be_a(Net::HTTPNoContent)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should delete uploaded file' do
|
81
|
+
expect(@api.delete_object(@bucket,'/multi-part-test.dat')).to be_a(Net::HTTPNoContent)
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should list unfinished multipart upload task' do
|
85
|
+
response = @api.multipart_upload_unfinished_task(@bucket)
|
86
|
+
expect(response).to be_a(Net::HTTPOK)
|
87
|
+
|
88
|
+
# cancel all unfinished task
|
89
|
+
nodes = Nokogiri::XML(response.body).xpath('//Upload')
|
90
|
+
nodes.each do |node|
|
91
|
+
path = '/' + node.xpath('Key').first.content
|
92
|
+
id = node.xpath('UploadId').first.content
|
93
|
+
response = @api.multipart_upload_abort(@bucket, path, id)
|
94
|
+
expect(response).to be_a(Net::HTTPNoContent)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'should delete this bucket' do
|
99
|
+
response = Aliyun::Oss::API.delete_bucket(@bucket)
|
100
|
+
expect(response).to be_a(Net::HTTPNoContent)
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'rspec'
|
3
|
+
require 'aliyunoss'
|
4
|
+
|
5
|
+
describe Aliyun::Oss::OssRequest do
|
6
|
+
|
7
|
+
it 'should correctly calculate CanonicalizedOssHeader' do
|
8
|
+
Aliyun::Oss.configure(:access_key_id => '44CF9590006BF252F707',
|
9
|
+
:access_key_secret => 'OtxrzxIsfpFjA7SwPzILwy8Bw21TLhquhboDYROV')
|
10
|
+
req = Net::HTTP::Get.new(URI("http://" + Aliyun::Oss.config[:host] + "/nelson"))
|
11
|
+
req["X-OSS-Meta-Author"] = "foo@bar.com"
|
12
|
+
req["X-OSS-Magic"] = "abracadabra"
|
13
|
+
req["Date"] = "Thu, 17 Nov 2005 18:49:58 GMT"
|
14
|
+
req["Content-Type"] = "text/html"
|
15
|
+
req["Content-MD5"] = "ODBGOERFMDMzQTczRUY3NUE3NzA5QzdFNUYzMDQxNEM="
|
16
|
+
result = Aliyun::Oss::OssRequest.new(nil, '/').
|
17
|
+
send(:canonicalized_oss_headers, req)
|
18
|
+
expect(result).to eq("x-oss-magic:abracadabra\nx-oss-meta-author:foo@bar.com\n")
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should correctly calculate CanonicalResource' do
|
22
|
+
bucket = Aliyun::Oss::Bucket.new(:name=>'BucketName',
|
23
|
+
:location=> 'oss-cn-hangzhou')
|
24
|
+
path = '/ObjectName'
|
25
|
+
queries = {"acl"=>nil, "uploadId"=>"UploadId",
|
26
|
+
"response-content-type"=>"ContentType" }
|
27
|
+
result = Aliyun::Oss::OssRequest.new(bucket, path, queries).
|
28
|
+
send(:canonicalized_resource)
|
29
|
+
expect(result).to eq("/BucketName/ObjectName?acl&response-content-type=ContentType&uploadId=UploadId")
|
30
|
+
|
31
|
+
result = Aliyun::Oss::OssRequest.new(bucket, path).
|
32
|
+
send(:canonicalized_resource)
|
33
|
+
expect(result).to eq("/BucketName/ObjectName")
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should correctly calculate signature in Authorization header' do
|
37
|
+
Aliyun::Oss.configure(:access_key_id => '44CF9590006BF252F707',
|
38
|
+
:access_key_secret => 'OtxrzxIsfpFjA7SwPzILwy8Bw21TLhquhboDYROV')
|
39
|
+
bucket = Aliyun::Oss::Bucket.new(name: 'oss-example', location: 'oss-cn-hangzhou')
|
40
|
+
oss_req = Aliyun::Oss::OssRequest.new(bucket, '/nelson')
|
41
|
+
param = Net::HTTP::Put.new(oss_req.get_uri)
|
42
|
+
param["X-OSS-Meta-Author"] = "foo@bar.com"
|
43
|
+
param["X-OSS-Magic"] = "abracadabra"
|
44
|
+
param["Date"] = "Thu, 17 Nov 2005 18:49:58 GMT"
|
45
|
+
param["Content-Type"] = "text/html"
|
46
|
+
param["Content-MD5"] = "ODBGOERFMDMzQTczRUY3NUE3NzA5QzdFNUYzMDQxNEM="
|
47
|
+
result = oss_req.send(:signature, param)
|
48
|
+
expect(result).to eq("26NBxoKdsyly4EDv6inkoDft/yA=")
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
Binary file
|
Binary file
|
Binary file
|