upyun 0.7.1 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3e728cd2f23654592cfaea37730c0ad780a31eea
4
+ data.tar.gz: 41572412bf3d5ea83525082d71851687605fc7bc
5
+ SHA512:
6
+ metadata.gz: ec030a7fcc5ff6009b077d2408bfa945666bcbeebf49f0fc9fd33dfb2c8635c74e84baea60943bc23c6ba4fcef794d4b6e0aeae92b407be203fda37a32153b62
7
+ data.tar.gz: 8ad799d0272e685c803308c313bea67d75432331d9964416fba150b8802df9b6e3f8cc7b9b08d3bc97bbe45db111814f12509eabab79a8bd6599bbad7ab5ed83
data/.coveralls.yml ADDED
@@ -0,0 +1 @@
1
+ service_name: travis-ci
data/.gitignore CHANGED
@@ -1,18 +1,14 @@
1
- *.gem
2
- *.rbc
3
- .bundle
4
- .config
5
- .yardoc
6
- Gemfile.lock
7
- InstalledFiles
8
- _yardoc
9
- coverage
10
- doc/
11
- lib/bundler/man
12
- pkg
13
- rdoc
14
- spec/reports
15
- test/tmp
16
- test/version_tmp
17
- tmp
18
- .DS_Store
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format doc
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.3
6
+
7
+
8
+ script: bundle exec rspec spec
9
+ before_install:
10
+ - gem update --system
11
+ - gem --version
data/Gemfile CHANGED
@@ -1,4 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in upyun.gemspec
3
+ gem 'coveralls', require: false
4
+
4
5
  gemspec
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012 lg2046
1
+ Copyright (c) 2014 jsvisa
2
2
 
3
3
  MIT License
4
4
 
@@ -19,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
19
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
20
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
21
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,31 +1,236 @@
1
- # Upyun
1
+ # UPYUN sdk for Ruby
2
+ [![RubyGems](https://img.shields.io/gem/dtv/upyun.svg?style=flat)](https://rubygems.org/gems/upyun)
3
+ [![Build status](https://img.shields.io/travis/upyun/ruby-sdk.svg?style=flat)](https://travis-ci.org/upyun/ruby-sdk)
4
+ [![Coverage Status](https://img.shields.io/coveralls/upyun/ruby-sdk.svg)](https://coveralls.io/r/upyun/ruby-sdk)
2
5
 
3
- 又拍云 ruby api
6
+ [UPYUN](https://www.upyun.com) [Rest API](http://docs.upyun.com/api/rest_api/) 和 [Form API](http://docs.upyun.com/api/form_api/) 的 Ruby SDK !
4
7
 
5
- ## Installation
6
8
 
7
- Add this line to your application's Gemfile:
9
+ ## 安装说明
8
10
 
9
- gem 'upyun'
11
+ *Gemfile* 中加入以下代码
10
12
 
11
- And then execute:
13
+ ```ruby
14
+ gem 'upyun'
15
+ ```
16
+
17
+ 然后执行:
12
18
 
13
19
  $ bundle
14
20
 
15
- Or install it yourself as:
21
+ 或者可以手动安装:
16
22
 
17
23
  $ gem install upyun
18
24
 
19
- ## Usage
25
+ ## 基本使用
26
+
27
+ ### Rest API 使用
28
+
29
+ #### 初始化一个实例
30
+
31
+ ```ruby
32
+ require 'upyun'
33
+
34
+ upyun = Upyun::Rest.new('bucket', 'operator', 'password', 'endpoint')
35
+ ```
36
+
37
+ 其中,参数 `bucket` 为空间名称,`operator` 为授权操作员帐号, `password` 为授权操作员密码,必选。
38
+
39
+ 参数 `endpoint` 为又拍云存储 API 接入点,根据国内的网络情况,又拍云存储 API 提供了电信、联通(网通)、移动(铁通)数个接入点,
40
+ 在初始化时可由参数 `endpoint` 进行设置,详情查阅 [API 域名](http://docs.upyun.com/api/)。其可选的值有:
41
+
42
+ ```ruby
43
+ Upyun::ED_AUTO # 自动判断最优线路
44
+ Upyun::ED_TELECOM # 电信接入点
45
+ Upyun::ED_UNION # 联通(网通)接入点
46
+ Upyun::ED_CMCC # 移动(铁通)接入点
47
+ ```
48
+
49
+ 默认设置为 `Upyun::ED_AUTO` ,但是我们推荐根据服务器网络状况,手动设置合理的接入点以获取最佳的访问速度。
50
+ 同时,也可以在初始化一个实例之后通过:
51
+
52
+ ```ruby
53
+ upyun.endpoint = Upyun::ED_CMCC
54
+ ```
55
+ 更改接入点。
56
+
57
+ #### 上传文件
58
+
59
+ 默认使用 Upyun 基本 Header 头上传文件:
60
+
61
+ ```ruby
62
+ upyun.put('/save/to/path', 'file or binary')
63
+ ```
64
+ 其中 `/save/to/path` 为文件保存路径, `file or binary` 为本机上文件路径或者文件内容。
65
+ **注:**
66
+ > 这里只指定了又拍云必选的 `Date`, `Content-Length` 两个 Header,其它 Header 信息均未指定
67
+
68
+ 也可以使用 Upyun 定义的额外 Header 头上传文件,详情查阅 [Rest API](http://docs.upyun.com/api/rest_api/), 如:
69
+
70
+ ```ruby
71
+ headers = {'Content-Type' => 'image/jpeg', 'x-gmkerl-type' => 'fix_width', 'x-gmkerl-value' => 1080}
72
+ upyun.put('/save/to/path', 'file or binary', headers)
73
+ ```
74
+
75
+ 上传成功返回 `true`,失败返回一个 `Hash` 结构: `{error: {code: code, message: message}}`,
76
+ 其中 `code` 为又拍云返回的错误码, `message` 为错误信息。
77
+
78
+
79
+ #### 下载文件
80
+
81
+ ```ruby
82
+ file = upyun.get('/path/to/file')
83
+ ```
84
+
85
+ 下载成功返回文件信息,失败返回一个 `Hash`: `{error: {code: code, message: message}}`,
86
+ 其中 `code` 为又拍云返回的错误码, `message` 为错误信息。
87
+
88
+ 也可以指定保存路径,下载到的文件将写入到保存路径中:
89
+
90
+ ```ruby
91
+ upyun.get('/path/to/file', 'saved/foo.png')
92
+ ```
93
+
94
+ 下载成功返回获取的文件长度。
95
+
96
+
97
+ #### 获取文件信息
98
+
99
+ ```ruby
100
+ upyun.getinfo('/path/to/file')
101
+ ```
102
+
103
+ 成功返回 `Hash` 结构:
104
+
105
+ ```
106
+ {file_type: "file", file_size: 397190, file_date: 1415954066}
107
+ ```
108
+
109
+ 其中
110
+
111
+ * `:file_type` 说明是文件(`"file"`)还是目录(`"folder"`)
112
+ * `:file_size` 是文件的大小
113
+ * `:file_date` 是文件最后的更改时间。
114
+
115
+ 失败返回一个 `Hash`: `{error: {code: code, message: message}}`。
116
+
117
+
118
+ #### 删除文件或者目录
119
+
120
+ ```ruby
121
+ upyun.delete('/path/to/file')
122
+ ```
123
+
124
+ 成功返回: `true`,
125
+
126
+ 失败返回一个 `Hash`: `{error: {code: code, message: message}}`。
127
+
128
+ #### 创建目录
129
+
130
+ ```ruby
131
+ upyun.mkdir('/path/to/dir')
132
+ ```
133
+
134
+ 成功返回: `true`,
135
+
136
+ 失败返回一个 `Hash`: `{error: {code: code, message: message}}`。
137
+
138
+ #### 获取目录文件列表
139
+
140
+ ```ruby
141
+ upyun.getlist('/path/to/dir')
142
+ ```
143
+
144
+ 成功返回一个数组,每个数组成员为一个文件/目录:
145
+
146
+ ```ruby
147
+ [{:name=>"foo", :type=>:folder, :length=>0, :last_modified=>1416193624},
148
+ {:name=>"bar.txt", :type=>:file, :length=>25, :last_modified=>1415261057}]
149
+ ```
150
+
151
+ 失败返回一个 `Hash`: `{error: {code: code, message: message}}`。
152
+
153
+ #### 获取空间使用情况
154
+
155
+ ```ruby
156
+ upyun.usage
157
+ ```
158
+
159
+ 成功返回空间使用量(单位为 `Byte`): `12400`,
160
+
161
+ 失败返回一个 `Hash`: `{error: {code: code, message: message}}`。
162
+
163
+ ### Form API 使用
164
+
165
+ #### 初始化一个实例
166
+
167
+ ```ruby
168
+ require 'upyun'
169
+
170
+ upyun = Upyun::Form.new('form-password', 'bucket')
171
+ ```
172
+
173
+ 其中,参数 `form-password` 为空间表单 API 密钥,可通过又拍云后台获取,`bucket` 为空间名称(必选)。
174
+
175
+ 与 Rest API 相似, 表单 API 也有个实例变量 `endpoint` 代表又拍云基本域名,默认设置为 `Upyun::ED_AUTO` ,也可以在初始化一个实例之后通过:
176
+
177
+ ```ruby
178
+ upyun.endpoint = Upyun::ED_CMCC
179
+ ```
180
+ 更改接入点。
181
+
182
+
183
+ #### 上传文件
184
+
185
+ 为了简化使用,又拍云文档必选的参数中:
186
+ >
187
+ `save-key` 默认设置为: `'/{year}/{mon}/{day}/{filename}{.suffix}'`
188
+ `expiration` 默认设置为10分钟: `Time.now.to_i + 600`
189
+
190
+
191
+ 使用简化版本,不使用额外的策略参数:
192
+
193
+ ```ruby
194
+ upyun.upload('file')
195
+ ```
196
+ 上传结果返回一个 `Hash` 结构:
197
+
198
+ ```ruby
199
+ {
200
+ :code=>200,
201
+ :message=>"ok",
202
+ :url=>"/2014/11/17/upyun.jpg",
203
+ :time=>1416208715,
204
+ :sign=>"f5165b35df431065ca54490a34028635"
205
+ }
206
+ ```
207
+ 其中
208
+ 1. `code`: 返回的状态码,`200` 为成功,其它为失败
209
+ 2. `message`: 错误信息,具体查阅 [表单 API 状态代码表](http://docs.upyun.com/api/form_api/#api_2)
210
+ 3. `url`: 上传文件保存路径
211
+ 4. `time`: 请求的时间戳
212
+ 5. `sign`: 签名参数,详情见 [sign与non-sign参数说明](http://docs.upyun.com/api/form_api/#note6)
213
+ 6. 如果在请求中指定了 `ext-param`, 那么返回的结构中也会有 `ext-param` 字段,详情见 [ext-param](http://docs.upyun.com/api/form_api/#note5)
214
+
215
+ 可以在上传的时候指定一些策略参数:
216
+
217
+ ```ruby
218
+ opts = {
219
+ 'save_key' => '/foo/bar.jpg',
220
+ 'content-type' => 'image/jpeg',
221
+ 'image-width-range' => '0,1024',
222
+ 'return-url' => 'http://www.example.com'
223
+ }
224
+ upyun.upload('file', opts)
225
+ ```
226
+ 特别地,如果指定了 `return-url`, 那么返回的是需要跳转的地址,
227
+ 详情查阅 [通知规则](http://docs.upyun.com/api/form_api/#notify_return)
20
228
 
21
- up_client = UpYun::Bucket.new("by-test-upload", "ichihuo", "********")
22
- res = up_client.writeFile("/test.png", File.new("test.png"))
23
- res['x-upyun-width']
24
229
 
25
230
  ## Contributing
26
231
 
27
- 1. Fork it
232
+ 1. Fork it ( https://github.com/[my-github-username]/upyun/fork )
28
233
  2. Create your feature branch (`git checkout -b my-new-feature`)
29
- 3. Commit your changes (`git commit -am 'Added some feature'`)
234
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
235
  4. Push to the branch (`git push origin my-new-feature`)
31
- 5. Create new Pull Request
236
+ 5. Create a new Pull Request
data/Rakefile CHANGED
@@ -1,2 +1,2 @@
1
- #!/usr/bin/env rake
2
1
  require "bundler/gem_tasks"
2
+
data/lib/upyun/form.rb ADDED
@@ -0,0 +1,87 @@
1
+ # encoding: utf-8
2
+ require 'restclient'
3
+ require 'base64'
4
+ require 'json'
5
+ require 'active_support/hash_with_indifferent_access'
6
+
7
+ module Upyun
8
+ class Form
9
+ include Utils
10
+
11
+ VALID_PARAMS = %w(
12
+ bucket
13
+ save-key
14
+ expiration
15
+ allow-file-type
16
+ content-length-range
17
+ content-md5
18
+ content-secret
19
+ content-type
20
+ image-width-range
21
+ image-height-range
22
+ notify-url
23
+ return-url
24
+ x-gmkerl-thumbnail
25
+ x-gmkerl-type
26
+ x-gmkerl-value
27
+ x-gmkerl-quality
28
+ x-gmkerl-unsharp
29
+ x-gmkerl-rotate
30
+ x-gmkerl-crop
31
+ x-gmkerl-exif-switch
32
+ ext-param
33
+ )
34
+
35
+ attr_accessor :bucket, :password
36
+
37
+ def initialize(password, bucket)
38
+ @password = password
39
+ @bucket = bucket
40
+ @endpoint = ED_AUTO
41
+ end
42
+
43
+ def upload(file, opts={})
44
+ base_opts = HashWithIndifferentAccess.new({
45
+ 'bucket' => @bucket,
46
+ 'save-key' => '/{year}/{mon}/{day}/{filename}{.suffix}',
47
+ 'expiration' => Time.now.to_i + 600
48
+ })
49
+
50
+ payload = {
51
+ policy: policy(base_opts.merge(opts)),
52
+ signature: signature,
53
+ file: File.new(file, 'rb')
54
+ }
55
+
56
+ RestClient.post("http://#{@endpoint}/#{@bucket}", payload) do |res|
57
+ case res.code
58
+ when 302
59
+ res
60
+ else
61
+ body = JSON.parse(res.body, symbolize_names: true)
62
+
63
+ # TODO Upyun have a small bug for the `code`,
64
+ # we have to adjust it to integer
65
+ body[:code] = body[:code].to_i
66
+ body
67
+ end
68
+ end
69
+ end
70
+
71
+ private
72
+ def policy(opts)
73
+ @_policy = Base64.encode64(policy_json(opts))
74
+ end
75
+
76
+ def signature
77
+ md5("#{@_policy}&#{@password}")
78
+ end
79
+
80
+ def policy_json(opts)
81
+ policies = VALID_PARAMS.reduce({}) do |memo, e|
82
+ (v = opts[e]) ? memo.merge!({e => v}) : memo
83
+ end
84
+ policies.to_json
85
+ end
86
+ end
87
+ end
data/lib/upyun/rest.rb ADDED
@@ -0,0 +1,132 @@
1
+ # encoding: utf-8
2
+ require 'restclient'
3
+ require 'uri'
4
+
5
+ module Upyun
6
+ class Rest
7
+ include Utils
8
+
9
+ def initialize(bucket, operator, password, endpoint=Upyun::ED_AUTO)
10
+ @bucket = bucket
11
+ @operator = operator
12
+ @password = md5(password)
13
+ @endpoint = endpoint
14
+ end
15
+
16
+ def put(path, file, headers={})
17
+ raise ArgumentError, "'file' is not an instance of String" unless file.is_a?(String)
18
+ headers = headers.merge({"mkdir" => true}) unless headers.key?("mkdir")
19
+ options = if File.file?(file)
20
+ {body: File.read(file), length: File.size(file), headers: headers}
21
+ else
22
+ {body: file, length: file.length, headers: headers}
23
+ end
24
+
25
+ request(:put, path, options)
26
+ end
27
+
28
+ def get(path, savepath=nil)
29
+ res = request(:get, path)
30
+ return res if res.is_a?(Hash) || !savepath
31
+
32
+ dir = File.dirname(savepath)
33
+ FileUtils.mkdir_p(dir) unless File.directory?(dir)
34
+ File.write(savepath, res)
35
+ end
36
+
37
+ def getinfo(path)
38
+ hds = request(:head, path)
39
+ hds = hds.key?(:error) ? hds : format_info(hds)
40
+ end
41
+
42
+ def delete(path)
43
+ request(:delete, path)
44
+ end
45
+
46
+ def mkdir(path)
47
+ request(:post, path, {headers: {folder: true, mkdir: true}})
48
+ end
49
+
50
+ def getlist(path="/")
51
+ res = request(:get, path)
52
+ return res if res.is_a?(Hash)
53
+ res.split("\n").map do |f|
54
+ attrs = f.split("\t")
55
+ {
56
+ name: attrs[0],
57
+ type: attrs[1] == "N" ? :file : :folder,
58
+ length: attrs[2].to_i,
59
+ last_modified: attrs[3].to_i
60
+ }
61
+ end
62
+ end
63
+
64
+ def usage(path="/")
65
+ res = request(:get, path, {params: "usage"})
66
+ return res if res.is_a?(Hash)
67
+
68
+ # RestClient has a bug, body.to_i returns the code instead of body,
69
+ # see more on https://github.com/rest-client/rest-client/pull/103
70
+ res.dup.to_i
71
+ end
72
+
73
+ private
74
+
75
+ def format_info(hds)
76
+ selected = hds.select { |k| k.to_s.match(/^x_upyun/i) }
77
+ selected.reduce({}) do |memo, (k, v)|
78
+ memo.merge!({k[8..-1].to_sym => /^\d+$/.match(v) ? v.to_i : v})
79
+ end
80
+ end
81
+
82
+ def fullpath(path)
83
+ "/#{@bucket}#{URI.encode(URI.decode(path[0] == '/' ? path : '/' + path))}"
84
+ end
85
+
86
+ def encode(fullpath, params)
87
+ URI.join("http://#{@endpoint}", fullpath, params.nil? ? '' : '?' + params).to_s
88
+ end
89
+
90
+ def request(method, path, options={})
91
+ fullpath = fullpath(path)
92
+ url = encode(fullpath, options[:params])
93
+ headers = options[:headers] || {}
94
+ date = gmdate
95
+ length = options[:length] || 0
96
+ headers.merge!({
97
+ 'Date' => date,
98
+ 'Authorization' => sign(method, date, fullpath, length)
99
+ })
100
+
101
+ if [:post, :patch, :put].include? method
102
+ RestClient.send(method, url, options[:body].nil? ? "" : options[:body], headers) do |res|
103
+ res.code == 200 ? true : {error: {code: res.code, message: res.body}}
104
+ end
105
+ else
106
+ RestClient.send(method, url, headers) do |res|
107
+ if res.code == 200
108
+ case method
109
+ when :get
110
+ res.body
111
+ when :head
112
+ res.headers
113
+ else
114
+ true
115
+ end
116
+ else
117
+ {error: {code: res.code, message: res.body}}
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ def gmdate
124
+ Time.now.utc.strftime('%a, %d %b %Y %H:%M:%S GMT')
125
+ end
126
+
127
+ def sign(method, date, path, length)
128
+ sign = "#{method.to_s.upcase}&#{path}&#{date}&#{length}&#{@password}"
129
+ "UpYun #{@operator}:#{md5(sign)}"
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,19 @@
1
+ require 'digest/md5'
2
+
3
+ module Upyun
4
+ module Utils
5
+ def md5(str)
6
+ Digest::MD5.hexdigest(str)
7
+ end
8
+
9
+ def self.included(receiver)
10
+ receiver.send(:define_method, :endpoint) { @endpoint }
11
+ receiver.send(:define_method, :endpoint=) do |ep|
12
+ unless Upyun::ED_LIST.member?(ep)
13
+ raise ArgumentError, "Valid endpoints are: #{Upyun::ED_LIST}"
14
+ end
15
+ @endpoint = ep
16
+ end
17
+ end
18
+ end
19
+ end
data/lib/upyun/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Upyun
2
- VERSION = "0.7.1"
2
+ VERSION = "1.0.1"
3
3
  end
data/lib/upyun.rb CHANGED
@@ -1,14 +1,13 @@
1
- require File.expand_path(File.dirname(__FILE__) + "/upyun/version")
2
- require File.expand_path(File.dirname(__FILE__) + "/upyun/bucket")
3
-
1
+ require 'upyun/version'
2
+ require 'upyun/utils'
3
+ require 'upyun/rest'
4
+ require 'upyun/form'
4
5
 
5
6
  module Upyun
6
- def self.get_config_for_bucket(bucket)
7
- UP_BUCKETS.find {|k, v| v.bucket_name.to_s == bucket.to_s}.try(:last)
8
- end
9
-
10
- # 为给定的路径与文件名生成保存后的远程路径
11
- def self.rand_save_path(filename)
12
- "/#{Date.today.year}/#{'%2s' % Date.today.month.to_s.rjust(2, "0")}/#{RandomCode.mix_string(16).downcase}#{File.basename(filename)}"
13
- end
14
- end
7
+ DOMAIN = 'api.upyun.com'
8
+ ED_AUTO = "v0.#{DOMAIN}"
9
+ ED_TELECOM = "v1.#{DOMAIN}"
10
+ ED_UNION = "v2.#{DOMAIN}"
11
+ ED_CMCC = "v3.#{DOMAIN}"
12
+ ED_LIST = (0..3).map { |e| "v#{e}.#{DOMAIN}" }
13
+ end
@@ -0,0 +1,4 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+
4
+ require 'upyun'
data/spec/upyun.jpg ADDED
Binary file
@@ -0,0 +1,201 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "Upyun Restful API Basic testing" do
4
+ before :all do
5
+ @upyun = Upyun::Rest.new('sdkfile', 'tester', 'grjxv2mxELR3')
6
+ @file = File.expand_path('../upyun.jpg', __FILE__)
7
+ @str = 'This is a binary string, not a file'
8
+ end
9
+
10
+ describe ".endpoint=" do
11
+ it "known ENDPOINT, should return ok" do
12
+ @upyun.endpoint = Upyun::ED_CMCC
13
+ expect(@upyun.endpoint).to eq 'v3.api.upyun.com'
14
+ end
15
+
16
+ it "unknown ENDPOINT, should raise ArgumentError" do
17
+ expect {@upyun.endpoint = 'v5.api.upyun.com'}.
18
+ to raise_error(ArgumentError, /Valid endpoints/)
19
+ end
20
+
21
+ after { @upyun.endpoint = Upyun::ED_AUTO }
22
+ end
23
+
24
+ describe ".put" do
25
+ before { @path = '/ruby-sdk/foo/test.jpg' }
26
+
27
+ it "PUT a file" do
28
+ expect(@upyun.put(@path, @file)).to be true
29
+ end
30
+
31
+ it "PUT a binary string" do
32
+ expect(@upyun.put(@path, @str)).to be true
33
+ end
34
+
35
+ it "PUT with some extra process headers" do
36
+ headers = {
37
+ 'Contetn-type' => 'image/jpeg',
38
+ 'x-gmkerl-type' => 'fix_width',
39
+ 'x-gmkerl-value' => 42,
40
+ 'x-gmkerl-unsharp' => true
41
+ }
42
+ expect(@upyun.put(@path, @file, headers)).to be true
43
+ end
44
+
45
+ after { @upyun.delete(@path) }
46
+ end
47
+
48
+ describe ".get" do
49
+ before :all do
50
+ @path = '/ruby-sdk/foo/test.jpg'
51
+ @upyun.put(@path, @str, {'Content-Type' => 'text/plain'})
52
+ end
53
+
54
+ it "GET a file" do
55
+ expect(@upyun.get(@path)).to eq(@str)
56
+ end
57
+
58
+ it "GET a file and save" do
59
+ expect(@upyun.get(@path, './save.jpg')).not_to eq(404)
60
+ expect(File.exists?('./save.jpg')).to be true
61
+ expect(File.read('./save.jpg')).to eq(@str)
62
+ File.delete('./save.jpg')
63
+ end
64
+
65
+ it "GET a not-exist file" do
66
+ res = @upyun.get('/ruby-sdk/foo/test-not-exist.jpg')
67
+ expect(res.is_a?(Hash) && res[:error][:code] == 404)
68
+ end
69
+
70
+ after(:all) { @upyun.delete(@path) }
71
+ end
72
+
73
+ describe ".getinfo" do
74
+ before :all do
75
+ @dir = '/ruby-sdk/foo'
76
+ @path = "#{@dir}/test.jpg"
77
+ @upyun.put(@path, @str, {'Content-Type' => 'text/plain'})
78
+ end
79
+
80
+ it "of file should success" do
81
+ res = @upyun.getinfo(@path)
82
+ expect(res[:file_type]).to eq('file')
83
+ end
84
+
85
+ it "of folder should success" do
86
+ res = @upyun.getinfo(@dir)
87
+ expect(res[:file_type]).to eq('folder')
88
+ end
89
+
90
+ after(:all) { @upyun.delete(@path) }
91
+ end
92
+
93
+ describe ".delete" do
94
+ before do
95
+ @path = '/ruby-sdk/foo/test.jpg'
96
+ @upyun.put(@path, @file)
97
+ end
98
+
99
+ it "DELETE a file" do
100
+ expect(@upyun.delete(@path)).to be true
101
+ end
102
+ end
103
+
104
+ describe ".mkdir" do
105
+ before(:all) { @path = '/ruby-skd/foo/dir' }
106
+
107
+ it "should success" do
108
+ expect(@upyun.mkdir(@path)).to be true
109
+ end
110
+
111
+ after(:all) { @upyun.delete(@path) }
112
+ end
113
+
114
+ describe ".getlist" do
115
+ it "should get a list of file record" do
116
+ expect(@upyun.getlist("/")).to be_instance_of(Array)
117
+ end
118
+ end
119
+
120
+ describe ".usage" do
121
+ it "should be an Fixnum" do
122
+ expect(@upyun.usage).to be_instance_of(Fixnum)
123
+ end
124
+ end
125
+ end
126
+
127
+ describe "Form Upload" do
128
+ before :all do
129
+ @form = Upyun::Form.new('ESxWIoMmF39nSDY7CSFUsC7s50U=', 'sdkfile')
130
+ @file = File.expand_path('../upyun.jpg', __FILE__)
131
+ end
132
+
133
+ describe ".endpoint=" do
134
+ it "known ENDPOINT, should return ok" do
135
+ @form.endpoint = Upyun::ED_CMCC
136
+ expect(@form.endpoint).to eq 'v3.api.upyun.com'
137
+ end
138
+
139
+ it "unknown ENDPOINT, should raise ArgumentError" do
140
+ expect {@form.endpoint = 'v5.api.upyun.com'}.
141
+ to raise_error(ArgumentError, /Valid endpoints/)
142
+ end
143
+
144
+ after { @form.endpoint = Upyun::ED_AUTO }
145
+ end
146
+
147
+ describe ".upload" do
148
+ it "with default attributes should success" do
149
+ res = @form.upload(@file)
150
+ expect(res.keys).to include(:code, :message, :url, :time)
151
+ expect(res[:code]).to eq(200)
152
+ expect(res[:message]).to match(/ok/)
153
+ now = Time.now
154
+ expect(res[:url]).to eq("/#{now.year}/#{now.mon}/#{now.day}/upyun.jpg")
155
+ end
156
+
157
+ it "set 'save-key' should success" do
158
+ res = @form.upload(@file, {'save-key' => 'name-ed keypath'})
159
+ expect(res[:code]).to eq(200)
160
+ expect(res[:url]).to eq('name-ed keypath')
161
+ end
162
+
163
+ it "set not correct 'expiration' should return 403 with expired" do
164
+ res = @form.upload(@file, {'expiration' => 102400})
165
+ expect(res[:code]).to eq(403)
166
+ expect(res[:message]).to match(/Authorize has expired/)
167
+ end
168
+
169
+ it "set 'return-url' should return 302 with 'location' header" do
170
+ res = @form.upload(@file, {'return-url' => 'http://www.example.com'})
171
+ expect(res.code).to eq(302)
172
+ expect(res.headers.key?(:location)).to be true
173
+ end
174
+
175
+ it "set 'return-url' and handle failed, should also return 302 with 'location' header" do
176
+ opts = {
177
+ 'image-width-range' => '0,10',
178
+ 'return-url' => 'http://www.example.com'
179
+ }
180
+ res = @form.upload(@file, opts)
181
+ expect(res.code).to eq(302)
182
+ expect(res.headers.key?(:location)).to be true
183
+ end
184
+
185
+ it "set 'notify-url' should return 200 success" do
186
+ res = @form.upload(@file, {'notify-url' => 'http://www.example.com'})
187
+ expect(res).to be_instance_of(Hash)
188
+ expect(res[:code]).to eq(200)
189
+ end
190
+
191
+ it "set 'notify-url' and handle failed, should return 403 failed" do
192
+ opts = {
193
+ 'image-width-range' => '0,10',
194
+ 'notify-url' => 'http://www.example.com'
195
+ }
196
+ res = @form.upload(@file, opts)
197
+ expect(res).to be_instance_of(Hash)
198
+ expect(res[:code]).to eq(403)
199
+ end
200
+ end
201
+ end
data/upyun.gemspec CHANGED
@@ -1,17 +1,28 @@
1
- # -*- encoding: utf-8 -*-
2
- require File.expand_path('../lib/upyun/version', __FILE__)
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'upyun/version'
3
5
 
4
- Gem::Specification.new do |gem|
5
- gem.authors = ["veggie"]
6
- gem.email = ["kkxlkkxllb@gmail.com"]
7
- gem.description = %q{又拍云存储ruby api}
8
- gem.summary = %q{又拍云存储ruby api}
9
- gem.homepage = "https://github.com/kkxlkkxllb/upyun"
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "upyun"
8
+ spec.version = Upyun::VERSION
9
+ spec.authors = ["jsvisa"]
10
+ spec.email = ["delweng@gmail.com"]
11
+ spec.summary = "UPYUN API SDK"
12
+ spec.description = "UPYUN Rest API and Form API SDK"
13
+ spec.homepage = "https://github.com/upyun/ruby-sdk"
14
+ spec.license = "MIT"
10
15
 
11
- gem.files = `git ls-files`.split($\)
12
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
- gem.name = "upyun"
15
- gem.require_paths = ["lib"]
16
- gem.version = Upyun::VERSION
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "rest-client", ">= 1.6.7"
22
+ spec.add_dependency "activesupport", "~> 4.1.0"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.7"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec", "~> 3.1.0"
17
27
  end
28
+
metadata CHANGED
@@ -1,55 +1,134 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: upyun
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
5
- prerelease:
4
+ version: 1.0.1
6
5
  platform: ruby
7
6
  authors:
8
- - veggie
7
+ - jsvisa
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-11-30 00:00:00.000000000Z
13
- dependencies: []
14
- description: 又拍云存储ruby api
11
+ date: 2014-11-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rest-client
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 1.6.7
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 1.6.7
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 4.1.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 4.1.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.7'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 3.1.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 3.1.0
83
+ description: UPYUN Rest API and Form API SDK
15
84
  email:
16
- - kkxlkkxllb@gmail.com
85
+ - delweng@gmail.com
17
86
  executables: []
18
87
  extensions: []
19
88
  extra_rdoc_files: []
20
89
  files:
21
- - .gitignore
90
+ - ".coveralls.yml"
91
+ - ".gitignore"
92
+ - ".rspec"
93
+ - ".travis.yml"
22
94
  - Gemfile
23
- - LICENSE
95
+ - LICENSE.txt
24
96
  - README.md
25
97
  - Rakefile
26
98
  - lib/upyun.rb
27
- - lib/upyun/bucket.rb
99
+ - lib/upyun/form.rb
100
+ - lib/upyun/rest.rb
101
+ - lib/upyun/utils.rb
28
102
  - lib/upyun/version.rb
103
+ - spec/spec_helper.rb
104
+ - spec/upyun.jpg
105
+ - spec/upyun_spec.rb
29
106
  - upyun.gemspec
30
- homepage: https://github.com/kkxlkkxllb/upyun
31
- licenses: []
107
+ homepage: https://github.com/upyun/ruby-sdk
108
+ licenses:
109
+ - MIT
110
+ metadata: {}
32
111
  post_install_message:
33
112
  rdoc_options: []
34
113
  require_paths:
35
114
  - lib
36
115
  required_ruby_version: !ruby/object:Gem::Requirement
37
- none: false
38
116
  requirements:
39
- - - ! '>='
117
+ - - ">="
40
118
  - !ruby/object:Gem::Version
41
119
  version: '0'
42
120
  required_rubygems_version: !ruby/object:Gem::Requirement
43
- none: false
44
121
  requirements:
45
- - - ! '>='
122
+ - - ">="
46
123
  - !ruby/object:Gem::Version
47
124
  version: '0'
48
125
  requirements: []
49
126
  rubyforge_project:
50
- rubygems_version: 1.8.10
127
+ rubygems_version: 2.4.4
51
128
  signing_key:
52
- specification_version: 3
53
- summary: 又拍云存储ruby api
54
- test_files: []
55
- has_rdoc:
129
+ specification_version: 4
130
+ summary: UPYUN API SDK
131
+ test_files:
132
+ - spec/spec_helper.rb
133
+ - spec/upyun.jpg
134
+ - spec/upyun_spec.rb
data/lib/upyun/bucket.rb DELETED
@@ -1,111 +0,0 @@
1
- require 'net/http'
2
- require 'uri'
3
- require "base64"
4
- require 'digest/md5'
5
-
6
- module Upyun
7
- class Bucket
8
- attr_accessor :bucketname, :username, :password
9
- attr_accessor :api_domain, :api_form_secret
10
-
11
- def initialize(bucketname, username, password, options = {})
12
- options = { :api_domain => "v0.api.upyun.com", :api_form_secret => "" }.merge(options)
13
- @bucketname = bucketname
14
- @username = username
15
- @bpwd = password
16
- @password = Digest::MD5.hexdigest(password)
17
- @api_domain = options[:api_domain]
18
- @api_form_secret = options[:api_form_secret]
19
- end
20
-
21
- def write_file(filepath, fd, mkdir='true')
22
- url = "http://#{api_domain}/#{bucketname}#{filepath}"
23
- uri = URI.parse(URI.encode(url))
24
-
25
- response = Net::HTTP.start(uri.host, uri.port) do |http|
26
- date = get_gmt_date
27
- length = File.size(fd)
28
- method = 'PUT'
29
- headers = {
30
- 'Date' => date,
31
- 'Content-Length' => length.to_s,
32
- 'Authorization' => sign(method, get_gmt_date, "/#{@bucketname}#{filepath}", length),
33
- 'mkdir' => mkdir
34
- }
35
-
36
- http.send_request(method, uri.request_uri, fd.read, headers)
37
- end
38
- response.body
39
- end
40
-
41
- def check_space
42
- url = "http://#{api_domain}/#{bucketname}/?usage"
43
- uri = URI.parse(URI.encode(url))
44
- req = Net::HTTP::Get.new(url)
45
- req.basic_auth @username, @bpwd
46
- response = Net::HTTP.start(uri.host, uri.port) do |http|
47
- http.request(req)
48
- end
49
- response.body
50
- end
51
-
52
- def checkout(file)
53
- url = "http://#{api_domain}/#{bucketname}/#{file}"
54
- uri = URI.parse(URI.encode(url))
55
- req = Net::HTTP::Get.new(url)
56
- req.basic_auth @username, @bpwd
57
- response = Net::HTTP.start(uri.host, uri.port) do |http|
58
- http.request(req)
59
- end
60
- response.body
61
- end
62
-
63
- # 生成api使用的policy 以及 signature 可以是图片或者是文件附件 图片最大为1M 文件附件最大为5M
64
- def api_form_params(file_type = "pic", notify_url = "", return_url = "", expire_date = 1.days)
65
- policy_doc = {
66
- "bucket" => bucketname,
67
- "expiration" => (DateTime.now + expire_date).to_i,
68
- "save-key" => "/{year}/{mon}/{random}{.suffix}",
69
- "notify-url" => notify_url,
70
- "return-url" => return_url
71
- }
72
-
73
- policy_doc = policy_doc.merge({"allow-file-type" => "jpg,jpeg,gif,png", "content-length-range" => "0,1048576"}) if file_type == "pic"
74
- policy_doc = policy_doc.merge({"allow-file-type" => "doc docx xls xlsx ppt txt zip rar", "content-length-range" => "0,5242880"}) if file_type == "file"
75
-
76
- policy = Base64.encode64(policy_doc.to_json).gsub("\n", "").strip
77
- signature = Digest::MD5.hexdigest(policy + "&" + api_form_secret)
78
-
79
- {:policy => policy, :signature => signature}
80
- end
81
-
82
- def parse_notify_params(params)
83
- params = params.with_indifferent_access
84
- if params[:code].to_s == "200" && (params[:sign] == Digest::MD5.hexdigest("#{params[:code]}&#{params[:message]}&#{params[:url]}&#{params[:time]}&#{api_form_secret}"))
85
- url = "http://#{bucketname}.b0.upaiyun.com#{params[:url]}"
86
- mid = Digest::MD5.hexdigest(url)
87
- image_attributes = { :mid => mid, :url => url }
88
-
89
- if params["image-width"] && params["image-height"] && params["image-frames"] && params["image-type"]
90
- image_attributes[:width] = params["image-width"].to_i
91
- image_attributes[:height] = params["image-height"].to_i
92
- image_attributes[:frames] = params["image-frames"].to_i
93
- image_attributes[:file_type] = params["image-type"]
94
- end
95
- image_attributes
96
- else
97
- nil
98
- end
99
- end
100
-
101
- private
102
- def get_gmt_date
103
- DateTime.now.utc.strftime('%a, %d %b %Y %H:%M:%S GMT')
104
- end
105
-
106
- def sign(method, date, url, length)
107
- sign = "#{method}&#{url}&#{date}&#{length}&#{password}"
108
- "UpYun #{@username}:#{Digest::MD5.hexdigest(sign)}"
109
- end
110
- end
111
- end