kt-paperclip-aliyun 1.0.0
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 +7 -0
- data/CHANGELOG.md +53 -0
- data/README.md +91 -0
- data/lib/aliyun/connection.rb +222 -0
- data/lib/aliyun/data_center.rb +43 -0
- data/lib/aliyun/errors.rb +4 -0
- data/lib/kt-paperclip-aliyun.rb +4 -0
- data/lib/paperclip/storage/aliyun.rb +80 -0
- metadata +83 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: c378e96208b951136a4aa554ea68e7e17c0763bdb9ce60f085047cf2bead67d5
|
|
4
|
+
data.tar.gz: 026302ed6206ca9f4c2b380ec5512bfc6fedb0e493a38e9057f8fcea54b624ba
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: dff31b7e8579232688b134c1dd5445b112130eba06d551f1f85447d19e170d6505ffb2fa77c11357f0653922b333cce689304d17b8861c43165ff7e2acea18e0
|
|
7
|
+
data.tar.gz: 5e45e9e02e5ba491ed8e20856837e7b20e1cf521ad273f71ac941ea01b3494ae42b040a175dfe29b3f5858873d3c7eb870913914778de815ff856ea87863d318
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.0] - 2025-12-09
|
|
9
|
+
|
|
10
|
+
### BREAKING CHANGES
|
|
11
|
+
- **Gem renamed from `paperclip-storage-aliyun` to `kt-paperclip-aliyun`**
|
|
12
|
+
- Main require file changed from `paperclip-storage-aliyun` to `kt-paperclip-aliyun`
|
|
13
|
+
- Update your Gemfile: `gem 'kt-paperclip-aliyun'` instead of `gem 'paperclip-storage-aliyun'`
|
|
14
|
+
- Update your requires: `require 'kt-paperclip-aliyun'` instead of `require 'paperclip-storage-aliyun'`
|
|
15
|
+
- **Repository ownership transferred to 7a6163**
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- Ruby 3.0+ support (tested on Ruby 2.7, 3.0, 3.1, 3.2, 3.3)
|
|
19
|
+
- GitHub Actions CI/CD workflow replacing Travis CI
|
|
20
|
+
- CodeCov integration for code coverage tracking
|
|
21
|
+
- WebMock integration for testing without real Aliyun credentials
|
|
22
|
+
- Comprehensive test suite improvements
|
|
23
|
+
- Gem metadata for better RubyGems.org integration
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
- **BREAKING**: Minimum Ruby version is now 2.7.0 (was 2.6.0)
|
|
27
|
+
- Updated `rest-client` dependency from `>= 1.6.7` to `>= 2.0.0` for Ruby 3 compatibility
|
|
28
|
+
- Updated test dependencies:
|
|
29
|
+
- `activerecord` from `~> 4.0.0` to `>= 5.0`
|
|
30
|
+
- `rspec` from `~> 3.3.0` to `~> 3.10`
|
|
31
|
+
- `rubocop` from `~> 0.34.2` to `~> 1.0`
|
|
32
|
+
- Gem source changed from `gems.ruby-china.com` to `rubygems.org`
|
|
33
|
+
- Replaced deprecated `URI.encode` with `URI::DEFAULT_PARSER.escape` for Ruby 3.0+ compatibility
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
- Fixed Ruby 3.0+ compatibility issues with URI encoding
|
|
37
|
+
- Fixed all test failures by improving test isolation and mock setup
|
|
38
|
+
- Renamed test fixture file from Chinese characters to `chinese-name.jpg` for better compatibility
|
|
39
|
+
|
|
40
|
+
### Deprecated
|
|
41
|
+
- Travis CI configuration (replaced with GitHub Actions)
|
|
42
|
+
|
|
43
|
+
## [0.1.6] - 2020-04-20
|
|
44
|
+
|
|
45
|
+
### Fixed
|
|
46
|
+
- Fixed argument pollution in #get_endpoint
|
|
47
|
+
|
|
48
|
+
## Previous versions
|
|
49
|
+
|
|
50
|
+
See git history for changes in versions prior to 1.0.0.
|
|
51
|
+
|
|
52
|
+
[1.0.0]: https://github.com/7a6163/kt-paperclip-aliyun/compare/v0.1.6...v1.0.0
|
|
53
|
+
[0.1.6]: https://github.com/Martin91/paperclip-storage-aliyun/compare/v0.1.5...v0.1.6
|
data/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
[](https://github.com/7a6163/kt-paperclip-aliyun/actions/workflows/ruby.yml)
|
|
2
|
+
[](https://codecov.io/gh/7a6163/kt-paperclip-aliyun)
|
|
3
|
+
[](https://badge.fury.io/rb/kt-paperclip-aliyun)
|
|
4
|
+
|
|
5
|
+
Aliyun Open Storage Service for Paperclip
|
|
6
|
+
===
|
|
7
|
+
This gem implement the support for [Aliyun open storage service(OSS)](http://oss.aliyun.com) to [Paperclip](https://github.com/thoughtbot/paperclip).
|
|
8
|
+
|
|
9
|
+
#### Ruby Version Support
|
|
10
|
+
- Ruby 2.7+
|
|
11
|
+
- Ruby 3.0+
|
|
12
|
+
- Ruby 3.1+
|
|
13
|
+
- Ruby 3.2+
|
|
14
|
+
- Ruby 3.3+
|
|
15
|
+
|
|
16
|
+
#### Installation
|
|
17
|
+
```shell
|
|
18
|
+
gem install kt-paperclip-aliyun
|
|
19
|
+
```
|
|
20
|
+
Or, if you are using a bundler, you can append the following line into your **Gemfile**:
|
|
21
|
+
```ruby
|
|
22
|
+
gem 'kt-paperclip-aliyun'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
#### Configuration
|
|
26
|
+
In order to make all the things work, you should do some important configurations through a initializer:
|
|
27
|
+
|
|
28
|
+
If you are developing a Rails application, you can append a new initializer like:
|
|
29
|
+
```ruby
|
|
30
|
+
# [rails_root]/config/initializers/paperclip-aliyun-configuration.rb
|
|
31
|
+
Paperclip::Attachment.default_options[:aliyun] = {
|
|
32
|
+
access_id: '3VL9XMho8iCushj8',
|
|
33
|
+
access_key: 'VAUI2q7Tc6yTh1jr3kBsEUzZ84gEa2',
|
|
34
|
+
bucket: 'xx-test',
|
|
35
|
+
data_center: 'cn-hangzhou',
|
|
36
|
+
internal: false,
|
|
37
|
+
protocol: 'https',
|
|
38
|
+
protocol_relative_url: false
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Then, in the model which defines the attachment, specify your storage and other options, for example:
|
|
43
|
+
```ruby
|
|
44
|
+
# [rails_root]/app/models/image.rb
|
|
45
|
+
class Image < ActiveRecord::Base
|
|
46
|
+
has_attached_file :attachment, {
|
|
47
|
+
storage: :aliyun,
|
|
48
|
+
styles: { thumbnail: "60x60#"},
|
|
49
|
+
path: 'public/system/:class/:attachment/:id_partition/:style/:filename',
|
|
50
|
+
url: ':aliyun_upload_url'
|
|
51
|
+
}
|
|
52
|
+
end
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Similar to Paperclip::Storage::S3, there are four options for the url by now:
|
|
56
|
+
- `:aliyun_upload_url` : the url based on the options you give
|
|
57
|
+
- `:aliyun_internal_url` : the internal url, no matter what `options[:aliyun][:internal]` is
|
|
58
|
+
- `:aliyun_external_url` : the external url, no matter what `options[:aliyun][:internal]` is
|
|
59
|
+
- `:aliyun_alias_url` : the alias url based on the `host_alias` you give, typically used together with CDN
|
|
60
|
+
|
|
61
|
+
Please note the values above are all strings, not symbols. You could still make your own url if only you know what you are doing.
|
|
62
|
+
|
|
63
|
+
#### Data Centers
|
|
64
|
+
A list of available regions can be found at [https://intl.aliyun.com/help/doc-detail/31837.htm](https://intl.aliyun.com/help/doc-detail/31837.htm).
|
|
65
|
+
You can use the "Region Expression" column value as it is for the data center, or you can remove the "oss-" prefix. For example: `oss-cn-hangzhou` and `cn-hangzhou` are both valid options.
|
|
66
|
+
|
|
67
|
+
#### Development & Testing
|
|
68
|
+
|
|
69
|
+
Run tests with:
|
|
70
|
+
```shell
|
|
71
|
+
bundle exec rspec
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
By default, tests run with mocked Aliyun OSS requests (using WebMock). No real credentials are required.
|
|
75
|
+
|
|
76
|
+
If you want to run tests against real Aliyun OSS:
|
|
77
|
+
```shell
|
|
78
|
+
export OSS_ACCESS_ID='your_access_key_id'
|
|
79
|
+
export OSS_ACCESS_KEY='your_access_key_secret'
|
|
80
|
+
bundle exec rspec
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
#### Contributing
|
|
84
|
+
|
|
85
|
+
1. Fork the repository
|
|
86
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
87
|
+
3. Make your changes and add tests
|
|
88
|
+
4. Run the test suite (`bundle exec rspec`)
|
|
89
|
+
5. Commit your changes (`git commit -am 'Add some feature'`)
|
|
90
|
+
6. Push to the branch (`git push origin my-new-feature`)
|
|
91
|
+
7. Create a Pull Request
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
require 'openssl'
|
|
2
|
+
require 'digest/md5'
|
|
3
|
+
require 'rest-client'
|
|
4
|
+
require 'base64'
|
|
5
|
+
require 'uri'
|
|
6
|
+
require 'aliyun/data_center'
|
|
7
|
+
|
|
8
|
+
module Aliyun
|
|
9
|
+
class Connection
|
|
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
|
+
|
|
25
|
+
# Initialize the OSS connection
|
|
26
|
+
#
|
|
27
|
+
# @param [Hash] An options to specify connection details
|
|
28
|
+
# @option access_id [String] used to set "Authorization" request header
|
|
29
|
+
# @option access_key [String] the access key
|
|
30
|
+
# @option bucket [String] bucket used to access
|
|
31
|
+
# @option data_center [String] available data center name, e.g. 'cn-hangzhou'
|
|
32
|
+
# @option internal [true, false] if the service should be accessed through internal network
|
|
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
|
|
36
|
+
# @note both access_id and acces_key are related to authorization algorithm:
|
|
37
|
+
# https://docs.aliyun.com/#/pub/oss/api-reference/access-control&signature-header
|
|
38
|
+
def initialize(options = {})
|
|
39
|
+
@aliyun_access_id = options[:access_id]
|
|
40
|
+
@aliyun_access_key = options[:access_key]
|
|
41
|
+
@aliyun_bucket = options[:bucket]
|
|
42
|
+
@aliyun_protocol_relative_url = !!options[:protocol_relative_url]
|
|
43
|
+
@aliyun_protocol = options[:protocol] || 'http'
|
|
44
|
+
|
|
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
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Return the meta informations for a file specified by the path
|
|
52
|
+
# https://docs.aliyun.com/#/pub/oss/api-reference/object&HeadObject
|
|
53
|
+
#
|
|
54
|
+
# @param path [String] the path of file storaged in Aliyun OSS
|
|
55
|
+
# @return [Hash] the meta data of the file
|
|
56
|
+
# @note the example headers will be like:
|
|
57
|
+
#
|
|
58
|
+
# {
|
|
59
|
+
# {:date=>"Sun, 02 Aug 2015 02:42:45 GMT",
|
|
60
|
+
# :content_type=>"image/jpg",
|
|
61
|
+
# :content_length=>"125198",
|
|
62
|
+
# :connection=>"close",
|
|
63
|
+
# :accept_ranges=>"bytes",
|
|
64
|
+
# :etag=>"\"336262A42E5B99AFF5B8BC66611FC156\"",
|
|
65
|
+
# :last_modified=>"Sun, 01 Dec 2013 16:39:57 GMT",
|
|
66
|
+
# :server=>"AliyunOSS",
|
|
67
|
+
# :x_oss_object_type=>"Normal",
|
|
68
|
+
# :x_oss_request_id=>"55BD83A5D4C05BDFF4A329E0"}}
|
|
69
|
+
#
|
|
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
|
+
}
|
|
79
|
+
url = path_to_url(path)
|
|
80
|
+
RestClient.head(url, headers).headers
|
|
81
|
+
rescue RestClient::ResourceNotFound
|
|
82
|
+
{}
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Upload File to Aliyun OSS
|
|
86
|
+
# https://docs.aliyun.com/#/pub/oss/api-reference/object&PutObject
|
|
87
|
+
#
|
|
88
|
+
# @param path [String] the target storing path on the oss
|
|
89
|
+
# @param file [File] an instance of File represents a file to be uploaded
|
|
90
|
+
# @param options [Hash]
|
|
91
|
+
# - content_type - MimeType value for the file, default is "image/jpg"
|
|
92
|
+
#
|
|
93
|
+
# @return [String] The downloadable url of the uploaded file
|
|
94
|
+
# @return [nil] if the uploading failed
|
|
95
|
+
def put(path, file, options = {})
|
|
96
|
+
path = format_path(path)
|
|
97
|
+
bucket_path = get_bucket_path(path)
|
|
98
|
+
content_md5 = Digest::MD5.file(file).base64digest
|
|
99
|
+
content_type = options[:content_type] || 'image/jpg'
|
|
100
|
+
date = gmtdate
|
|
101
|
+
url = path_to_url(path)
|
|
102
|
+
auth_sign = sign('PUT', bucket_path, content_md5, content_type, date)
|
|
103
|
+
headers = {
|
|
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'
|
|
111
|
+
}
|
|
112
|
+
response = RestClient.put(url, file, headers)
|
|
113
|
+
response.code == 200 ? path_to_url(path) : nil
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Delete a file from the OSS
|
|
117
|
+
# https://docs.aliyun.com/#/pub/oss/api-reference/object&DeleteObject
|
|
118
|
+
#
|
|
119
|
+
# @param path [String] the path to retrieve the file on remote storage
|
|
120
|
+
# @return [String] the expired url to the file, if the file deleted successfully
|
|
121
|
+
# @return [nil] if the delete operation failed
|
|
122
|
+
def delete(path)
|
|
123
|
+
path = format_path(path)
|
|
124
|
+
bucket_path = get_bucket_path(path)
|
|
125
|
+
date = gmtdate
|
|
126
|
+
headers = {
|
|
127
|
+
'Host' => @aliyun_upload_host,
|
|
128
|
+
'Date' => date,
|
|
129
|
+
'Authorization' => sign('DELETE', bucket_path, '', '', date)
|
|
130
|
+
}
|
|
131
|
+
url = path_to_url(path)
|
|
132
|
+
response = RestClient.delete(url, headers)
|
|
133
|
+
response.code == 204 ? url : nil
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Download the file from OSS
|
|
137
|
+
# https://docs.aliyun.com/#/pub/oss/api-reference/object&GetObject
|
|
138
|
+
#
|
|
139
|
+
# @param path [String] the path to retrieve the file on remote storage
|
|
140
|
+
# @return [?] the file content consist of bytes
|
|
141
|
+
def get(path)
|
|
142
|
+
path = format_path(path)
|
|
143
|
+
bucket_path = get_bucket_path(path)
|
|
144
|
+
date = gmtdate
|
|
145
|
+
headers = {
|
|
146
|
+
'Host' => @aliyun_upload_host,
|
|
147
|
+
'Date' => date,
|
|
148
|
+
'Authorization' => sign('GET', bucket_path, '', '', date)
|
|
149
|
+
}
|
|
150
|
+
url = path_to_url(path)
|
|
151
|
+
response = RestClient.get(url, headers)
|
|
152
|
+
response.body
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Determine if the file exists on the OSS
|
|
156
|
+
# https://docs.aliyun.com/#/pub/oss/api-reference/object&HeadObject
|
|
157
|
+
#
|
|
158
|
+
# @param path [String] the path to retrieve the file on remote storage
|
|
159
|
+
# @return [true] if file exists
|
|
160
|
+
# @return [false] if file could not be found
|
|
161
|
+
def exists?(path)
|
|
162
|
+
head(path).empty? ? false : true
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# The GMT format time referenced from HTTP 1.1
|
|
166
|
+
# https://docs.aliyun.com/#/pub/oss/api-reference/public-header
|
|
167
|
+
#
|
|
168
|
+
# @return [String] a string represents the formated time, e.g. "Wed, 05 Sep. 2012 23:00:00 GMT"
|
|
169
|
+
def gmtdate
|
|
170
|
+
Time.now.gmtime.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# remove leading slashes in the path
|
|
174
|
+
#
|
|
175
|
+
# @param path [String] the path to retrieve the file on remote storage
|
|
176
|
+
# @return [String] the new string after removing leading slashed
|
|
177
|
+
def format_path(path)
|
|
178
|
+
path.blank? ? '' : path.gsub(%r{^/+}, '')
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# A path consis of the bucket name and file name
|
|
182
|
+
# https://docs.aliyun.com/#/pub/oss/api-reference/access-control&signature-header
|
|
183
|
+
#
|
|
184
|
+
# @param path [String] the path to retrieve the file on remote storage
|
|
185
|
+
# @return [String] the expected bucket path, e.g. "test-bucket/oss-api.pdf"
|
|
186
|
+
def get_bucket_path(path)
|
|
187
|
+
[@aliyun_bucket, path].join('/')
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# The full path contains host name to the file
|
|
191
|
+
#
|
|
192
|
+
# @param path [String] the path to retrieve the file on remote storage
|
|
193
|
+
# @return [String] the expected full path, e.g. "http://martin-test.oss-cn-hangzhou.aliyuncs.com/oss-api.pdf"
|
|
194
|
+
def path_to_url(path)
|
|
195
|
+
url = path =~ %r{^https?://} ? path : "http://#{aliyun_upload_host}/#{path}"
|
|
196
|
+
# URI.encode is deprecated in Ruby 2.7 and removed in Ruby 3.0
|
|
197
|
+
# Use URI::DEFAULT_PARSER.escape for Ruby 3 compatibility
|
|
198
|
+
URI::DEFAULT_PARSER.escape(url)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
private
|
|
202
|
+
|
|
203
|
+
# The signature algorithm
|
|
204
|
+
# https://docs.aliyun.com/#/pub/oss/api-reference/access-control&signature-header
|
|
205
|
+
#
|
|
206
|
+
# @param verb [String] the request verb, e.g. "GET" or "DELETE"
|
|
207
|
+
# @param content_md5 [String] the md5 value for the content to be uploaded
|
|
208
|
+
# @param content_type [String] the content type of the file, e.g. "application/pdf"
|
|
209
|
+
# @param date [String] the GMT formatted date string
|
|
210
|
+
def sign(verb, path, content_md5, content_type, date)
|
|
211
|
+
canonicalized_oss_headers = ''
|
|
212
|
+
canonicalized_resource = "/#{path}"
|
|
213
|
+
string_to_sign = [
|
|
214
|
+
verb, content_md5, content_type, date,
|
|
215
|
+
canonicalized_oss_headers + canonicalized_resource
|
|
216
|
+
].join("\n")
|
|
217
|
+
digest = OpenSSL::Digest.new('sha1')
|
|
218
|
+
h = OpenSSL::HMAC.digest(digest, @aliyun_access_key, string_to_sign)
|
|
219
|
+
"OSS #{@aliyun_access_id}:#{Base64.encode64(h)}"
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'aliyun/errors'
|
|
2
|
+
|
|
3
|
+
module Aliyun
|
|
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-northeast-2
|
|
25
|
+
oss-ap-south-1
|
|
26
|
+
oss-eu-central-1
|
|
27
|
+
oss-eu-west-1
|
|
28
|
+
oss-me-east-1
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
def get_endpoint(options)
|
|
32
|
+
data_center = options[:data_center]
|
|
33
|
+
|
|
34
|
+
data_center = 'oss-' + data_center unless data_center.match(/^oss/)
|
|
35
|
+
|
|
36
|
+
unless AVAILABLE_DATA_CENTERS.include?(data_center)
|
|
37
|
+
raise InvalildDataCenter, "Unsupported Data Center #{options[:data_center]} Detected"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
"#{data_center}#{options[:internal] ? '-internal' : ''}.aliyuncs.com"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
module Paperclip
|
|
2
|
+
module Storage
|
|
3
|
+
module Aliyun
|
|
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
|
|
17
|
+
end
|
|
18
|
+
|
|
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
|
|
30
|
+
|
|
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
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def flush_writes #:nodoc:
|
|
48
|
+
@queued_for_write.each do |style_name, file|
|
|
49
|
+
oss_connection.put path(style_name), (File.new file.path), content_type: file.content_type
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
after_flush_writes
|
|
53
|
+
|
|
54
|
+
@queued_for_write = {}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def flush_deletes #:nodoc:
|
|
58
|
+
@queued_for_delete.each do |path|
|
|
59
|
+
oss_connection.delete path
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
@queued_for_delete = []
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def copy_to_local_file(style, local_dest_path)
|
|
66
|
+
log("copying #{path(style)} to local file #{local_dest_path}")
|
|
67
|
+
local_file = ::File.open(local_dest_path, 'wb')
|
|
68
|
+
remote_file_str = oss_connection.get path(style)
|
|
69
|
+
local_file.write(remote_file_str)
|
|
70
|
+
local_file.close
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def oss_connection
|
|
74
|
+
@oss_connection ||= ::Aliyun::Connection.new(
|
|
75
|
+
Paperclip::Attachment.default_options[:aliyun].merge(@aliyun_options)
|
|
76
|
+
)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: kt-paperclip-aliyun
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Martin Hong
|
|
8
|
+
- Aidi Stan
|
|
9
|
+
- 7a6163
|
|
10
|
+
autorequire:
|
|
11
|
+
bindir: bin
|
|
12
|
+
cert_chain: []
|
|
13
|
+
date: 2025-12-09 00:00:00.000000000 Z
|
|
14
|
+
dependencies:
|
|
15
|
+
- !ruby/object:Gem::Dependency
|
|
16
|
+
name: kt-paperclip
|
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
|
18
|
+
requirements:
|
|
19
|
+
- - ">="
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: 3.5.2
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
requirements:
|
|
26
|
+
- - ">="
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
version: 3.5.2
|
|
29
|
+
- !ruby/object:Gem::Dependency
|
|
30
|
+
name: rest-client
|
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
|
32
|
+
requirements:
|
|
33
|
+
- - ">="
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: 2.0.0
|
|
36
|
+
type: :runtime
|
|
37
|
+
prerelease: false
|
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
39
|
+
requirements:
|
|
40
|
+
- - ">="
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: 2.0.0
|
|
43
|
+
description: Provides Aliyun OSS (Object Storage Service) storage backend for kt-paperclip
|
|
44
|
+
gem
|
|
45
|
+
email: hongzeqin@gmail.com
|
|
46
|
+
executables: []
|
|
47
|
+
extensions: []
|
|
48
|
+
extra_rdoc_files: []
|
|
49
|
+
files:
|
|
50
|
+
- CHANGELOG.md
|
|
51
|
+
- README.md
|
|
52
|
+
- lib/aliyun/connection.rb
|
|
53
|
+
- lib/aliyun/data_center.rb
|
|
54
|
+
- lib/aliyun/errors.rb
|
|
55
|
+
- lib/kt-paperclip-aliyun.rb
|
|
56
|
+
- lib/paperclip/storage/aliyun.rb
|
|
57
|
+
homepage: https://github.com/7a6163/kt-paperclip-aliyun
|
|
58
|
+
licenses:
|
|
59
|
+
- MIT
|
|
60
|
+
metadata:
|
|
61
|
+
bug_tracker_uri: https://github.com/7a6163/kt-paperclip-aliyun/issues
|
|
62
|
+
changelog_uri: https://github.com/7a6163/kt-paperclip-aliyun/blob/master/CHANGELOG.md
|
|
63
|
+
source_code_uri: https://github.com/7a6163/kt-paperclip-aliyun
|
|
64
|
+
post_install_message:
|
|
65
|
+
rdoc_options: []
|
|
66
|
+
require_paths:
|
|
67
|
+
- lib
|
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
69
|
+
requirements:
|
|
70
|
+
- - ">="
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: 2.7.0
|
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
74
|
+
requirements:
|
|
75
|
+
- - ">="
|
|
76
|
+
- !ruby/object:Gem::Version
|
|
77
|
+
version: '0'
|
|
78
|
+
requirements: []
|
|
79
|
+
rubygems_version: 3.5.22
|
|
80
|
+
signing_key:
|
|
81
|
+
specification_version: 4
|
|
82
|
+
summary: Aliyun OSS storage adapter for kt-paperclip
|
|
83
|
+
test_files: []
|