paperclip-storage-aliyun 0.0.2 → 0.0.3
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 -13
- data/.gitignore +3 -0
- data/.rspec +1 -0
- data/Gemfile +15 -0
- data/README.md +44 -0
- data/lib/aliyun/connection.rb +203 -0
- data/lib/aliyun/data_center.rb +20 -0
- data/lib/aliyun/errors.rb +4 -0
- data/lib/paperclip-storage-aliyun.rb +4 -0
- data/lib/paperclip/storage/aliyun.rb +46 -0
- data/paperclip-storage-aliyun.gemspec +16 -0
- data/spec/aliyun_spec.rb +59 -0
- data/spec/attachments/girl.jpg +0 -0
- data/spec/attachments/masu.pdf +0 -0
- data/spec/fixtures/schema.rb +8 -0
- data/spec/lib/paperclip/storage/aliyun_spec.rb +76 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/support/post.rb +24 -0
- data/spec/support/rails.rb +10 -0
- metadata +29 -10
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
ZGZkN2MzZDM4OTI2MjdkMjFjNzY2NDQ0MDE0OGY0YTk4YjZiODJkOA==
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 770e2aba77af7013237abcb823fec49c244d15d3
|
4
|
+
data.tar.gz: 199af51f6159ab6b0138eab907b7dd7286b2fd2a
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
NDIyZTA5ZDFhMmQ0YWQzNmZlNGUxNDEwMzU1NDhmOGUyZjM1NDhiMjJhNTgw
|
11
|
-
OGRhOWU5MWI5Mjk4MTZjZDNjNmNhMGNkNzdiNjM3ODI3NTM2NDI=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
ZGFiNmFmNTQ2MjNiMDMxZjExZWU2NmU4ZWQxMzdjYTY5ZDQ1NWZlZDFjYmEz
|
14
|
-
MjNmOWM1NTBmNzViOGRmY2I1ZmY0MjY5NmJmMzYwYjJmMTI4YzEyZDhjYmU5
|
15
|
-
MmFiZGQzMGZkMTVlNjEyMjA3ZWM4NjM4YWJjMTAyZDQxN2E0OWU=
|
6
|
+
metadata.gz: 418741aa19eeb9400e7cae5eb9448c671a99688632cb19a9e7c6c8c43a269297c608c2ef53dc84881ff437833ecdaa826db8f7ece5c0052ac453aa61a9703422
|
7
|
+
data.tar.gz: 27af1cb2c3f924a9f1d736206a57c648d82a3b10b03c3e1ed373dcc8c9d48050f19344c1d7bd22aafe8a021919284c71dbb8fb53170d35142d8a0fddaec3059f
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
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
ADDED
@@ -0,0 +1,44 @@
|
|
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**先生的开源代码。
|
@@ -0,0 +1,203 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'openssl'
|
4
|
+
require 'digest/md5'
|
5
|
+
require "rest-client"
|
6
|
+
require "base64"
|
7
|
+
require 'uri'
|
8
|
+
require 'aliyun/data_center'
|
9
|
+
|
10
|
+
module Aliyun
|
11
|
+
class Connection
|
12
|
+
include DataCenter
|
13
|
+
# Initialize the OSS connection
|
14
|
+
#
|
15
|
+
# @param [Hash] An options to specify connection details
|
16
|
+
# @option access_id [String] used to set "Authorization" request header
|
17
|
+
# @option access_key [String] the access key
|
18
|
+
# @option bucket [String] bucket used to access
|
19
|
+
# @option data_center [String] available data center name, e.g. 'hangzhou'
|
20
|
+
# @option internal [true, false] if the service should be accessed through internal network
|
21
|
+
# @option host [String] force the host to a given value, only use it when this gem can not work expectly
|
22
|
+
# @note both access_id and acces_key are related to authorization algorithm:
|
23
|
+
# https://docs.aliyun.com/#/pub/oss/api-reference/access-control&signature-header
|
24
|
+
def initialize(options = {})
|
25
|
+
@aliyun_access_id = options[:access_id]
|
26
|
+
@aliyun_access_key = options[:access_key]
|
27
|
+
@aliyun_bucket = options[:bucket]
|
28
|
+
|
29
|
+
@endpoint = get_endpoint(options)
|
30
|
+
|
31
|
+
@aliyun_upload_host = "#{@aliyun_bucket}.#{@endpoint}"
|
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
|
41
|
+
end
|
42
|
+
|
43
|
+
# Return the meta informations for the a file specified by the path
|
44
|
+
# https://docs.aliyun.com/#/pub/oss/api-reference/object&HeadObject
|
45
|
+
#
|
46
|
+
# @param path [String] the path of file storaged in Aliyun OSS
|
47
|
+
# @return [Hash] the meta data of the file
|
48
|
+
# @note the example headers will be like:
|
49
|
+
#
|
50
|
+
# {
|
51
|
+
# {:date=>"Sun, 02 Aug 2015 02:42:45 GMT",
|
52
|
+
# :content_type=>"image/jpg",
|
53
|
+
# :content_length=>"125198",
|
54
|
+
# :connection=>"close",
|
55
|
+
# :accept_ranges=>"bytes",
|
56
|
+
# :etag=>"\"336262A42E5B99AFF5B8BC66611FC156\"",
|
57
|
+
# :last_modified=>"Sun, 01 Dec 2013 16:39:57 GMT",
|
58
|
+
# :server=>"AliyunOSS",
|
59
|
+
# :x_oss_object_type=>"Normal",
|
60
|
+
# :x_oss_request_id=>"55BD83A5D4C05BDFF4A329E0"}}
|
61
|
+
#
|
62
|
+
def head(path)
|
63
|
+
url = path_to_url(path)
|
64
|
+
RestClient.head(url).headers
|
65
|
+
rescue RestClient::ResourceNotFound
|
66
|
+
{}
|
67
|
+
end
|
68
|
+
|
69
|
+
# Upload File to Aliyun OSS
|
70
|
+
# https://docs.aliyun.com/#/pub/oss/api-reference/object&PutObject
|
71
|
+
#
|
72
|
+
# @param path [String] the target storing path on the oss
|
73
|
+
# @param file [File] an instance of File represents a file to be uploaded
|
74
|
+
# @param options [Hash]
|
75
|
+
# - content_type - MimeType value for the file, default is "image/jpg"
|
76
|
+
#
|
77
|
+
# @return [String] The downloadable url of the uploaded file
|
78
|
+
# @return [nil] if the uploading failed
|
79
|
+
def put(path, file, options={})
|
80
|
+
path = format_path(path)
|
81
|
+
bucket_path = get_bucket_path(path)
|
82
|
+
content_md5 = Digest::MD5.file(file)
|
83
|
+
content_type = options[:content_type] || "image/jpg"
|
84
|
+
date = gmtdate
|
85
|
+
url = path_to_url(path)
|
86
|
+
auth_sign = sign("PUT", bucket_path, content_md5, content_type, date)
|
87
|
+
headers = {
|
88
|
+
"Authorization" => auth_sign,
|
89
|
+
"Content-Type" => content_type,
|
90
|
+
"Content-Length" => file.size,
|
91
|
+
"Date" => date,
|
92
|
+
"Host" => @aliyun_upload_host,
|
93
|
+
"Expect" => "100-Continue"
|
94
|
+
}
|
95
|
+
response = RestClient.put(URI.encode(url), file, headers)
|
96
|
+
response.code == 200 ? path_to_url(path) : nil
|
97
|
+
end
|
98
|
+
|
99
|
+
# Delete a file from the OSS
|
100
|
+
# https://docs.aliyun.com/#/pub/oss/api-reference/object&DeleteObject
|
101
|
+
#
|
102
|
+
# @param path [String] the path to retrieve the file on remote storage
|
103
|
+
# @return [String] the expired url to the file, if the file deleted successfully
|
104
|
+
# @return [nil] if the delete operation failed
|
105
|
+
def delete(path)
|
106
|
+
path = format_path(path)
|
107
|
+
bucket_path = get_bucket_path(path)
|
108
|
+
date = gmtdate
|
109
|
+
headers = {
|
110
|
+
"Host" => @aliyun_upload_host,
|
111
|
+
"Date" => date,
|
112
|
+
"Authorization" => sign("DELETE", bucket_path, "", "" ,date)
|
113
|
+
}
|
114
|
+
url = path_to_url(path)
|
115
|
+
response = RestClient.delete(URI.encode(url), headers)
|
116
|
+
response.code == 204 ? url : nil
|
117
|
+
end
|
118
|
+
|
119
|
+
# Download the file from OSS
|
120
|
+
# https://docs.aliyun.com/#/pub/oss/api-reference/object&GetObject
|
121
|
+
#
|
122
|
+
# @param path [String] the path to retrieve the file on remote storage
|
123
|
+
# @return [?] the file content consist of bytes
|
124
|
+
def get(path)
|
125
|
+
path = format_path(path)
|
126
|
+
bucket_path = get_bucket_path(path)
|
127
|
+
date = gmtdate
|
128
|
+
headers = {
|
129
|
+
"Host" => @aliyun_upload_host,
|
130
|
+
"Date" => date,
|
131
|
+
"Authorization" => sign("GET", bucket_path, "", "" ,date)
|
132
|
+
}
|
133
|
+
url = path_to_url(path)
|
134
|
+
response = RestClient.get(URI.encode(url), headers)
|
135
|
+
response.body
|
136
|
+
end
|
137
|
+
|
138
|
+
# Determine if the file exists on the OSS
|
139
|
+
# https://docs.aliyun.com/#/pub/oss/api-reference/object&HeadObject
|
140
|
+
#
|
141
|
+
# @param path [String] the path to retrieve the file on remote storage
|
142
|
+
# @return [true] if file exists
|
143
|
+
# @return [false] if file could not be found
|
144
|
+
def exists?(path)
|
145
|
+
head(path).empty? ? false : true
|
146
|
+
end
|
147
|
+
|
148
|
+
# The GMT format time referenced from HTTP 1.1
|
149
|
+
# https://docs.aliyun.com/#/pub/oss/api-reference/public-header
|
150
|
+
#
|
151
|
+
# @return [String] a string represents the formated time, e.g. "Wed, 05 Sep. 2012 23:00:00 GMT"
|
152
|
+
def gmtdate
|
153
|
+
Time.now.gmtime.strftime("%a, %d %b %Y %H:%M:%S GMT")
|
154
|
+
end
|
155
|
+
|
156
|
+
# remove leading slashes in the path
|
157
|
+
#
|
158
|
+
# @param path [String] the path to retrieve the file on remote storage
|
159
|
+
# @return [String] the new string after removing leading slashed
|
160
|
+
def format_path(path)
|
161
|
+
return "" if path.blank?
|
162
|
+
path.gsub!(/^\/+/,"")
|
163
|
+
|
164
|
+
path
|
165
|
+
end
|
166
|
+
|
167
|
+
# A path consis of the bucket name and file name
|
168
|
+
# https://docs.aliyun.com/#/pub/oss/api-reference/access-control&signature-header
|
169
|
+
#
|
170
|
+
# @param path [String] the path to retrieve the file on remote storage
|
171
|
+
# @return [String] the expected bucket path, e.g. "test-bucket/oss-api.pdf"
|
172
|
+
def get_bucket_path(path)
|
173
|
+
[@aliyun_bucket,path].join("/")
|
174
|
+
end
|
175
|
+
|
176
|
+
# The full path contains host name to the file
|
177
|
+
#
|
178
|
+
# @param path [String] the path to retrieve the file on remote storage
|
179
|
+
# @return [String] the expected full path, e.g. "http://martin-test.oss-cn-hangzhou.aliyuncs.com/oss-api.pdf"
|
180
|
+
def path_to_url(path)
|
181
|
+
return path if path =~ /^https?:\/{2}/ # 已经是全路径
|
182
|
+
"http://#{fetch_file_host}/#{path}"
|
183
|
+
end
|
184
|
+
|
185
|
+
private
|
186
|
+
# The signature algorithm
|
187
|
+
# https://docs.aliyun.com/#/pub/oss/api-reference/access-control&signature-header
|
188
|
+
#
|
189
|
+
# @param verb [String] the request verb, e.g. "GET" or "DELETE"
|
190
|
+
# @param content_md5 [String] the md5 value for the content to be uploaded
|
191
|
+
# @param content_type [String] the content type of the file, e.g. "application/pdf"
|
192
|
+
# @param date [String] the GMT formatted date string
|
193
|
+
def sign(verb, path, content_md5 = '', content_type = '', date)
|
194
|
+
canonicalized_oss_headers = ''
|
195
|
+
canonicalized_resource = "/#{path}"
|
196
|
+
string_to_sign = "#{verb}\n\n#{content_type}\n#{date}\n#{canonicalized_oss_headers}#{canonicalized_resource}"
|
197
|
+
digest = OpenSSL::Digest.new('sha1')
|
198
|
+
h = OpenSSL::HMAC.digest(digest, @aliyun_access_key, string_to_sign)
|
199
|
+
h = Base64.encode64(h)
|
200
|
+
"OSS #{@aliyun_access_id}:#{h}"
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'aliyun/errors'
|
2
|
+
|
3
|
+
module Aliyun
|
4
|
+
module DataCenter
|
5
|
+
# https://docs.aliyun.com/#/pub/oss/product-documentation/domain-region
|
6
|
+
AVAILABLE_CHINA_DATA_CENTERS = %w(hangzhou qingdao beijing hongkong shenzhen shanghai)
|
7
|
+
AVAILABLE_US_DATA_CENTERS = %w(us-west-1)
|
8
|
+
|
9
|
+
def get_endpoint(options)
|
10
|
+
data_center = options[:data_center]
|
11
|
+
if (AVAILABLE_CHINA_DATA_CENTERS + AVAILABLE_US_DATA_CENTERS).exclude?(data_center)
|
12
|
+
raise InvalildDataCenter, "Unsupported Data Center #{data_center} Detected"
|
13
|
+
end
|
14
|
+
|
15
|
+
internal = options[:internal] ? "-internal" : ''
|
16
|
+
country = AVAILABLE_CHINA_DATA_CENTERS.include?(data_center) ? 'cn' : 'us'
|
17
|
+
"oss-#{country}-#{data_center}#{internal}.aliyuncs.com"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Paperclip
|
2
|
+
module Storage
|
3
|
+
module Aliyun
|
4
|
+
def self.extended(base)
|
5
|
+
end
|
6
|
+
|
7
|
+
def exists?(style = default_style)
|
8
|
+
return false unless path(style)
|
9
|
+
|
10
|
+
oss_connection.exists? path(style)
|
11
|
+
end
|
12
|
+
|
13
|
+
def flush_writes #:nodoc:
|
14
|
+
@queued_for_write.each do |style_name, file|
|
15
|
+
oss_connection.put path(style_name), (File.new file.path), content_type: file.content_type
|
16
|
+
end
|
17
|
+
|
18
|
+
after_flush_writes
|
19
|
+
|
20
|
+
@queued_for_write = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def flush_deletes #:nodoc:
|
24
|
+
@queued_for_delete.each do |path|
|
25
|
+
oss_connection.delete path
|
26
|
+
end
|
27
|
+
|
28
|
+
@queued_for_delete = []
|
29
|
+
end
|
30
|
+
|
31
|
+
def copy_to_local_file(style = default_style, local_dest_path)
|
32
|
+
log("copying #{path(style)} to local file #{local_dest_path}")
|
33
|
+
local_file = ::File.open(local_dest_path, 'wb')
|
34
|
+
remote_file_str = oss_connection.get path(style)
|
35
|
+
local_file.write(remote_file_str)
|
36
|
+
local_file.close
|
37
|
+
end
|
38
|
+
|
39
|
+
def oss_connection
|
40
|
+
return @oss_connection if @oss_connection
|
41
|
+
|
42
|
+
@oss_connection ||= ::Aliyun::Connection.new Paperclip::Attachment.default_options[:aliyun]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,16 @@
|
|
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
ADDED
@@ -0,0 +1,59 @@
|
|
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
|
Binary file
|
Binary file
|
@@ -0,0 +1,76 @@
|
|
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
ADDED
@@ -0,0 +1,29 @@
|
|
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
|
@@ -0,0 +1,24 @@
|
|
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
|
metadata
CHANGED
@@ -1,41 +1,41 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paperclip-storage-aliyun
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martin Hong
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-08-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: paperclip
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 3.5.2
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 3.5.2
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rest-client
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: 1.6.7
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 1.6.7
|
41
41
|
description: Extend a Aliyun OSS storage for paperclip
|
@@ -43,7 +43,25 @@ email: hongzeqin@gmail.com
|
|
43
43
|
executables: []
|
44
44
|
extensions: []
|
45
45
|
extra_rdoc_files: []
|
46
|
-
files:
|
46
|
+
files:
|
47
|
+
- ".gitignore"
|
48
|
+
- ".rspec"
|
49
|
+
- Gemfile
|
50
|
+
- README.md
|
51
|
+
- lib/aliyun/connection.rb
|
52
|
+
- lib/aliyun/data_center.rb
|
53
|
+
- lib/aliyun/errors.rb
|
54
|
+
- lib/paperclip-storage-aliyun.rb
|
55
|
+
- 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
|
47
65
|
homepage: https://github.com/Martin91/paperclip-storage-aliyun
|
48
66
|
licenses:
|
49
67
|
- MIT
|
@@ -54,18 +72,19 @@ require_paths:
|
|
54
72
|
- lib
|
55
73
|
required_ruby_version: !ruby/object:Gem::Requirement
|
56
74
|
requirements:
|
57
|
-
- -
|
75
|
+
- - ">="
|
58
76
|
- !ruby/object:Gem::Version
|
59
77
|
version: '0'
|
60
78
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
79
|
requirements:
|
62
|
-
- -
|
80
|
+
- - ">="
|
63
81
|
- !ruby/object:Gem::Version
|
64
82
|
version: '0'
|
65
83
|
requirements: []
|
66
84
|
rubyforge_project:
|
67
|
-
rubygems_version: 2.
|
85
|
+
rubygems_version: 2.4.8
|
68
86
|
signing_key:
|
69
87
|
specification_version: 4
|
70
88
|
summary: Extend a Aliyun OSS storage for paperclip
|
71
89
|
test_files: []
|
90
|
+
has_rdoc:
|