activestorage-aliyun-oss 0.1.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 +29 -0
- data/README.md +79 -0
- data/Rakefile +27 -0
- data/lib/active_storage/service/aliyun_service.rb +178 -0
- data/lib/active_storage_aliyun/railtie.rb +4 -0
- data/lib/active_storage_aliyun/version.rb +3 -0
- data/lib/activestorage-aliyun.rb +5 -0
- metadata +78 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 846fe944de46bf6c077edbb456ff894aa1fb19ba
|
4
|
+
data.tar.gz: 88447903a5ad8b3b62e5c57212b9b7448f292bdf
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: eccccb84caac1bd05abc4ddc1da341efe0eeefb842d78736e377147c1e18c9a7df3f34f4fa05f40e55f159631dc2ef8bf9d5ef610fadfdb8381337b888715bb0
|
7
|
+
data.tar.gz: c471d3b5025a4e4ea4c22b71fcd549bcd34d9e435f8ac6301e779cdaac7422b5f6f6bcadabeda4fdfd9b709b36f44fbc0b27e8011b6fd4a2a65c02c134875303
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
## 0.4.0
|
2
|
+
|
3
|
+
- Support present `params` to `service_url` to oss image url.
|
4
|
+
|
5
|
+
for example:
|
6
|
+
|
7
|
+
```rb
|
8
|
+
@photo.image.service_url(params: { 'x-oss-process' => "image/resize,h_100,w_100" })
|
9
|
+
```
|
10
|
+
|
11
|
+
## 0.3.0
|
12
|
+
|
13
|
+
- Add `mode` config for setup OSS ACL, `mode: "private"` will always output URL that have signature info.
|
14
|
+
- Support `disposition: :attachment` option for `service_url` method for download original filename.
|
15
|
+
|
16
|
+
## 0.2.0
|
17
|
+
|
18
|
+
- Add url_for_direct_upload support.
|
19
|
+
- Fix delete_prefixed error when path not exists.
|
20
|
+
|
21
|
+
## 0.1.1
|
22
|
+
|
23
|
+
- Fix streaming upload.
|
24
|
+
- Fix delete by prefixed.
|
25
|
+
- Add full test.
|
26
|
+
|
27
|
+
## 0.1.0
|
28
|
+
|
29
|
+
- First release.
|
data/README.md
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# ActiveStorage Aliyun Service
|
2
|
+
|
3
|
+
Wraps the Aliyun OSS as an Active Storage service.
|
4
|
+
|
5
|
+
|
6
|
+
[](https://badge.fury.io/rb/activestorage-aliyun) [](https://travis-ci.org/huacnlee/activestorage-aliyun) [](https://codeclimate.com/github/huacnlee/activestorage-aliyun) [](https://codecov.io/github/huacnlee/activestorage-aliyun?branch=master)
|
7
|
+
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'activestorage-aliyun'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
```bash
|
20
|
+
$ bundle
|
21
|
+
```
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
config/storage.yml
|
26
|
+
|
27
|
+
```rb
|
28
|
+
production:
|
29
|
+
service: Aliyun
|
30
|
+
access_key_id: "your-oss-access-key-id"
|
31
|
+
access_key_secret: "your-oss-access-key-secret"
|
32
|
+
bucket: "bucket-name"
|
33
|
+
endpoint: "https://oss-cn-beijing.aliyuncs.com"
|
34
|
+
# path prefix, default: /
|
35
|
+
path: "my-app-files"
|
36
|
+
# Bucket mode: [public, private], default: public
|
37
|
+
mode: "public"
|
38
|
+
```
|
39
|
+
|
40
|
+
### Use for image url
|
41
|
+
|
42
|
+
```erb
|
43
|
+
Orignial File URL:
|
44
|
+
|
45
|
+
<%= image_tag @photo.image.service_url %>
|
46
|
+
```
|
47
|
+
|
48
|
+
Thumb with OSS image service:
|
49
|
+
|
50
|
+
```rb
|
51
|
+
class Photo < ApplicationRecord
|
52
|
+
def image_thumb_url(process)
|
53
|
+
self.image.service_url(params: { "x-oss-process" => process })
|
54
|
+
end
|
55
|
+
end
|
56
|
+
```
|
57
|
+
|
58
|
+
And then:
|
59
|
+
|
60
|
+
```erb
|
61
|
+
<%= image_tag @photo.image.image_thumb_url("image/resize,h_100,w_100") %>
|
62
|
+
```
|
63
|
+
|
64
|
+
### Use for file download
|
65
|
+
|
66
|
+
If you want to get original filename (Include Chinese and other UTF-8 chars), for example: `演示文件 download.zip`, you need present `disposition: :attachment` option.
|
67
|
+
|
68
|
+
```erb
|
69
|
+
#
|
70
|
+
<%= image_tag @photo.image.service_url(disposition: :attachment) %>
|
71
|
+
```
|
72
|
+
|
73
|
+
## Contributing
|
74
|
+
|
75
|
+
Contribution directions go here.
|
76
|
+
|
77
|
+
## License
|
78
|
+
|
79
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'ActiveStorage Aliyun Service'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'bundler/gem_tasks'
|
18
|
+
|
19
|
+
require 'rake/testtask'
|
20
|
+
|
21
|
+
Rake::TestTask.new(:test) do |t|
|
22
|
+
t.libs << 'test'
|
23
|
+
t.pattern = 'test/**/*_test.rb'
|
24
|
+
t.verbose = false
|
25
|
+
end
|
26
|
+
|
27
|
+
task default: :test
|
@@ -0,0 +1,178 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "aliyun/oss"
|
3
|
+
|
4
|
+
module ActiveStorage
|
5
|
+
class Service::AliyunService < Service
|
6
|
+
def initialize(**config)
|
7
|
+
Aliyun::Common::Logging.set_log_file("/dev/null")
|
8
|
+
@config = config
|
9
|
+
end
|
10
|
+
|
11
|
+
def upload(key, io, checksum: nil)
|
12
|
+
instrument :upload, key: key, checksum: checksum do
|
13
|
+
bucket.put_object(path_for(key)) do |stream|
|
14
|
+
stream << io.read
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def download(key)
|
20
|
+
instrument :download, key: key do
|
21
|
+
chunk_buff = []
|
22
|
+
bucket.get_object(path_for(key)) do |chunk|
|
23
|
+
if block_given?
|
24
|
+
yield chunk
|
25
|
+
else
|
26
|
+
chunk_buff << chunk
|
27
|
+
end
|
28
|
+
end
|
29
|
+
chunk_buff.join("")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def download_chunk(key, range)
|
34
|
+
instrument :download_chunk, key: key, range: range do
|
35
|
+
uri = URI(url(key, expires_in: 30.seconds, filename: ActiveStorage::Filename.new(""), content_type: "application/octet-stream", disposition: "inline"))
|
36
|
+
|
37
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |client|
|
38
|
+
client.get(uri, "Range" => "bytes=#{range.begin}-#{range.exclude_end? ? range.end - 1 : range.end}").body
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def delete(key)
|
44
|
+
instrument :delete, key: key do
|
45
|
+
bucket.delete_object(path_for(key))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def delete_prefixed(prefix)
|
50
|
+
instrument :delete_prefixed, prefix: prefix do
|
51
|
+
files = bucket.list_objects(prefix: path_for(prefix))
|
52
|
+
return if files.blank?
|
53
|
+
keys = files.map(&:key)
|
54
|
+
return if keys.blank?
|
55
|
+
bucket.batch_delete_objects(keys, quiet: true)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def exist?(key)
|
60
|
+
instrument :exist, key: key do |payload|
|
61
|
+
bucket.object_exists?(path_for(key))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def url(key, expires_in:, filename: nil, content_type:, disposition:, params: {})
|
66
|
+
instrument :url, key: key do |payload|
|
67
|
+
sign = private_mode? || disposition == :attachment
|
68
|
+
filekey = path_for(filename.to_s)
|
69
|
+
|
70
|
+
if disposition == :attachment
|
71
|
+
params["response-content-type"] = content_type if content_type
|
72
|
+
unless filename.is_a?(ActiveStorage::Filename)
|
73
|
+
filename = ActiveStorage::Filename.new(filename)
|
74
|
+
end
|
75
|
+
|
76
|
+
params["response-content-disposition"] = content_disposition_with(type: disposition, filename: filename)
|
77
|
+
end
|
78
|
+
|
79
|
+
generated_url = object_url(filekey, sign: sign, expires_in: expires_in, params: params)
|
80
|
+
payload[:url] = generated_url
|
81
|
+
generated_url
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# You must setup CORS on OSS control panel to allow JavaScript request from your site domain.
|
86
|
+
# https://www.alibabacloud.com/help/zh/doc-detail/31988.htm
|
87
|
+
# https://help.aliyun.com/document_detail/31925.html
|
88
|
+
# Source: *.your.host.com
|
89
|
+
# Allowed Methods: POST, PUT, HEAD
|
90
|
+
# Allowed Headers: *
|
91
|
+
def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:)
|
92
|
+
instrument :url, key: key do |payload|
|
93
|
+
generated_url = bucket.object_url(path_for(key), false)
|
94
|
+
payload[:url] = generated_url
|
95
|
+
generated_url
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Headers for Direct Upload
|
100
|
+
# https://help.aliyun.com/document_detail/31951.html
|
101
|
+
# headers["Date"] is required use x-oss-date instead
|
102
|
+
def headers_for_direct_upload(key, content_type:, checksum:, **)
|
103
|
+
date = Time.now.httpdate
|
104
|
+
{
|
105
|
+
"Content-Type" => content_type,
|
106
|
+
"Content-MD5" => checksum,
|
107
|
+
"Authorization" => authorization(key, content_type, checksum, date),
|
108
|
+
"x-oss-date" => date,
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
attr_reader :config
|
114
|
+
|
115
|
+
def path_for(key)
|
116
|
+
return key unless config.fetch(:path, nil)
|
117
|
+
File.join(config.fetch(:path), key)
|
118
|
+
end
|
119
|
+
|
120
|
+
def object_url(key, sign:, expires_in: 60, params: {})
|
121
|
+
url = bucket.object_url(key, false)
|
122
|
+
unless sign
|
123
|
+
return url if params.blank?
|
124
|
+
return url + "?" + params.to_query
|
125
|
+
end
|
126
|
+
|
127
|
+
resource = "/#{bucket.name}/#{key}"
|
128
|
+
expires = Time.now.to_i + expires_in
|
129
|
+
query = {
|
130
|
+
"Expires" => expires,
|
131
|
+
"OSSAccessKeyId" => config.fetch(:access_key_id)
|
132
|
+
}
|
133
|
+
query.merge!(params)
|
134
|
+
|
135
|
+
if params.present?
|
136
|
+
resource += "?" + params.map { |k, v| "#{k}=#{v}" }.sort.join("&")
|
137
|
+
end
|
138
|
+
|
139
|
+
string_to_sign = ["GET", "", "", expires, resource].join("\n")
|
140
|
+
query["Signature"] = bucket.sign(string_to_sign)
|
141
|
+
|
142
|
+
[url, query.to_query].join('?')
|
143
|
+
end
|
144
|
+
|
145
|
+
def bucket
|
146
|
+
return @bucket if defined? @bucket
|
147
|
+
@bucket = client.get_bucket(config.fetch(:bucket))
|
148
|
+
@bucket
|
149
|
+
end
|
150
|
+
|
151
|
+
# Bucket mode :public | :private
|
152
|
+
def private_mode?
|
153
|
+
@private_mode ||= config.fetch(:mode, "public").to_s == "private"
|
154
|
+
end
|
155
|
+
|
156
|
+
def authorization(key, content_type, checksum, date)
|
157
|
+
filename = File.expand_path("/#{bucket.name}/#{path_for(key)}")
|
158
|
+
addition_headers = "x-oss-date:#{date}"
|
159
|
+
sign = ["PUT", checksum, content_type, date, addition_headers, filename].join("\n")
|
160
|
+
signature = bucket.sign(sign)
|
161
|
+
"OSS " + config.fetch(:access_key_id) + ":" + signature
|
162
|
+
end
|
163
|
+
|
164
|
+
def endpoint
|
165
|
+
config.fetch(:endpoint, "https://oss-cn-hangzhou.aliyuncs.com")
|
166
|
+
end
|
167
|
+
|
168
|
+
def client
|
169
|
+
@client ||= Aliyun::OSS::Client.new(
|
170
|
+
endpoint: endpoint,
|
171
|
+
access_key_id: config.fetch(:access_key_id),
|
172
|
+
access_key_secret: config.fetch(:access_key_secret),
|
173
|
+
cname: config.fetch(:cname, false)
|
174
|
+
)
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: activestorage-aliyun-oss
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Zhou
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-02-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 5.2.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 5.2.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: aliyun-sdk
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.6.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.6.0
|
41
|
+
description: Wraps the Aliyun OSS as an Active Storage service.
|
42
|
+
email: zhouchunyu20xx@163.com
|
43
|
+
executables: []
|
44
|
+
extensions: []
|
45
|
+
extra_rdoc_files: []
|
46
|
+
files:
|
47
|
+
- CHANGELOG.md
|
48
|
+
- README.md
|
49
|
+
- Rakefile
|
50
|
+
- lib/active_storage/service/aliyun_service.rb
|
51
|
+
- lib/active_storage_aliyun/railtie.rb
|
52
|
+
- lib/active_storage_aliyun/version.rb
|
53
|
+
- lib/activestorage-aliyun.rb
|
54
|
+
homepage: https://github.com/huacnlee/activestorage-aliyun
|
55
|
+
licenses:
|
56
|
+
- MIT
|
57
|
+
metadata: {}
|
58
|
+
post_install_message:
|
59
|
+
rdoc_options: []
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
requirements: []
|
73
|
+
rubyforge_project:
|
74
|
+
rubygems_version: 2.4.5
|
75
|
+
signing_key:
|
76
|
+
specification_version: 4
|
77
|
+
summary: Wraps the Aliyun OSS as an Active Storage service
|
78
|
+
test_files: []
|