paperclip-storage-aliyun 0.0.3 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/aliyun/connection.rb +69 -53
- data/lib/aliyun/data_center.rb +35 -13
- data/lib/aliyun/errors.rb +3 -3
- data/lib/paperclip/storage/aliyun.rb +41 -7
- metadata +4 -18
- data/.gitignore +0 -3
- data/.rspec +0 -1
- data/Gemfile +0 -15
- data/README.md +0 -44
- data/paperclip-storage-aliyun.gemspec +0 -16
- data/spec/aliyun_spec.rb +0 -59
- data/spec/attachments/girl.jpg +0 -0
- data/spec/attachments/masu.pdf +0 -0
- data/spec/fixtures/schema.rb +0 -8
- data/spec/lib/paperclip/storage/aliyun_spec.rb +0 -76
- data/spec/spec_helper.rb +0 -29
- data/spec/support/post.rb +0 -24
- data/spec/support/rails.rb +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ff16e09b8b10e23dd324564800cc616a8a18f10f8b565232cc07828f188e4187
|
4
|
+
data.tar.gz: b19db842235f98300bebe578bc0ba04fe5d3901382aea376b14b5ecf49e7eae3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b25f22a60b2c143cb8defa97c95046f7b03520298ba9a9467bd9252ff7629025aa7e3201cf4be8c38b093ea5e3730dc1d35d49eaa93e003953c82ff5b40f6e3f
|
7
|
+
data.tar.gz: dc7a329077c64cde95e3ffa7389f18bb386e1cd3d058434301e5e1e9db5ea6b122470b593f10bfc97f885b97e319fa7e83f07387cf752e56ea5ef4932f5baa25
|
data/lib/aliyun/connection.rb
CHANGED
@@ -1,46 +1,54 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
require 'openssl'
|
4
2
|
require 'digest/md5'
|
5
|
-
require
|
6
|
-
require
|
3
|
+
require 'rest-client'
|
4
|
+
require 'base64'
|
7
5
|
require 'uri'
|
8
6
|
require 'aliyun/data_center'
|
9
7
|
|
10
8
|
module Aliyun
|
11
9
|
class Connection
|
12
10
|
include DataCenter
|
11
|
+
|
12
|
+
# The upload host according to the connection configurations
|
13
|
+
attr_reader :aliyun_upload_host
|
14
|
+
# The internal host
|
15
|
+
attr_reader :aliyun_internal_host
|
16
|
+
# The external host
|
17
|
+
attr_reader :aliyun_external_host
|
18
|
+
# The alias host
|
19
|
+
attr_reader :aliyun_alias_host
|
20
|
+
|
21
|
+
attr_reader :aliyun_protocol
|
22
|
+
|
23
|
+
attr_reader :aliyun_protocol_relative_url
|
24
|
+
|
13
25
|
# Initialize the OSS connection
|
14
26
|
#
|
15
27
|
# @param [Hash] An options to specify connection details
|
16
28
|
# @option access_id [String] used to set "Authorization" request header
|
17
29
|
# @option access_key [String] the access key
|
18
30
|
# @option bucket [String] bucket used to access
|
19
|
-
# @option data_center [String] available data center name, e.g. 'hangzhou'
|
31
|
+
# @option data_center [String] available data center name, e.g. 'cn-hangzhou'
|
20
32
|
# @option internal [true, false] if the service should be accessed through internal network
|
21
|
-
# @option
|
33
|
+
# @option host_alias [String] the alias of the host, such as the CDN domain name
|
34
|
+
# @option protocol [String] 'http' or 'https', default to 'http'
|
35
|
+
# @option protocol_relative_url [true, false] if to use protocol relative url, https://en.wikipedia.org/wiki/Wikipedia:Protocol-relative_URL
|
22
36
|
# @note both access_id and acces_key are related to authorization algorithm:
|
23
37
|
# https://docs.aliyun.com/#/pub/oss/api-reference/access-control&signature-header
|
24
38
|
def initialize(options = {})
|
25
39
|
@aliyun_access_id = options[:access_id]
|
26
40
|
@aliyun_access_key = options[:access_key]
|
27
41
|
@aliyun_bucket = options[:bucket]
|
42
|
+
@aliyun_protocol_relative_url = !!options[:protocol_relative_url]
|
43
|
+
@aliyun_protocol = options[:protocol] || 'http'
|
28
44
|
|
29
|
-
@
|
30
|
-
|
31
|
-
@
|
32
|
-
|
33
|
-
@aliyun_host = options[:host] || @aliyun_upload_host
|
34
|
-
end
|
35
|
-
|
36
|
-
# the file host according to the connection configurations
|
37
|
-
#
|
38
|
-
# @return [String] the host value
|
39
|
-
def fetch_file_host
|
40
|
-
@aliyun_host
|
45
|
+
@aliyun_upload_host = "#{@aliyun_bucket}.#{get_endpoint(options)}"
|
46
|
+
@aliyun_internal_host = "#{@aliyun_bucket}.#{get_endpoint(options.merge(internal: true))}"
|
47
|
+
@aliyun_external_host = "#{@aliyun_bucket}.#{get_endpoint(options.merge(internal: false))}"
|
48
|
+
@aliyun_alias_host = options[:host_alias] || @aliyun_upload_host
|
41
49
|
end
|
42
50
|
|
43
|
-
# Return the meta informations for
|
51
|
+
# Return the meta informations for a file specified by the path
|
44
52
|
# https://docs.aliyun.com/#/pub/oss/api-reference/object&HeadObject
|
45
53
|
#
|
46
54
|
# @param path [String] the path of file storaged in Aliyun OSS
|
@@ -60,39 +68,48 @@ module Aliyun
|
|
60
68
|
# :x_oss_request_id=>"55BD83A5D4C05BDFF4A329E0"}}
|
61
69
|
#
|
62
70
|
def head(path)
|
71
|
+
path = format_path(path)
|
72
|
+
bucket_path = get_bucket_path(path)
|
73
|
+
date = gmtdate
|
74
|
+
headers = {
|
75
|
+
'Host' => @aliyun_upload_host,
|
76
|
+
'Date' => date,
|
77
|
+
'Authorization' => sign('HEAD', bucket_path, '', '', date)
|
78
|
+
}
|
63
79
|
url = path_to_url(path)
|
64
|
-
RestClient.head(url).headers
|
80
|
+
RestClient.head(url, headers).headers
|
65
81
|
rescue RestClient::ResourceNotFound
|
66
82
|
{}
|
67
83
|
end
|
68
84
|
|
69
85
|
# Upload File to Aliyun OSS
|
70
86
|
# https://docs.aliyun.com/#/pub/oss/api-reference/object&PutObject
|
71
|
-
#
|
87
|
+
#
|
72
88
|
# @param path [String] the target storing path on the oss
|
73
89
|
# @param file [File] an instance of File represents a file to be uploaded
|
74
90
|
# @param options [Hash]
|
75
91
|
# - content_type - MimeType value for the file, default is "image/jpg"
|
76
|
-
#
|
92
|
+
#
|
77
93
|
# @return [String] The downloadable url of the uploaded file
|
78
94
|
# @return [nil] if the uploading failed
|
79
|
-
def put(path, file, options={})
|
95
|
+
def put(path, file, options = {})
|
80
96
|
path = format_path(path)
|
81
97
|
bucket_path = get_bucket_path(path)
|
82
|
-
content_md5 = Digest::MD5.file(file)
|
83
|
-
content_type = options[:content_type] ||
|
98
|
+
content_md5 = Digest::MD5.file(file).base64digest
|
99
|
+
content_type = options[:content_type] || 'image/jpg'
|
84
100
|
date = gmtdate
|
85
101
|
url = path_to_url(path)
|
86
|
-
auth_sign = sign(
|
102
|
+
auth_sign = sign('PUT', bucket_path, content_md5, content_type, date)
|
87
103
|
headers = {
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
104
|
+
'Authorization' => auth_sign,
|
105
|
+
'Content-Md5' => content_md5,
|
106
|
+
'Content-Type' => content_type,
|
107
|
+
'Content-Length' => file.size,
|
108
|
+
'Date' => date,
|
109
|
+
'Host' => @aliyun_upload_host,
|
110
|
+
'Expect' => '100-Continue'
|
94
111
|
}
|
95
|
-
response = RestClient.put(
|
112
|
+
response = RestClient.put(url, file, headers)
|
96
113
|
response.code == 200 ? path_to_url(path) : nil
|
97
114
|
end
|
98
115
|
|
@@ -107,12 +124,12 @@ module Aliyun
|
|
107
124
|
bucket_path = get_bucket_path(path)
|
108
125
|
date = gmtdate
|
109
126
|
headers = {
|
110
|
-
|
111
|
-
|
112
|
-
|
127
|
+
'Host' => @aliyun_upload_host,
|
128
|
+
'Date' => date,
|
129
|
+
'Authorization' => sign('DELETE', bucket_path, '', '', date)
|
113
130
|
}
|
114
131
|
url = path_to_url(path)
|
115
|
-
response = RestClient.delete(
|
132
|
+
response = RestClient.delete(url, headers)
|
116
133
|
response.code == 204 ? url : nil
|
117
134
|
end
|
118
135
|
|
@@ -126,12 +143,12 @@ module Aliyun
|
|
126
143
|
bucket_path = get_bucket_path(path)
|
127
144
|
date = gmtdate
|
128
145
|
headers = {
|
129
|
-
|
130
|
-
|
131
|
-
|
146
|
+
'Host' => @aliyun_upload_host,
|
147
|
+
'Date' => date,
|
148
|
+
'Authorization' => sign('GET', bucket_path, '', '', date)
|
132
149
|
}
|
133
150
|
url = path_to_url(path)
|
134
|
-
response = RestClient.get(
|
151
|
+
response = RestClient.get(url, headers)
|
135
152
|
response.body
|
136
153
|
end
|
137
154
|
|
@@ -150,7 +167,7 @@ module Aliyun
|
|
150
167
|
#
|
151
168
|
# @return [String] a string represents the formated time, e.g. "Wed, 05 Sep. 2012 23:00:00 GMT"
|
152
169
|
def gmtdate
|
153
|
-
Time.now.gmtime.strftime(
|
170
|
+
Time.now.gmtime.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
154
171
|
end
|
155
172
|
|
156
173
|
# remove leading slashes in the path
|
@@ -158,10 +175,7 @@ module Aliyun
|
|
158
175
|
# @param path [String] the path to retrieve the file on remote storage
|
159
176
|
# @return [String] the new string after removing leading slashed
|
160
177
|
def format_path(path)
|
161
|
-
|
162
|
-
path.gsub!(/^\/+/,"")
|
163
|
-
|
164
|
-
path
|
178
|
+
path.blank? ? '' : path.gsub(%r{^/+}, '')
|
165
179
|
end
|
166
180
|
|
167
181
|
# A path consis of the bucket name and file name
|
@@ -170,7 +184,7 @@ module Aliyun
|
|
170
184
|
# @param path [String] the path to retrieve the file on remote storage
|
171
185
|
# @return [String] the expected bucket path, e.g. "test-bucket/oss-api.pdf"
|
172
186
|
def get_bucket_path(path)
|
173
|
-
[@aliyun_bucket,path].join(
|
187
|
+
[@aliyun_bucket, path].join('/')
|
174
188
|
end
|
175
189
|
|
176
190
|
# The full path contains host name to the file
|
@@ -178,11 +192,11 @@ module Aliyun
|
|
178
192
|
# @param path [String] the path to retrieve the file on remote storage
|
179
193
|
# @return [String] the expected full path, e.g. "http://martin-test.oss-cn-hangzhou.aliyuncs.com/oss-api.pdf"
|
180
194
|
def path_to_url(path)
|
181
|
-
|
182
|
-
"http://#{fetch_file_host}/#{path}"
|
195
|
+
URI.encode(path =~ %r{^https?://} ? path : "http://#{aliyun_upload_host}/#{path}")
|
183
196
|
end
|
184
197
|
|
185
198
|
private
|
199
|
+
|
186
200
|
# The signature algorithm
|
187
201
|
# https://docs.aliyun.com/#/pub/oss/api-reference/access-control&signature-header
|
188
202
|
#
|
@@ -190,14 +204,16 @@ module Aliyun
|
|
190
204
|
# @param content_md5 [String] the md5 value for the content to be uploaded
|
191
205
|
# @param content_type [String] the content type of the file, e.g. "application/pdf"
|
192
206
|
# @param date [String] the GMT formatted date string
|
193
|
-
def sign(verb, path, content_md5
|
207
|
+
def sign(verb, path, content_md5, content_type, date)
|
194
208
|
canonicalized_oss_headers = ''
|
195
209
|
canonicalized_resource = "/#{path}"
|
196
|
-
string_to_sign =
|
210
|
+
string_to_sign = [
|
211
|
+
verb, content_md5, content_type, date,
|
212
|
+
canonicalized_oss_headers + canonicalized_resource
|
213
|
+
].join("\n")
|
197
214
|
digest = OpenSSL::Digest.new('sha1')
|
198
215
|
h = OpenSSL::HMAC.digest(digest, @aliyun_access_key, string_to_sign)
|
199
|
-
|
200
|
-
"OSS #{@aliyun_access_id}:#{h}"
|
216
|
+
"OSS #{@aliyun_access_id}:#{Base64.encode64(h)}"
|
201
217
|
end
|
202
218
|
end
|
203
219
|
end
|
data/lib/aliyun/data_center.rb
CHANGED
@@ -1,20 +1,42 @@
|
|
1
1
|
require 'aliyun/errors'
|
2
2
|
|
3
3
|
module Aliyun
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
module DataCenter
|
5
|
+
# https://help.aliyun.com/document_detail/31837.html
|
6
|
+
AVAILABLE_DATA_CENTERS = %w(
|
7
|
+
oss-cn-hangzhou
|
8
|
+
oss-cn-shanghai
|
9
|
+
oss-cn-qingdao
|
10
|
+
oss-cn-beijing
|
11
|
+
oss-cn-zhangjiakou
|
12
|
+
oss-cn-huhehaote
|
13
|
+
oss-cn-shenzhen
|
14
|
+
oss-cn-heyuan
|
15
|
+
oss-cn-chengdu
|
16
|
+
oss-cn-hongkong
|
17
|
+
oss-us-west-1
|
18
|
+
oss-us-east-1
|
19
|
+
oss-ap-southeast-1
|
20
|
+
oss-ap-southeast-2
|
21
|
+
oss-ap-southeast-3
|
22
|
+
oss-ap-southeast-5
|
23
|
+
oss-ap-northeast-1
|
24
|
+
oss-ap-south-1
|
25
|
+
oss-eu-central-1
|
26
|
+
oss-eu-west-1
|
27
|
+
oss-me-east-1
|
28
|
+
)
|
8
29
|
|
9
30
|
def get_endpoint(options)
|
10
|
-
|
11
|
-
if (AVAILABLE_CHINA_DATA_CENTERS + AVAILABLE_US_DATA_CENTERS).exclude?(data_center)
|
12
|
-
raise InvalildDataCenter, "Unsupported Data Center #{data_center} Detected"
|
13
|
-
end
|
31
|
+
data_center = options[:data_center]
|
14
32
|
|
15
|
-
|
16
|
-
|
17
|
-
|
33
|
+
data_center.prepend('oss-') unless data_center.match(/^oss/)
|
34
|
+
|
35
|
+
unless AVAILABLE_DATA_CENTERS.include?(data_center)
|
36
|
+
fail InvalildDataCenter, "Unsupported Data Center #{options[:data_center]} Detected"
|
37
|
+
end
|
38
|
+
|
39
|
+
"#{data_center}#{options[:internal] ? '-internal' : ''}.aliyuncs.com"
|
18
40
|
end
|
19
|
-
|
20
|
-
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/aliyun/errors.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
1
|
module Aliyun
|
2
|
-
|
3
|
-
|
4
|
-
end
|
2
|
+
class InvalildDataCenter < StandardError
|
3
|
+
end
|
4
|
+
end
|
@@ -2,12 +2,46 @@ module Paperclip
|
|
2
2
|
module Storage
|
3
3
|
module Aliyun
|
4
4
|
def self.extended(base)
|
5
|
+
base.instance_eval do
|
6
|
+
@aliyun_options = @options[:aliyun]
|
7
|
+
end
|
8
|
+
|
9
|
+
[
|
10
|
+
:aliyun_upload_url, :aliyun_internal_url,
|
11
|
+
:aliyun_external_url, :aliyun_alias_url
|
12
|
+
].each do |url_style|
|
13
|
+
Paperclip.interpolates(url_style) do |attachment, style|
|
14
|
+
attachment.send(url_style, style)
|
15
|
+
end unless Paperclip::Interpolations.respond_to? url_style
|
16
|
+
end
|
5
17
|
end
|
6
18
|
|
7
|
-
def
|
8
|
-
|
19
|
+
def build_aliyun_object_url(host, style)
|
20
|
+
if oss_connection.aliyun_protocol_relative_url
|
21
|
+
"//#{host}/#{path(style).sub(%r{\A/}, '')}"
|
22
|
+
else
|
23
|
+
"#{oss_connection.aliyun_protocol}://#{host}/#{path(style).sub(%r{\A/}, '')}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def aliyun_upload_url(style = default_style)
|
28
|
+
build_aliyun_object_url(oss_connection.aliyun_upload_host, style)
|
29
|
+
end
|
9
30
|
|
10
|
-
|
31
|
+
def aliyun_internal_url(style = default_style)
|
32
|
+
build_aliyun_object_url(oss_connection.aliyun_internal_host, style)
|
33
|
+
end
|
34
|
+
|
35
|
+
def aliyun_external_url(style = default_style)
|
36
|
+
build_aliyun_object_url(oss_connection.aliyun_external_host, style)
|
37
|
+
end
|
38
|
+
|
39
|
+
def aliyun_alias_url(style = default_style)
|
40
|
+
build_aliyun_object_url(oss_connection.aliyun_alias_host, style)
|
41
|
+
end
|
42
|
+
|
43
|
+
def exists?(style = default_style)
|
44
|
+
path(style) ? oss_connection.exists?(path(style)) : false
|
11
45
|
end
|
12
46
|
|
13
47
|
def flush_writes #:nodoc:
|
@@ -28,7 +62,7 @@ module Paperclip
|
|
28
62
|
@queued_for_delete = []
|
29
63
|
end
|
30
64
|
|
31
|
-
def copy_to_local_file(style
|
65
|
+
def copy_to_local_file(style, local_dest_path)
|
32
66
|
log("copying #{path(style)} to local file #{local_dest_path}")
|
33
67
|
local_file = ::File.open(local_dest_path, 'wb')
|
34
68
|
remote_file_str = oss_connection.get path(style)
|
@@ -37,9 +71,9 @@ module Paperclip
|
|
37
71
|
end
|
38
72
|
|
39
73
|
def oss_connection
|
40
|
-
|
41
|
-
|
42
|
-
|
74
|
+
@oss_connection ||= ::Aliyun::Connection.new(
|
75
|
+
Paperclip::Attachment.default_options[:aliyun].merge(@aliyun_options)
|
76
|
+
)
|
43
77
|
end
|
44
78
|
end
|
45
79
|
end
|
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paperclip-storage-aliyun
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martin Hong
|
8
|
+
- Aidi Stan
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date:
|
12
|
+
date: 2020-06-14 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: paperclip
|
@@ -44,24 +45,11 @@ executables: []
|
|
44
45
|
extensions: []
|
45
46
|
extra_rdoc_files: []
|
46
47
|
files:
|
47
|
-
- ".gitignore"
|
48
|
-
- ".rspec"
|
49
|
-
- Gemfile
|
50
|
-
- README.md
|
51
48
|
- lib/aliyun/connection.rb
|
52
49
|
- lib/aliyun/data_center.rb
|
53
50
|
- lib/aliyun/errors.rb
|
54
51
|
- lib/paperclip-storage-aliyun.rb
|
55
52
|
- lib/paperclip/storage/aliyun.rb
|
56
|
-
- paperclip-storage-aliyun.gemspec
|
57
|
-
- spec/aliyun_spec.rb
|
58
|
-
- spec/attachments/girl.jpg
|
59
|
-
- spec/attachments/masu.pdf
|
60
|
-
- spec/fixtures/schema.rb
|
61
|
-
- spec/lib/paperclip/storage/aliyun_spec.rb
|
62
|
-
- spec/spec_helper.rb
|
63
|
-
- spec/support/post.rb
|
64
|
-
- spec/support/rails.rb
|
65
53
|
homepage: https://github.com/Martin91/paperclip-storage-aliyun
|
66
54
|
licenses:
|
67
55
|
- MIT
|
@@ -81,10 +69,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
81
69
|
- !ruby/object:Gem::Version
|
82
70
|
version: '0'
|
83
71
|
requirements: []
|
84
|
-
|
85
|
-
rubygems_version: 2.4.8
|
72
|
+
rubygems_version: 3.0.6
|
86
73
|
signing_key:
|
87
74
|
specification_version: 4
|
88
75
|
summary: Extend a Aliyun OSS storage for paperclip
|
89
76
|
test_files: []
|
90
|
-
has_rdoc:
|
data/.gitignore
DELETED
data/.rspec
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
--color
|
data/Gemfile
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
source 'http://ruby.taobao.org'
|
2
|
-
|
3
|
-
# A database backend that translates database interactions into no-ops. Using
|
4
|
-
# NullDB enables you to test your model business logic - including after_save
|
5
|
-
# hooks - without ever touching a real database.
|
6
|
-
gem "activerecord-nulldb-adapter"
|
7
|
-
|
8
|
-
gemspec
|
9
|
-
|
10
|
-
group :test do
|
11
|
-
gem 'activerecord', '~> 4.0.0'
|
12
|
-
gem 'rspec', '~> 3.3.0'
|
13
|
-
gem 'pry'
|
14
|
-
gem 'pry-nav'
|
15
|
-
end
|
data/README.md
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
Aliyun Open Storage Service for Paperclip
|
2
|
-
===
|
3
|
-
This gem implement the support for [Aliyun open storage service(OSS)](http://oss.aliyun.com) to [Paperclip](https://github.com/thoughtbot/paperclip).
|
4
|
-
|
5
|
-
#### Installation
|
6
|
-
```shell
|
7
|
-
gem install paperclip-storage-aliyun
|
8
|
-
```
|
9
|
-
Or, if you are using a bundler, you can append the following line into your **Gemfile**:
|
10
|
-
```ruby
|
11
|
-
gem 'paperclip-storage-aliyun'
|
12
|
-
```
|
13
|
-
|
14
|
-
#### Configuration
|
15
|
-
In order to make all the things work, you should do some important configurations through a initializer:
|
16
|
-
|
17
|
-
If you are developing a Rails application, you can append a new initializer like:
|
18
|
-
```ruby
|
19
|
-
# [rails_root]/config/initializers/paperclip-aliyun-configuration.rb
|
20
|
-
Paperclip::Attachment.default_options[:aliyun] = {
|
21
|
-
access_id: '3VL9XMho8iCushj8',
|
22
|
-
access_key: 'VAUI2q7Tc6yTh1jr3kBsEUzZ84gEa2',
|
23
|
-
bucket: 'xx-test',
|
24
|
-
data_centre: 'hangzhou',
|
25
|
-
internal: false
|
26
|
-
}
|
27
|
-
```
|
28
|
-
Then, in the model which defines the attachment, specify your storage and other options, for example:
|
29
|
-
```ruby
|
30
|
-
# [rails_root]/app/models/image.rb
|
31
|
-
include Paperclip::Storage::Aliyun
|
32
|
-
|
33
|
-
class Image < ActiveRecord::Base
|
34
|
-
has_attached_file :attachment, {
|
35
|
-
storage: :aliyun,
|
36
|
-
styles: { thumbnail: "60x60#"},
|
37
|
-
path: 'public/system/:class/:attachment/:id_partition/:style/:filename',
|
38
|
-
url: "http://#{oss_connection.fetch_file_host}/public/system/:class/:attachment/:id_partition/:style/:filename"
|
39
|
-
}
|
40
|
-
end
|
41
|
-
```
|
42
|
-
|
43
|
-
### Thanks
|
44
|
-
这个gem是在参考[Jason Lee](https://github.com/huacnlee)先生写的gem [carrierwave-aliyun](https://github.com/huacnlee/carrierwave-aliyun)的基础上写出来的,其中主要直接用了阿里云接口的代码以及对应的测试代码,在此基础上自行实现Paperclip必要的`get`方法以及`exists?`方法。在此特别感谢**Jason Lee**先生的开源代码。
|
@@ -1,16 +0,0 @@
|
|
1
|
-
Gem::Specification.new do |s|
|
2
|
-
s.name = 'paperclip-storage-aliyun'
|
3
|
-
s.platform = Gem::Platform::RUBY
|
4
|
-
s.require_path = 'lib'
|
5
|
-
s.summary = 'Extend a Aliyun OSS storage for paperclip'
|
6
|
-
s.description = 'Extend a Aliyun OSS storage for paperclip'
|
7
|
-
s.version = '0.0.3'
|
8
|
-
s.files = `git ls-files`.split("\n")
|
9
|
-
s.authors = ['Martin Hong']
|
10
|
-
s.email = 'hongzeqin@gmail.com'
|
11
|
-
s.homepage = 'https://github.com/Martin91/paperclip-storage-aliyun'
|
12
|
-
s.license = 'MIT'
|
13
|
-
|
14
|
-
s.add_runtime_dependency 'paperclip', '>= 3.5.2'
|
15
|
-
s.add_runtime_dependency 'rest-client', '>= 1.6.7'
|
16
|
-
end
|
data/spec/aliyun_spec.rb
DELETED
@@ -1,59 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
require "net/http"
|
4
|
-
|
5
|
-
describe Aliyun::Connection do
|
6
|
-
before :all do
|
7
|
-
@connection = ::Aliyun::Connection.new OSS_CONNECTION_OPTIONS
|
8
|
-
@path = 'a/a.jpg'
|
9
|
-
end
|
10
|
-
|
11
|
-
describe '#initialize' do
|
12
|
-
it "raise error when use invalid data center" do
|
13
|
-
expect do
|
14
|
-
::Aliyun::Connection.new data_center: 'guangzhou'
|
15
|
-
end.to raise_error(Aliyun::InvalildDataCenter)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
describe '#put' do
|
20
|
-
it "upload the attachment" do
|
21
|
-
url = @connection.put @path, load_attachment("girl.jpg")
|
22
|
-
response_code = Net::HTTP.get_response(URI.parse(url)).code
|
23
|
-
expect(response_code).to eq("200")
|
24
|
-
end
|
25
|
-
|
26
|
-
it "support setting content type" do
|
27
|
-
content_type = "application/pdf"
|
28
|
-
path = 'pdfs/masu.pdf'
|
29
|
-
@connection.put path, load_attachment("masu.pdf"), content_type: content_type
|
30
|
-
file_meta = @connection.head(path)
|
31
|
-
expect(file_meta[:content_type]).to eq(content_type)
|
32
|
-
|
33
|
-
@connection.delete path
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
describe '#delete' do
|
38
|
-
it "delete the attachment" do
|
39
|
-
url = @connection.delete @path
|
40
|
-
response_code = Net::HTTP.get_response(URI.parse(url)).code
|
41
|
-
expect(response_code).to eq("404")
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
describe "#exists?" do
|
46
|
-
before :all do
|
47
|
-
@connection.put @path, load_attachment("girl.jpg")
|
48
|
-
end
|
49
|
-
|
50
|
-
it "return true if the file has been uploaded" do
|
51
|
-
expect(@connection.exists?(@path)).to be_truthy
|
52
|
-
end
|
53
|
-
|
54
|
-
it "return false if the specified file didn't exist" do
|
55
|
-
@connection.delete @path
|
56
|
-
expect(@connection.exists?(@path)).to be_falsey
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
data/spec/attachments/girl.jpg
DELETED
Binary file
|
data/spec/attachments/masu.pdf
DELETED
Binary file
|
data/spec/fixtures/schema.rb
DELETED
@@ -1,76 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require "open-uri"
|
3
|
-
require 'net/http'
|
4
|
-
require 'support/post'
|
5
|
-
|
6
|
-
describe Paperclip::Storage::Aliyun do
|
7
|
-
before :all do
|
8
|
-
@file = load_attachment('girl.jpg')
|
9
|
-
@post = Post.create attachment: @file
|
10
|
-
end
|
11
|
-
|
12
|
-
after :all do
|
13
|
-
if @post && @post.respond_to?(:id)
|
14
|
-
@post.destroy!
|
15
|
-
end
|
16
|
-
|
17
|
-
@file.close
|
18
|
-
end
|
19
|
-
|
20
|
-
describe "#flush_writes" do
|
21
|
-
it "uploads the attachment to Aliyun" do
|
22
|
-
response = open(@post.attachment.url)
|
23
|
-
expect(response).to be_truthy
|
24
|
-
end
|
25
|
-
|
26
|
-
it "get uploaded file from Aliyun" do
|
27
|
-
attachment = open @post.attachment.url
|
28
|
-
expect(attachment.size).to eq(@file.size)
|
29
|
-
end
|
30
|
-
|
31
|
-
it "set content type according to the original file" do
|
32
|
-
attachment = load_attachment('masu.pdf')
|
33
|
-
post = Post.create attachment: attachment
|
34
|
-
headers = RestClient.head(post.attachment.url).headers
|
35
|
-
expect(headers[:content_type]).to eq('application/pdf')
|
36
|
-
|
37
|
-
post.destroy
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
describe "#exists?" do
|
42
|
-
it "returns true if the file exists on Aliyun" do
|
43
|
-
expect(@post.attachment).to exist
|
44
|
-
end
|
45
|
-
|
46
|
-
it "returns false if the file doesn't exist on Aliyun" do
|
47
|
-
post = Post.new attachment: @file
|
48
|
-
expect(post.attachment).not_to exist
|
49
|
-
end
|
50
|
-
|
51
|
-
it "not raise exception when attachment not saved" do
|
52
|
-
post = Post.create
|
53
|
-
expect{post.attachment.exists?}.not_to raise_error
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
describe "#copy_to_local_file" do
|
58
|
-
it "copies file from Aliyun to a local file" do
|
59
|
-
destination = File.join(Bundler.root, "tmp/photo.jpg")
|
60
|
-
@post.attachment.copy_to_local_file(:original, destination)
|
61
|
-
expect(File.exists?(destination)).to be_truthy
|
62
|
-
|
63
|
-
File.delete destination
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
describe "#flush_deletes" do
|
68
|
-
it "deletes the attachment from Aliyun" do
|
69
|
-
attachment_url = @post.attachment.url
|
70
|
-
@post.destroy
|
71
|
-
|
72
|
-
response_code = Net::HTTP.get_response(URI.parse(attachment_url)).code
|
73
|
-
expect(response_code).to eq("404")
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
require 'pry'
|
2
|
-
require 'pry-nav'
|
3
|
-
require 'paperclip-storage-aliyun'
|
4
|
-
|
5
|
-
Dir[File.join(Bundler.root, "spec/support/**/*.rb")].each &method(:require)
|
6
|
-
|
7
|
-
include Paperclip::Storage::Aliyun
|
8
|
-
def file_host
|
9
|
-
oss_connection.fetch_file_host
|
10
|
-
end
|
11
|
-
|
12
|
-
OSS_CONNECTION_OPTIONS = {
|
13
|
-
access_id: '3VL9XMho8iCuslj8',
|
14
|
-
access_key: 'VAUI2q7Tc6yTf1jr3kBsEUzZ84gEa2',
|
15
|
-
bucket: 'martin-test',
|
16
|
-
data_center: 'hangzhou',
|
17
|
-
internal: false
|
18
|
-
# host: nil
|
19
|
-
}
|
20
|
-
|
21
|
-
# paperclip初始化设置
|
22
|
-
Paperclip::Attachment.default_options[:storage] = :aliyun
|
23
|
-
Paperclip::Attachment.default_options[:path] = 'public/system/:class/:attachment/:id_partition/:style/:filename'
|
24
|
-
Paperclip::Attachment.default_options[:aliyun] = OSS_CONNECTION_OPTIONS
|
25
|
-
Paperclip::Attachment.default_options[:url] = "http://#{file_host}/public/system/:class/:attachment/:id_partition/:style/:filename"
|
26
|
-
|
27
|
-
def load_attachment(file_name)
|
28
|
-
File.open (File.expand_path "attachments/#{file_name}", File.dirname(__FILE__)), 'rb'
|
29
|
-
end
|
data/spec/support/post.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
require "active_record"
|
2
|
-
require "nulldb"
|
3
|
-
require "paperclip"
|
4
|
-
|
5
|
-
class Post < ActiveRecord::Base
|
6
|
-
include Paperclip::Glue
|
7
|
-
|
8
|
-
has_attached_file :attachment
|
9
|
-
do_not_validate_attachment_file_type :attachment
|
10
|
-
end
|
11
|
-
|
12
|
-
RSpec.configure do |config|
|
13
|
-
config.before(:all) do
|
14
|
-
FileUtils.mkdir_p File.join(Bundler.root, "tmp")
|
15
|
-
ActiveRecord::Base.establish_connection(
|
16
|
-
adapter: "nulldb",
|
17
|
-
schema: File.join(Bundler.root, "spec/fixtures/schema.rb"),
|
18
|
-
)
|
19
|
-
end
|
20
|
-
|
21
|
-
config.after(:all) do
|
22
|
-
ActiveRecord::Base.remove_connection
|
23
|
-
end
|
24
|
-
end
|