paperclip-storage-aliyun 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|