paperclip-storage-aliyun 0.0.3 → 0.1.5
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 +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
|