baidu-sdk 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +15 -0
  5. data/.yardopts +1 -0
  6. data/Gemfile +17 -0
  7. data/Guardfile +6 -0
  8. data/HISTORY.md +2 -0
  9. data/LICENSE.txt +22 -0
  10. data/README.md +161 -0
  11. data/Rakefile +4 -0
  12. data/baidu-pcs.gemspec +24 -0
  13. data/lib/baidu/configure.rb +27 -0
  14. data/lib/baidu/core.rb +19 -0
  15. data/lib/baidu/errors/error.rb +22 -0
  16. data/lib/baidu/oauth.rb +11 -0
  17. data/lib/baidu/oauth/client.rb +66 -0
  18. data/lib/baidu/oauth/flow/base.rb +44 -0
  19. data/lib/baidu/oauth/flow/code.rb +69 -0
  20. data/lib/baidu/oauth/flow/device.rb +75 -0
  21. data/lib/baidu/pcs.rb +19 -0
  22. data/lib/baidu/pcs/client.rb +1090 -0
  23. data/lib/baidu/session.rb +36 -0
  24. data/lib/baidu/support/cacert.pem +37 -0
  25. data/lib/baidu/support/request.rb +127 -0
  26. data/lib/baidu/support/util.rb +67 -0
  27. data/lib/baidu/version.rb +5 -0
  28. data/spec/baidu/core_spec.rb +20 -0
  29. data/spec/baidu/oauth/client_spec.rb +199 -0
  30. data/spec/baidu/pcs/client_spec.rb +878 -0
  31. data/spec/baidu/session_spec.rb +27 -0
  32. data/spec/baidu/support/request_spec.rb +58 -0
  33. data/spec/baidu/support/util_spec.rb +48 -0
  34. data/spec/fixtures/add_task.json +1 -0
  35. data/spec/fixtures/cancel_task.json +3 -0
  36. data/spec/fixtures/copy.json +7 -0
  37. data/spec/fixtures/delete.json +3 -0
  38. data/spec/fixtures/diff.json +17 -0
  39. data/spec/fixtures/empty.json +1 -0
  40. data/spec/fixtures/get_token_code.json +8 -0
  41. data/spec/fixtures/get_token_device.json +8 -0
  42. data/spec/fixtures/list.json +11 -0
  43. data/spec/fixtures/list_task_0.json +1 -0
  44. data/spec/fixtures/list_task_1.json +1 -0
  45. data/spec/fixtures/listrecycle.json +23 -0
  46. data/spec/fixtures/logo.png +0 -0
  47. data/spec/fixtures/meta.json +11 -0
  48. data/spec/fixtures/mkdir.json +7 -0
  49. data/spec/fixtures/move.json +8 -0
  50. data/spec/fixtures/query_task_0.json +24 -0
  51. data/spec/fixtures/query_task_1.json +22 -0
  52. data/spec/fixtures/quota.json +5 -0
  53. data/spec/fixtures/rapidupload.json +10 -0
  54. data/spec/fixtures/refresh_token.json +8 -0
  55. data/spec/fixtures/restore.json +1 -0
  56. data/spec/fixtures/search.json +11 -0
  57. data/spec/fixtures/stream_list.json +16 -0
  58. data/spec/fixtures/streaming.m3u8 +5 -0
  59. data/spec/fixtures/upload.json +9 -0
  60. data/spec/fixtures/upload_block.json +4 -0
  61. data/spec/fixtures/user_and_device_code.json +8 -0
  62. data/spec/spec_helper.rb +66 -0
  63. metadata +169 -0
@@ -0,0 +1,44 @@
1
+ # encoding: UTF-8
2
+
3
+ module Baidu
4
+ module OAuth
5
+ module Flow
6
+ module Base
7
+ include Baidu::Support
8
+
9
+ # 此授权流程对应的 OAuth Client 实例
10
+ attr_reader :client
11
+
12
+ def initialize(client)
13
+ @client = client
14
+ end
15
+
16
+ def authorize_url(redirect_uri, params={})
17
+ query = authorize_query.update params
18
+ query.update({ client_id: self.client.client_id, redirect_uri: redirect_uri })
19
+ Util.clean_params query
20
+ uri = URI(self.client.site)
21
+ uri.path = authorize_endpoint
22
+ uri.query = Util.encode_params(query) unless Util.blank? query
23
+ uri.to_s
24
+ end
25
+
26
+ def get_token(code, redirect_uri=nil, params={})
27
+ body = token_body.update params
28
+ body.update({ client_id: self.client.client_id,
29
+ client_secret: self.client.client_secret,
30
+ redirect_uri: redirect_uri, code: code})
31
+ rest = self.client.post Baidu::OAuth::TOKEN_ENDPOINT, nil, body
32
+ return nil if rest.nil?
33
+ Baidu::Session.from rest
34
+ end
35
+
36
+ private
37
+
38
+ def authorize_endpoint
39
+ Baidu::OAuth::AUTHORIZATION_ENDPOINT
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,69 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'baidu/oauth/flow/base'
4
+
5
+ module Baidu
6
+ module OAuth
7
+ module Flow
8
+
9
+ # 采用Authorization Code获取Access Token的授权验证流程又被称为Web Server Flow,
10
+ # 适用于所有有Server端的应用,如Web/Wap站点、有Server端的手机/桌面客户端应用等。
11
+ #
12
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/oauth/authorization Authorization Code授权
13
+ class Code
14
+ include Base
15
+
16
+ # 通过 Device Code 来获取 Access Token
17
+ #
18
+ # @note 每一个 Authorization Code 的有效期为10分钟,并且只能使用一次,再次使用将无效。
19
+ #
20
+ # 如果用户在此页面同意授权,授权服务则将重定向用户浏览器到应用所指定的“redirect_uri”,
21
+ # 并附带上表示授权服务所分配的 Authorization Code 的 +code+ 参数,以及 state 参数(如果请求authorization code时带了这个参数)。
22
+ #
23
+ # @param [String] code 所获得的 Authorization Code (redirect_uri 附带的 {code} 参数)
24
+ # @return [Baidu::Session]
25
+ # @see #user_and_device_code
26
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/oauth/device 通过Device Code获取Access Token
27
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/oauth/overview Access Token生命周期
28
+ def get_token(code, redirect_uri); super end
29
+
30
+ # 获取 Authorization URL
31
+ # @param [String] redirect_uri 授权后要回调的URI,即接收Authorization Code的URI。
32
+ # 如果用户在授权过程中取消授权,会回调该URI,并在URI末尾附上error=access_denied参数。
33
+ # 对于无Web Server的应用,其值可以是“oob”,此时用户同意授权后,
34
+ # 授权服务会将Authorization Code直接显示在响应页面的页面中及页面title中。
35
+ # 非“oob”值的redirect_uri按照如下规则进行匹配:
36
+ # (1)如果开发者在“授权安全设置”中配置了“授权回调地址”,
37
+ # 则redirect_uri必须与“授权回调地址”中的某一个相匹配;
38
+ # (2)如果未配置“授权回调地址”,redirect_uri所在域名必须与开发者注册应用时
39
+ # 所提供的网站根域名列表或应用的站点地址(如果根域名列表没填写)的域名相匹配。
40
+ # @option params [String] :scope 以空格分隔的权限列表,若不传递此参数,代表请求用户的默认权限
41
+ # @option params [String] :state 用于保持请求和回调的状态,授权服务器在回调时(重定向用户浏览器到“redirect_uri”时),
42
+ # 会在Query Parameter中原样回传该参数。OAuth2.0标准协议建议,利用state参数来防止CSRF攻击
43
+ # @option params [String] :display 登录和授权页面的展现样式,默认为“page”
44
+ # @option params [Boolean] :force_login +true+ 表示加载登录页时强制用户输入用户名和口令,不会从cookie中读取百度用户的登陆状态
45
+ # @option params [Boolean] :confirm_login +true+ 表示且百度用户已处于登陆状态,会提示是否使用已当前登陆用户对应用授权
46
+ # @return [String]
47
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/oauth/list 权限列表
48
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/oauth/set 页面设置
49
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/oauth/redirect 授权回调地址
50
+ def authorize_url(redirect_uri, params={})
51
+ opts = {}.update(params)
52
+ opts.update({ force_login: 1 }) if params[:force_login]
53
+ opts.update({ confirm_login: 1 }) if params[:confirm_login]
54
+ super redirect_uri, opts
55
+ end
56
+
57
+ private
58
+
59
+ def authorize_query
60
+ { response_type: 'code', display: 'page' }
61
+ end
62
+
63
+ def token_body
64
+ { grant_type: 'authorization_code' }
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,75 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'baidu/oauth/flow/base'
4
+
5
+ module Baidu
6
+ module OAuth
7
+ module Flow
8
+
9
+ # 对设备应用而言,其流程由获取User Code和Device Code、引导用户去百度
10
+ # 填写User Code并授权、以及通过Device Code获取Access Token这3步组成。
11
+ #
12
+ # @note 使用此授权流程,对于终端类型的应用也非常方便,同时还可以获取 Refresh Token
13
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/oauth/device Device授权
14
+ class Device
15
+ include Base
16
+
17
+ # 获取 User Code 和 Device Code
18
+ # @example 返回的原始 JSON
19
+ # {
20
+ # "device_code": "a82hjs723h72h3a82hjs723h72h3vb",
21
+ # "user_code": "8sjiae3p",
22
+ # "verification_url": "https://openapi.baidu.com/oauth/2.0/device",
23
+ # "qrcode_url": "http://openapi.baidu.com/device/qrcode/6c6a8afee394f99e55eb25858/2c885vjk",
24
+ # "expires_in": 1800,
25
+ # "interval": 5
26
+ # }
27
+ #
28
+ # :device_code Device Code,设备需要保存,供下一步使用
29
+ # :user_code User Code,设备需要展示给用户
30
+ # :verification_url 用户填写User Code并进行授权的url,设备需要展示给用户
31
+ # :qrcode_url 用于二维码登陆的Qr Code图片url,用户用智能终端扫描该二维码之后,
32
+ # 可直接进入步骤2的登陆授权页面
33
+ # :expires_in Device Code/ Use Code的过期时间,单位为秒
34
+ # :interval 设备尝试获取Access Token的时间间隔,单位为秒,设备下一步会使用
35
+ #
36
+ # @param [String] scope 非必须参数,以空格分隔的权限列表
37
+ # @return [Hash]
38
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/oauth/list 权限列表
39
+ def user_and_device_code(scope=nil)
40
+ query = authorize_query.update({ client_id: self.client.client_id })
41
+ query[:scope] = scope unless scope.nil?
42
+ self.client.get(authorize_endpoint, query)
43
+ end
44
+
45
+ # 通过 Device Code 来获取 Access Token
46
+ #
47
+ # @param [String] code {#user_and_device_code} 所获得的 Device Code
48
+ # @return [Baidu::Session]
49
+ # @see #user_and_device_code
50
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/oauth/device 通过Device Code获取Access Token
51
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/oauth/overview Access Token生命周期
52
+ def get_token(code); super end
53
+
54
+ # @private
55
+ def authorize_url
56
+ raise NoMethodError, 'no such method in device flow'
57
+ end
58
+
59
+ private
60
+
61
+ def authorize_query
62
+ { response_type: 'device_code', display: nil }
63
+ end
64
+
65
+ def token_body
66
+ { grant_type: 'device_token' }
67
+ end
68
+
69
+ def authorize_endpoint
70
+ Baidu::OAuth::DEVICE_ENDPOINT
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
data/lib/baidu/pcs.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'baidu/core'
2
+ require 'baidu/pcs/client'
3
+
4
+ module Baidu
5
+
6
+ module PCS
7
+ SITE = 'https://pcs.baidu.com'
8
+ UPLOAD_SITE = 'https://c.pcs.baidu.com'
9
+ DOWNLOAD_SITE = 'https://d.pcs.baidu.com'
10
+ BASE_PATH = '/rest/2.0/pcs'
11
+ APPS_PATH_PREFIX = '/apps'
12
+ end
13
+
14
+ module Configure
15
+ # 全局配置 PCS 的文件目录
16
+ # @return [String]
17
+ attr_accessor :pcs_dir_name
18
+ end
19
+ end
@@ -0,0 +1,1090 @@
1
+ # encoding: UTF-8
2
+
3
+ module Baidu
4
+ module PCS
5
+
6
+ # Client 类封装实现了 Baidu PCS 的文件API,主要包括文件上传、下载、拷贝、删除、搜索、断点续传及缩略图等功能。
7
+ #
8
+ # 本文档多数内容取自 {http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list 文件API列表},
9
+ # 使用本 API 之前请先参考 Baidu PCS {http://developer.baidu.com/wiki/index.php?title=docs/pcs/guide/overview 开发指南},
10
+ # 准备好相应环境。
11
+ # @example
12
+ # require 'baidu/pcs'
13
+ #
14
+ # #全局配置,如已在其他地方配置过,可以忽略
15
+ # Baidu.config do |c|
16
+ # # ...
17
+ # c.pcs_dir_name = 'notes' # 可选,如此处未做配置,那么实例化 Client 时必须指定 dir_name
18
+ # # ...
19
+ # end
20
+ #
21
+ # # 用户授权完成之后获取的 access token
22
+ # access_token = 'xxxxxxxxxxxxxxxxxxxxxxxxx'
23
+ # # 使用全局配置 pcs_dir_name 作为文件目录
24
+ # client = Baidu::PCS::Client.new(access_token)
25
+ # # 不使用全局配置 pcs_dir_name
26
+ # # client = Baidu::PCS::Client.new(access_token, 'notes')
27
+ #
28
+ # File.open('/opt/ubuntu-12.04.3-server-amd64.iso', 'r') do |f|
29
+ # result = client.upload(f)
30
+ # # result 为 Hash 实例,是直接根据 Baidu REST API 返回的 JSON 转换而来
31
+ # # 具体键值及意义可参看各方法中 “返回的原始 JSON” 部分
32
+ # puts result[:path] # 输出上传后文件的保存路径
33
+ # puts result[:size] # 输出上传的文件大小
34
+ # puts result[:md5] # 输出上传的文件 md5 签名
35
+ # end
36
+ #
37
+ class Client
38
+ include Baidu::Support::Request
39
+
40
+ # 创建一个 +Baidu::PCS::Client+ 文件API 实例,通过此实例可以执行 文件API 调用
41
+ # @overload initialize(access_token, dir_name=Baidu.pcs_dir_name)
42
+ # @param access_token [String] 通过 Baidu OAuth API 获得的 Access Token
43
+ # @param dir_name [String] 开通 PCS API 权限时,填写的文件目录。如果未设置此参数,则使用全局配置
44
+ #
45
+ # @overload initialize(session, dir_name=Baidu.pcs_dir_name)
46
+ # @param session [Baidu::Session] 通过 Baidu OAuth API 获得的 Session
47
+ # @param dir_name [String] 开通 PCS API 权限时,填写的文件目录。如果未设置此参数,则使用全局配置
48
+ #
49
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/guide/api_approve 开通PCS API权限
50
+ def initialize(access_token_or_session, dir_name=Baidu.pcs_dir_name)
51
+ @dir_name = dir_name
52
+ @access_token = case access_token_or_session
53
+ when String then access_token_or_session
54
+ when Baidu::Session then access_token_or_session.access_token
55
+ end
56
+ raise ArgumentError, 'dir_name must not be blank' if Util.blank? @dir_name
57
+ raise ArgumentError, 'access_token must not be blank' if Util.blank? @access_token
58
+ @site = Baidu::PCS::SITE
59
+ @dir_path = "#{APPS_PATH_PREFIX}/#{@dir_name}"
60
+ end
61
+
62
+ # @!group 1 基本功能
63
+
64
+ # 空间配额信息
65
+ #
66
+ # 获取当前用户空间配额信息
67
+ #
68
+ # @example 返回的原始 JSON
69
+ # {"quota":15000000000,"used":5221166,"request_id":4043312634}
70
+ #
71
+ # :quota 空间配额,单位为字节
72
+ # :used 已使用空间大小,单位为字节
73
+ #
74
+ # @return [Hash]
75
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E7.A9.BA.E9.97.B4.E9.85.8D.E9.A2.9D.E4.BF.A1.E6.81.AF 空间配额信息
76
+ def quota
77
+ get "#{BASE_PATH}/quota", base_query('info')
78
+ end
79
+
80
+ # 上传单个文件
81
+ #
82
+ # @note 百度PCS服务目前支持最大2G的单个文件上传
83
+ # @note 文件大小超过 128MB 时,自动启用文件分块上传;
84
+ # 如果不想启用文件分块上传,可以通过 +block_upload: false+ 来关闭
85
+ #
86
+ # @example
87
+ # File.open('/opt/ubuntu-12.04.3-server-amd64.iso', 'r') do |f|
88
+ # c.upload(f, block_upload: true)
89
+ # end
90
+ #
91
+ # @example 返回的原始 JSON
92
+ # {
93
+ # "fs_id": 3916799999,
94
+ # "path": "/apps/album/1.png",
95
+ # "ctime": 1384493574,
96
+ # "mtime": 1384493574,
97
+ # "md5": "6c37219ba0d3dfdfa95ff6912e2c42b9",
98
+ # "size": 4914,
99
+ # "request_id": 3036629135
100
+ # }
101
+ #
102
+ # :fs_id 文件在PCS的临时唯一标识ID
103
+ # :path 该文件的绝对路径
104
+ # :ctime 文件创建时间
105
+ # :mtime 文件修改时间
106
+ # :md5 文件的md5签名
107
+ # :size 文件字节大小
108
+ #
109
+ # @param file [File] 待上传的文件
110
+ # @option options [String] :path 上传文件路径,含上传的文件名称(相对于应用根目录),默认为 +file+ 的文件名
111
+ # @option options [Boolean] :overwrite
112
+ # +true+: 表示覆盖同名文件,
113
+ # +false+: 表示生成文件副本并进行重命名,命名规则为“文件名_日期.后缀”,
114
+ # 默认为 +false+
115
+ # @option options [Boolean] :block_upload 对文件分块上传,仅当 +file+ 大小超过 8MB 时,此设置有效;
116
+ # 分块大小取决于文件大小,4GB 以下文件分块大小为 4MB
117
+ # @option options [Fixnum] :retry_times <b>分块上传时</b>,出错自动重试次数,默认 5
118
+ # @option options [Fixnum] :retry_waitsec <b>分块上传时</b>,出错自动重试暂停秒数,默认 30
119
+ # @return [Hash]
120
+ # @see #upload_block
121
+ # @see #create_super_file
122
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E4.B8.8A.E4.BC.A0.E5.8D.95.E4.B8.AA.E6.96.87.E4.BB.B6 上传单个文件
123
+ def upload(file, options={})
124
+ raise ArgumentError, 'file must be an instance of File' unless file.instance_of? File
125
+ path = options[:path] || File.basename(file)
126
+ size = file.size
127
+ if (options[:block_upload] && size >= 4*1024*1024*2) || # at least 2 blocks
128
+ (options[:block_upload].nil? && size >= 128*1024*1024)
129
+ block_size = 4*1024*1024
130
+ while block_size * 1024 < size # at most 1024 blocks1
131
+ block_size *= 2
132
+ end
133
+ offset, block_list = 0, []
134
+ max_retry_times = options[:retry_times] || 5
135
+ retry_waitsec = options[:retry_waitsec] || 30
136
+ loop do
137
+ with_retries(max_retry_times, retry_waitsec) do
138
+ rest = upload_block IO.binread(file, block_size, offset)
139
+ block_list << rest[:md5]
140
+ end
141
+ offset += block_size
142
+ break if offset >= size
143
+ end
144
+ with_retries(max_retry_times, retry_waitsec) do
145
+ create_super_file block_list, path, options[:overwrite]
146
+ end
147
+ else
148
+ raise IOError, 'file is too large (larger than 2G)' if size > 2*1024*1024*1024
149
+ query = build_upload_query 'upload', path, options[:overwrite]
150
+ post "#{BASE_PATH}/file", query, { file: file }, site: Baidu::PCS::UPLOAD_SITE
151
+ end
152
+ end
153
+
154
+ # 上传分片文件
155
+ #
156
+ # @note 百度PCS服务支持每次直接上传最大2G的单个文件。
157
+ # 如需支持上传超大文件(>2G),则可以通过组合调用分片文件上传方法和合并分片文件方法实现:
158
+ # 首先,将超大文件分割为2G以内的单文件,并调用 {#upload_block} 将分片文件依次上传;
159
+ # 其次,调用 {#create_super_file} ,完成分片文件的重组。
160
+ # 除此之外,如果应用中需要支持断点续传的功能,也可以通过分片上传文件并调用 {#create_super_file} 的方式实现。
161
+ #
162
+ # @example 返回的原始 JSON
163
+ # {"md5":"a7619410bca74850f985e488c9a0d51e","request_id":3238563823}
164
+ #
165
+ # :md5 上传内容的md5签名
166
+ #
167
+ # @param data [String] 上传的内容
168
+ # @return [Hash]
169
+ # @see #create_super_file
170
+ # @see #upload
171
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E5.88.86.E7.89.87.E4.B8.8A.E4.BC.A0.E2.80.94.E6.96.87.E4.BB.B6.E5.88.86.E7.89.87.E5.8F.8A.E4.B8.8A.E4.BC.A0 文件分片及上传
172
+ def upload_block(data)
173
+ query = build_upload_query 'upload', nil, nil, true
174
+ post "#{BASE_PATH}/file", query, { file: StringIO.new(data) }, site: Baidu::PCS::UPLOAD_SITE
175
+ end
176
+
177
+ # 合并分片文件
178
+ #
179
+ # 与分片文件上传 {#upload_block} 方法配合使用,可实现超大文件(>2G)上传,同时也可用于断点续传的场景
180
+ #
181
+ # @example 返回的原始 JSON
182
+ # {
183
+ # "path": "/apps/album/1.png",
184
+ # "size": 6844,
185
+ # "ctime": 1331197101,
186
+ # "mtime": 1331197101,
187
+ # "md5": "baa7c379639b74e9bf98c807498e1b64",
188
+ # "fs_id": 1548308694,
189
+ # "request_id": 4043313276
190
+ # }
191
+ #
192
+ # :path 该文件的绝对路径
193
+ # :size 文件字节大小
194
+ # :ctime 文件创建时间
195
+ # :mtime 文件修改时间
196
+ # :md5 文件的md5签名
197
+ # :fs_id 文件在PCS的临时唯一标识ID
198
+ #
199
+ # @param block_list [Array<String>] 数组,数组的取值为子文件内容的MD5;子文件至少2个,最多1024个
200
+ # @param path [String] 上传文件路径(含上传的文件名称)
201
+ # @param overwrite [Boolean]
202
+ # +true+: 表示覆盖同名文件
203
+ # +false+: 表示生成文件副本并进行重命名,命名规则为“文件名_日期.后缀”
204
+ # @return [Hash]
205
+ # @see #upload_block
206
+ # @see #upload
207
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E5.88.86.E7.89.87.E4.B8.8A.E4.BC.A0.E2.80.94.E5.90.88.E5.B9.B6.E5.88.86.E7.89.87.E6.96.87.E4.BB.B6 合并分片文件
208
+ def create_super_file(block_list, path, overwrite=false)
209
+ raise ArgumentError, 'block_list must be Array' unless block_list.instance_of? Array
210
+ raise ArgumentError, 'block_list size must be in 2..1024' unless block_list.length.between? 2, 1024
211
+ query = build_upload_query 'createsuperfile', path, overwrite
212
+ param = { block_list: block_list }
213
+ post "#{BASE_PATH}/file", query, param: JSON.dump(param)
214
+ end
215
+
216
+ # 下载单个文件
217
+ #
218
+ # Download 接口支持 HTTP 协议标准 range 定义,通过指定 range 的取值可以实现断点下载功能。
219
+ #
220
+ # @example 下载文件第 101 - 200 字节之间的内容
221
+ # File.open('logo.part2.png', 'w') do |f|
222
+ # f.write client.download('logo.png', begin: 100, end: 199)
223
+ # end
224
+ #
225
+ # @overload download(path, options={})
226
+ # 适合下载小文件,简单直接
227
+ # @note 下载大文件会占用过多的内存, 请使用 block 方法 #download(path, &block)
228
+ # @example
229
+ # File.open('logo.png', 'w') do |f|
230
+ # f.write client.download('logo.png')
231
+ # end
232
+ #
233
+ # @param path [String] 下载文件路径,路径相对于应用目录,从应用根目录起计算
234
+ # @option options [Fixnum] :begin 断点下载的开始字节索引
235
+ # @option options [Fixnum] :end 断点下载的结束字节索引
236
+ # @return [String] 直接返回文件内容
237
+ #
238
+ # @overload download(path, options={}, &block)
239
+ # 针对下载大文件优化的方法
240
+ # @example
241
+ # File.open('ubuntu.iso', 'w') do |f|
242
+ # client.download('ubuntu-12.04.3-server-amd64.iso') do |segment|
243
+ # f.write segment
244
+ # end
245
+ # end
246
+ #
247
+ # @param path [String] 下载文件路径,路径相对于应用目录,从应用根目录起计算
248
+ # @option options [Fixnum] :begin 断点下载的开始字节索引
249
+ # @option options [Fixnum] :end 断点下载的结束字节索引
250
+ # @yield [segment] 下载内容将以片段的方式提供
251
+ # @return [void]
252
+ #
253
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E4.B8.8B.E8.BD.BD.E5.8D.95.E4.B8.AA.E6.96.87.E4.BB.B6 下载单个文件
254
+ def download(path, options={}, &block)
255
+ site = Baidu::PCS::DOWNLOAD_SITE
256
+ query = { path: build_path(path) }.update(base_query 'download')
257
+ headers = if options[:begin] || options[:end]
258
+ range = "#{options[:begin] || 0}-#{options[:end]}"
259
+ { Range: "bytes=#{range}" }
260
+ end
261
+ if block_given?
262
+ get "#{BASE_PATH}/file", query, site: site, headers: headers, &block
263
+ else
264
+ get "#{BASE_PATH}/file", query, site: site, headers: headers, raw: true
265
+ end
266
+ end
267
+
268
+ # 创建目录
269
+ #
270
+ # 为当前用户创建一个目录
271
+ #
272
+ # @example 返回的原始 JSON
273
+ # {
274
+ # "fs_id": 1636599174,
275
+ # "path": "/apps/yunfom/music",
276
+ # "ctime": 1331183814,
277
+ # "mtime": 1331183814,
278
+ # "request_id": 4043312656
279
+ # }
280
+ #
281
+ # :fs_id 目录在PCS的临时唯一标识id
282
+ # :path 该目录的绝对路径
283
+ # :ctime 目录创建时间
284
+ # :mtime 目录修改时间
285
+ #
286
+ # @param path [String] 需要创建的目录,相对于应用根目录
287
+ # @return [Hash]
288
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E5.88.9B.E5.BB.BA.E7.9B.AE.E5.BD.95 创建目录
289
+ def mkdir(path)
290
+ query = { path: build_path(path, true) }
291
+ post "#{BASE_PATH}/file", query.update(base_query 'mkdir')
292
+ end
293
+
294
+ # 单个或批量获取文件/目录的元信息
295
+ #
296
+ # @example 返回的原始 JSON
297
+ # {
298
+ # "list": [
299
+ # {
300
+ # "fs_id": 3528850315,
301
+ # "path": "/apps/album/music/hello",
302
+ # "ctime": 1331184269,
303
+ # "mtime": 1331184269,
304
+ # "block_list": [
305
+ # "59ca0efa9f5633cb0371bbc0355478d8"
306
+ # ],
307
+ # "size": 13,
308
+ # "isdir": 1
309
+ # }
310
+ # ],
311
+ # "request_id": 4043312678
312
+ # }
313
+ #
314
+ # :fs_id 文件或目录在PCS的临时唯一标识ID
315
+ # :path 文件或目录的绝对路径
316
+ # :ctime 文件或目录的创建时间
317
+ # :mtime 文件或目录的最后修改时间
318
+ # :block_list 文件所有分片的md5数组JSON字符串
319
+ # :size 文件大小(byte)
320
+ # :isdir 是否是目录的标识符:“0”为文件,“1”为目录
321
+ # :ifhassubdir 是否含有子目录的标识符:“0”表示没有子目录,“1”表示有子目录
322
+ #
323
+ # @overload meta(path)
324
+ # 获取单个文件或目录的元信息
325
+ # @param path [String] 文件或目录路径,相对于应用根目录
326
+ # @return [Hash]
327
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E8.8E.B7.E5.8F.96.E5.8D.95.E4.B8.AA.E6.96.87.E4.BB.B6.2F.E7.9B.AE.E5.BD.95.E7.9A.84.E5.85.83.E4.BF.A1.E6.81.AF 获取单个文件/目录的元信息
328
+ #
329
+ # @overload meta(paths)
330
+ # 批量获取文件或目录的元信息
331
+ # @param paths [Array<String>] 文件或目录路径,相对于应用根目录
332
+ # @return [Hash]
333
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E6.89.B9.E9.87.8F.E8.8E.B7.E5.8F.96.E6.96.87.E4.BB.B6.2F.E7.9B.AE.E5.BD.95.E7.9A.84.E5.85.83.E4.BF.A1.E6.81.AF 批量获取文件/目录的元信息
334
+ #
335
+ # @return [Hash]
336
+ def meta(path)
337
+ meta_or_delete :meta, path
338
+ end
339
+
340
+ # 获取目录下的文件列表
341
+ #
342
+ # @example 调用示例
343
+ # client.list('apitest/movies', order: 'asc', by: 'time', limit: '2-10')
344
+ #
345
+ # @example 返回的原始 JSON
346
+ # {
347
+ # "list": [
348
+ # {
349
+ # "fs_id": 703525418,
350
+ # "path": "/apps/Backups/apitest/movies/1.mkv",
351
+ # "ctime": 1377136220,
352
+ # "mtime": 1384493344,
353
+ # "md5": "6366d2a234e8139c63dab707ec4569c3",
354
+ # "size": 74818037,
355
+ # "isdir": 0
356
+ # }
357
+ # ],
358
+ # "request_id": 4043312670
359
+ # }
360
+ #
361
+ # :fs_id 文件或目录在PCS的临时唯一标识id
362
+ # :path 文件或目录的绝对路径
363
+ # :ctime 文件或目录的创建时间
364
+ # :mtime 文件或目录的最后修改时间
365
+ # :md5 文件的md5值
366
+ # :size 文件大小(byte)
367
+ # :isdir 是否是目录的标识符:“0”为文件,“1”为目录
368
+ #
369
+ # @param path [String] 需要list的目录(相对于应用的根目录)
370
+ #
371
+ # @option options [String] :by 排序字段,缺省根据文件名排序:
372
+ # +time+(修改时间),
373
+ # +name+(文件名),
374
+ # +size+(大小,注意目录无大小)
375
+ # @option options [String] :order “+asc+”或“+desc+”,缺省采用降序排序:
376
+ # +asc+(升序),
377
+ # +desc+(降序)
378
+ # @option options [String] :limit 返回条目控制,参数格式为:n1-n2。
379
+ # 返回结果集的[n1, n2)之间的条目,缺省返回所有条目;n1从0开始
380
+ # @return [Hash]
381
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E8.8E.B7.E5.8F.96.E7.9B.AE.E5.BD.95.E4.B8.8B.E7.9A.84.E6.96.87.E4.BB.B6.E5.88.97.E8.A1.A8 获取目录下的文件列表
382
+ def list(path, options={})
383
+ query = { path: build_path(path) }
384
+ query[:by] = options[:by] || 'name'
385
+ query[:order] = options[:order] || 'desc'
386
+ query[:limit] = options[:limit]
387
+ get "#{BASE_PATH}/file", query.update(base_query 'list')
388
+ end
389
+
390
+ # 单个或批量移动文件/目录
391
+ #
392
+ # @example 返回的原始 JSON
393
+ # {
394
+ # "extra": {
395
+ # "list": [
396
+ # {
397
+ # "to": "/apps/album/test2/2.jpg",
398
+ # "from": "/apps/album/test1/1.jpg"
399
+ # }
400
+ # ]
401
+ # },
402
+ # "request_id": 2298812844
403
+ # }
404
+ #
405
+ # :from 执行move操作成功的源文件地址
406
+ # :to 执行move操作成功的目标文件地址
407
+ #
408
+ # @overload move(from, to)
409
+ # 移动单个文件/目录
410
+ # @note 调用move接口时,目标文件的名称如果和源文件不相同,将会在move操作时对文件进行重命名
411
+ # @param from [String] 源文件地址(包括文件名,相对于应用根目录)
412
+ # @param to [String] 目标文件地址(包括文件名,相对于应用根目录)
413
+ # @return [Hash] 如果move操作执行成功,那么response会返回执行成功的from/to列表
414
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E7.A7.BB.E5.8A.A8.E5.8D.95.E4.B8.AA.E6.96.87.E4.BB.B6.2F.E7.9B.AE.E5.BD.95 移动单个文件/目录
415
+ #
416
+ # @overload move(froms, tos)
417
+ # 批量移动文件/目录
418
+ # @note 批量执行move操作时,move接口一次对请求参数中的每个from/to进行操作;执行失败就会退出,成功就继续,返回执行成功的from/to列表
419
+ # @param froms [Array<String>] 源文件地址(包括文件名,相对于应用根目录)
420
+ # @param tos [Array<String>] 目标文件地址(包括文件名,相对于应用根目录)
421
+ # @return [Hash] 返回参数extra由list数组组成,list数组的两个元素分别是“from”和“to”,代表move操作的源地址和目的地址
422
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E6.89.B9.E9.87.8F.E7.A7.BB.E5.8A.A8.E6.96.87.E4.BB.B6.2F.E7.9B.AE.E5.BD.95 批量移动文件/目录
423
+ #
424
+ # @return [Hash]
425
+ def move(from, to)
426
+ move_or_copy :move, from, to
427
+ end
428
+
429
+ # 单个或批量拷贝文件/目录
430
+ #
431
+ # @example 返回的原始 JSON
432
+ # {
433
+ # "extra": {
434
+ # "list": [
435
+ # {
436
+ # "to": "/apps/album/test2/6.jpg",
437
+ # "from": "/apps/album/test1/6.jpg"
438
+ # }
439
+ # ]
440
+ # },
441
+ # "request_id": 2298812844
442
+ # }
443
+ #
444
+ # :from 执行copy操作成功的源文件地址
445
+ # :to 执行copy操作成功的目标文件地址
446
+ #
447
+ # @overload copy(from, to)
448
+ # 拷贝单个文件/目录
449
+ # @note move操作后,源文件被移动至目标地址;copy操作则会保留原文件
450
+ # @param from [String] 源文件地址(包括文件名,相对于应用根目录)
451
+ # @param to [String] 目标文件地址(包括文件名,相对于应用根目录)
452
+ # @return [Hash] 如果copy操作执行成功,那么response会返回执行成功的from/to列表
453
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E6.8B.B7.E8.B4.9D.E5.8D.95.E4.B8.AA.E6.96.87.E4.BB.B6.2F.E7.9B.AE.E5.BD.95 拷贝单个文件/目录
454
+ #
455
+ # @overload copy(froms, tos)
456
+ # 批量拷贝文件/目录
457
+ # @note 批量执行copy操作时,copy接口一次对请求参数中的每个from/to进行操作;执行失败就会退出,成功就继续,返回执行成功的from/to列表
458
+ # @param froms [Array<String>] 源文件地址(相对于应用根目录)
459
+ # @param tos [Array<String>] 目标文件地址(相对于应用根目录)
460
+ # @return [Hash] 返回参数extra由list数组组成,list数组的两个元素分别是“from”和“to”,代表copy操作的源地址和目的地址
461
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E6.89.B9.E9.87.8F.E6.8B.B7.E8.B4.9D.E6.96.87.E4.BB.B6.2F.E7.9B.AE.E5.BD.95 批量拷贝文件/目录
462
+ #
463
+ # @return [Hash]
464
+ def copy(from, to)
465
+ move_or_copy :copy, from, to
466
+ end
467
+
468
+ # 单个或批量删除文件/目录
469
+ # @note 文件/目录删除后默认临时存放在回收站内,删除文件或目录的临时存放不占用用户的空间配额;
470
+ # 存放有效期为10天,10天内可还原回原路径下,10天后则永久删除
471
+ #
472
+ # @example 返回的原始 JSON
473
+ # {"request_id":4043312866}
474
+ #
475
+ # @overload delete(path)
476
+ # 删除单个文件/目录
477
+ # @param path [String] 需要删除的文件或者目录路径
478
+ # @return [Hash]
479
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E5.88.A0.E9.99.A4.E5.8D.95.E4.B8.AA.E6.96.87.E4.BB.B6.2F.E7.9B.AE.E5.BD.95 删除单个文件/目录
480
+ #
481
+ # @overload delete(paths)
482
+ # 批量删除文件/目录
483
+ # @param paths [Array<String>] 需要删除的文件或者目录路径
484
+ # @return [Hash]
485
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E6.89.B9.E9.87.8F.E5.88.A0.E9.99.A4.E6.96.87.E4.BB.B6.2F.E7.9B.AE.E5.BD.95 批量删除文件/目录
486
+ #
487
+ # @return [Hash]
488
+ def delete(path)
489
+ meta_or_delete :delete, path
490
+ end
491
+
492
+ # 按文件名搜索文件
493
+ # @note 不支持查找目录
494
+ #
495
+ # @example 返回的原始 JSON
496
+ # {
497
+ # "list": [
498
+ # {
499
+ # "fs_id": 3528850315,
500
+ # "path": "/apps/album/music/hello",
501
+ # "ctime": 1331184269,
502
+ # "mtime": 1331184269,
503
+ # "block_list": [
504
+ # "59ca0efa9f5633cb0371bbc0355478d8"
505
+ # ],
506
+ # "size": 13,
507
+ # "isdir": 0
508
+ # }
509
+ # ],
510
+ # "request_id": 4043312670
511
+ # }
512
+ #
513
+ # :fs_id 目录在PCS的临时唯一标识ID。
514
+ # :path 该目录的绝对路径。
515
+ # :ctime 文件服务器创建时间。
516
+ # :mtime 文件服务器修改时间。
517
+ # :md5 文件的md5值。
518
+ # :size 文件大小(byte)。
519
+ # :isdir 是否是目录的标识符:“0”为文件,“1”为目录
520
+ #
521
+ # @param path [String] 需要检索的目录(相对于应用根目录)
522
+ # @param wd [String] 关键词
523
+ # @param re [Boolean] 是否递归:+true+ 表示递归,+false+ 表示不递归
524
+ # @return [Hash]
525
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E6.90.9C.E7.B4.A2 搜索
526
+ def search(path, wd, re=false)
527
+ path = build_path path
528
+ query = { path: path, wd: wd, re: (re ? 1 : 0) }
529
+ get "#{BASE_PATH}/file", query.update(base_query 'search')
530
+ end
531
+
532
+ # @!endgroup
533
+
534
+ # @!group 2 高级功能
535
+
536
+ # 获取指定图片文件的缩略图
537
+ # @note 限制条件:
538
+ # 原图大小(0, 10M];
539
+ # 原图类型: jpg、jpeg、bmp、gif、png;
540
+ # 目标图类型和原图的类型有关;例如:原图是gif图片,则缩略后也为gif图片
541
+ # @param path [String] 源图片的路径(相对于应用根目录)
542
+ # @param width [Fixnum] 指定缩略图的宽度,取值范围为(0,1600]
543
+ # @param height [Fixnum] 指定缩略图的高度,取值范围为(0,1600]
544
+ # @param quality [Fixnum] 缩略图的质量,默认为“100”,取值范围(0,100]
545
+ #
546
+ # @overload thumbnail(path, width, height, quality=100)
547
+ # @example
548
+ # File.open('logo_120.png', 'w') do |f|
549
+ # f.write client.thumbnail('logo.png', 120, 120)
550
+ # end
551
+ # @return [String] 缩略图文件内容
552
+ #
553
+ # @overload thumbnail(path, width, height, quality=100, &block)
554
+ # 使用 block 方式,减少内存占用率
555
+ # @example
556
+ # File.open('logo_1600.png', 'w') do |f|
557
+ # client.thumbnail('logo.png', 1600, 1600) do |segment|
558
+ # f.write segment
559
+ # end
560
+ # end
561
+ # @yield [segment] 内容将以片段的方式提供
562
+ # @return [void]
563
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E7.BC.A9.E7.95.A5.E5.9B.BE 缩略图
564
+ # @see #download
565
+ def thumbnail(path, width, height, quality=100, &block)
566
+ path = build_path path
567
+ query = { path: path, width: width, height: height, quality: quality }.update base_query('generate')
568
+ if block_given?
569
+ get "#{BASE_PATH}/thumbnail", query, &block
570
+ else
571
+ get "#{BASE_PATH}/thumbnail", query, raw: true
572
+ end
573
+ end
574
+
575
+ # 文件增量更新操作查询
576
+ # @note 本接口有数秒延迟,但保证返回结果为最终一致
577
+ #
578
+ # @example 返回的原始 JSON
579
+ # {
580
+ # "entries": {
581
+ # "/apps/album/6.png": {
582
+ # "fs_id": 3858723392,
583
+ # "path": "/apps/album/6.png",
584
+ # "size": 4914,
585
+ # "isdir": 0,
586
+ # "isdelete": 0,
587
+ # "revision": 0,
588
+ # "md5": "6c37219ba0d3dfdfa95ff6912e2c42b9",
589
+ # "mtime": 1384526979,
590
+ # "ctime": 1384526979
591
+ # },
592
+ # "/apps/ablum/logo.png": {
593
+ # "fs_id": 3866920660,
594
+ # "path": "/apps/album/logo.png",
595
+ # "size": 4914,
596
+ # "isdir": 0,
597
+ # "isdelete": 0,
598
+ # "revision": 0,
599
+ # "md5": "6c37219ba0d3dfdfa95ff6912e2c42b9",
600
+ # "mtime": 1384486230,
601
+ # "ctime": 1384021638
602
+ # }
603
+ # },
604
+ # "has_more": true,
605
+ # "reset": true,
606
+ # "cursor": "MxKx6UPie%2F9WzBkwALPrVWQlyxlmK0LgHG8zutwXp8oyC%2F2BQ%3D%3D...",
607
+ # "request_id": 3355443548
608
+ # }
609
+ #
610
+ # :entries k-v形式的列表,分为以下两种形式:
611
+ # 1. key为path,value为path对应的meta值,meta中isdelete=0为更新操作
612
+ # 如果path为文件,则更新path对应的文件;
613
+ # 如果path为目录,则更新path对应的目录信息,但不更新path下的文件。
614
+ # 2. key为path,value为path删除的meta信息,meta中“isdelete!=0”为删除操作
615
+ # isdelete=1 该文件被永久删除;
616
+ # isdelete=-1 该文件被放置进回收站;
617
+ # 如果path为文件,则删除该path对应的文件;
618
+ # 如果path为目录,则删除该path对应的目录和目录下的所有子目录和文件;
619
+ # 如果path在本地没有任何记录,则跳过本删除操作。
620
+ # :has_more True: 本次调用diff接口,增量更新结果服务器端无法一次性返回,客户端可以立刻再调用一次diff接口获取剩余结果;
621
+ # False: 截止当前的增量更新结果已经全部返回,客户端可以等待一段时间(1-2分钟)之后再diff一次查看是否有更新。
622
+ # :reset True: 服务器通知客户端,服务器端将按时间排序从第一条开始向客户端返回一份完整的数据列表;
623
+ # False:返回上次请求返回cursor之后的增量更新结果。
624
+ # :cursor 用于下一次调用diff接口时传入的断点参数。
625
+ #
626
+ # @param cursor [String] 用于标记更新断点:
627
+ # 首次调用cursor=null;
628
+ # 非首次调用,使用最后一次调用diff接口的返回结果中的cursor
629
+ # @return [Hash]
630
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E5.A2.9E.E9.87.8F.E6.9B.B4.E6.96.B0.E6.9F.A5.E8.AF.A2 增量更新查询
631
+ def diff(cursor='null')
632
+ query = { cursor: cursor }
633
+ get "#{BASE_PATH}/file", query.update(base_query 'diff')
634
+ end
635
+
636
+ # 视频转码
637
+ #
638
+ # 对视频文件进行转码,实现实时观看视频功能。可下载支持HLS/M3U8的媒体云播放器SDK配合使用。
639
+ # @note 目前这个接口支持的源文件格式如下:m3u8/m3u/asf/avi/flv/gif/mkv/mov/mp4/m4a/3gp/3g2/mj2/mpeg/ts/rm/rmvb/webm
640
+ # @param path [String] 需要下载的视频文件路径,需含源文件的文件名(相对于应用根目录)
641
+ # @param type [String] 目前支持以下格式:M3U8_320_240、M3U8_480_224、M3U8_480_360、M3U8_640_480和M3U8_854_480
642
+ # @return [String] 直接返回文件内容(在线播放的 URL 地址清单)
643
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E8.A7.86.E9.A2.91.E8.BD.AC.E7.A0.81 视频转码
644
+ def streaming(path, type, &block)
645
+ path = build_path path
646
+ query = { path: path, type: type }.update base_query('streaming')
647
+ if block_given?
648
+ get "#{BASE_PATH}/file", query, &block
649
+ else
650
+ get "#{BASE_PATH}/file", query, raw: true
651
+ end
652
+ end
653
+
654
+ # 获取流式文件列表
655
+ #
656
+ # 以视频、音频、图片及文档四种类型的视图获取所创建应用程序下的文件列表。
657
+ #
658
+ # @example 返回的原始 JSON
659
+ # {
660
+ # "total": 13,
661
+ # "start": 0,
662
+ # "limit": 1,
663
+ # "list": [
664
+ # {
665
+ # "path": "/apps/album/1.jpg",
666
+ # "size": 372121,
667
+ # "ctime": 1234567890,
668
+ # "mtime": 1234567890,
669
+ # "md5": "cb123afcc12453543ef",
670
+ # "fs_id": 12345,
671
+ # "isdir": 0
672
+ # }
673
+ # ]
674
+ # }
675
+ #
676
+ # :total 文件总数
677
+ # :start 起始数
678
+ # :limit 获取数
679
+ # :path 获取流式文件的绝对路径
680
+ # :block_list 分片MD5列表
681
+ # :size 流式文件的文件大小(byte)
682
+ # :mtime 流式文件在服务器上的修改时间
683
+ # :ctime 流式文件在服务器上的创建时间
684
+ # :fs_id 流式文件在PCS中的唯一标识ID
685
+ # :isdir “0”文件,“1”目录
686
+ #
687
+ # @param type [String] 类型分为video、audio、image及doc四种
688
+ # @option options [Fixnum] :start 返回条目控制起始值,默认为0
689
+ # @option options [Fixnum] :limit 返回条目控制长度,默认为1000
690
+ # @option options [String] :filter_path 需要过滤的前缀路径,如:/apps/album
691
+ # @return [Hash]
692
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E8.8E.B7.E5.8F.96.E6.B5.81.E5.BC.8F.E6.96.87.E4.BB.B6.E5.88.97.E8.A1.A8 获取流式文件列表
693
+ def stream_list(type, options={})
694
+ query = { type: type }.update options
695
+ get "#{BASE_PATH}/stream", query.update(base_query 'list')
696
+ end
697
+
698
+ # 秒传文件
699
+ # @note 被秒传文件必须大于256KB(即 256*1024 B);校验段为文件的前256KB,秒传接口需要提供校验段的MD5。
700
+ # @note 非强一致接口,上传后请等待1秒后再读取
701
+ #
702
+ # @example 返回的原始 JSON
703
+ # {
704
+ # "path": "/apps/album/1.jpg",
705
+ # "size": 372121,
706
+ # "ctime": 1234567890,
707
+ # "mtime": 1234567890,
708
+ # "md5": "cb123afcc12453543ef",
709
+ # "fs_id": 12345,
710
+ # "isdir": 0,
711
+ # "request_id": 12314124
712
+ # }
713
+ # :path 秒传文件的绝对路径
714
+ # :size 秒传文件的字节大小
715
+ # :ctime 秒传文件的创建时间。
716
+ # :mtime 秒传文件的修改时间
717
+ # :md5 秒传文件的md5签名
718
+ # :fs_id 秒传文件在PCS的唯一标识ID
719
+ # :isdir “0”文件 “1”目录
720
+ #
721
+ # @param path [String] 上传文件的全路径名(相对于应用根目录)
722
+ # @param content_length [Fixnum] 待秒传的文件长度
723
+ # @param content_md5 [String] 待秒传的文件的MD5
724
+ # @param slice_md5 [String] 待秒传文件校验段的MD5
725
+ # @param content_crc32 [String] 待秒传文件校验段的MD5
726
+ # @param overwrite [Boolean] +true+: 表示覆盖同名文件,+false+:表示生成文件副本并进行重命名,命名规则为“文件名_日期.后缀”
727
+ # @return [Hash]
728
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E7.A7.92.E4.BC.A0.E6.96.87.E4.BB.B6a 秒传文件
729
+ def rapid_upload(path, content_length, content_md5, slice_md5, content_crc32, overwrite=false)
730
+ path = build_path path, true
731
+ query = { :path => path,
732
+ :'content-length' => content_length,
733
+ :'content-md5' => content_md5,
734
+ :'slice-md5' => slice_md5,
735
+ :'content-crc32' => content_crc32 }
736
+ query[:ondup] = overwrite ? 'overwrite' : 'newcopy'
737
+ post "#{BASE_PATH}/file", query.update(base_query 'rapidupload')
738
+ end
739
+
740
+ # 添加离线下载任务,实现单个文件离线下载
741
+ #
742
+ # @example 返回的原始 JSON
743
+ # {"task_id":432432432432432,"request_id":3372220525}
744
+ #
745
+ # :task_id 任务ID号
746
+ #
747
+ # @param source_url [String] 源文件的URL
748
+ # @option options :save_path [String] 下载后的文件保存路径(相对于应用根目录)
749
+ # 如果未指定则获取源文件的URL名称,如果获取失败则使用当前时间
750
+ # @option options :timeout [Fixnum] 下载超时时间
751
+ # @option options :expires [Fixnum] 请求失效时间,如果有,则会校验
752
+ # @option options :rate_limit [Fixnum] 下载限速,默认不限速
753
+ # @option options :callback [String] 下载完毕后的回调
754
+ # @return [Hash]
755
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E6.B7.BB.E5.8A.A0.E7.A6.BB.E7.BA.BF.E4.B8.8B.E8.BD.BD.E4.BB.BB.E5.8A.A1 添加离线下载任务
756
+ def add_task(source_url, options={})
757
+ query = { source_url: source_url }
758
+ query[:timeout] = options.delete(:timeout) || 3600
759
+ save_path = options.delete(:save_path)
760
+ unless save_path
761
+ save_path = URI(source_url).path.split('/').last || Time.now.localtime.to_s
762
+ end
763
+ save_path = build_path save_path, true
764
+ query[:save_path] = save_path
765
+ query.update options
766
+ post "#{BASE_PATH}/services/cloud_dl", query.update(base_query 'add_task')
767
+ end
768
+
769
+ # 精确查询离线下载任务
770
+ #
771
+ # 根据任务ID号,查询离线下载任务信息及进度信息
772
+ #
773
+ # @example 查询进度信息,返回的原始 JSON
774
+ # {
775
+ # "task_info": {
776
+ # "23998044": {
777
+ # "create_time": "1384703711",
778
+ # "start_time": "1384703711",
779
+ # "finish_time": "1384703717",
780
+ # "file_size": "0",
781
+ # "finished_size": "0",
782
+ # "task_name": "1.dmg",
783
+ # "save_path": "/apps/album/1.dmg",
784
+ # "source_url": "https://example.com/1.dmg",
785
+ # "status": "1",
786
+ # "result": 0
787
+ # }
788
+ # },
789
+ # "request_id": 631260401
790
+ # }
791
+ #
792
+ # :result 0查询成功,结果有效,1要查询的task_id不存在
793
+ # :status 0下载成功,1下载进行中 2系统错误,3资源不存在,4下载超时,
794
+ # 5资源存在但下载失败 6存储空间不足 7任务取消
795
+ # :status status为0、1时,其余字段有效
796
+ #
797
+ # @example 查询任务信息,返回的原始 JSON
798
+ # {
799
+ # "task_info": {
800
+ # "23654044": {
801
+ # "source_url": "https://example.com/1.dmg",
802
+ # "finished_size": "0",
803
+ # "save_path": "/apps/album/1.dmg",
804
+ # "rate_limit": "0",
805
+ # "timeout": "3600",
806
+ # "callback": "",
807
+ # "status": "7",
808
+ # "create_time": "1384703711",
809
+ # "task_name": "1.dmg",
810
+ # "od_type": "0",
811
+ # "result": 0
812
+ # }
813
+ # },
814
+ # "request_id": 689959608
815
+ # }
816
+ #
817
+ # :result 0查询成功,结果有效,1要查询的task_id不存在
818
+ # :source_url 下载数据源地址
819
+ # :save_path 下载完成后的存放地址
820
+ # :status 0下载成功,1下载进行中 2系统错误,3资源不存在,4下载超时,
821
+ # 5资源存在但下载失败 6存储空间不足 7目标地址数据已存在 8任务取消
822
+ # :create_time 任务创建时间
823
+ #
824
+ # @overload query_task(task_id, options={})
825
+ # @param task_id [String] 要查询的任务ID
826
+ # @return [Hash]
827
+ #
828
+ # @overload query_task(task_ids, options={})
829
+ # @param task_ids [Array<String>] 要查询的任务ID
830
+ # @return [Hash]
831
+ #
832
+ # @option options [Fixnum] :op_type 0:查任务信息;1:查进度信息;默认为1
833
+ # @option options [Fixnum] :expires 请求失效时间,如果有,则会校验
834
+ # @return [Hash]
835
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E7.B2.BE.E7.A1.AE.E6.9F.A5.E8.AF.A2.E7.A6.BB.E7.BA.BF.E4.B8.8B.E8.BD.BD.E4.BB.BB.E5.8A.A1 精确查询离线下载任务
836
+ def query_task(task_ids, options={})
837
+ task_ids = task_ids.join(',') if task_ids.is_a? Array
838
+ query = { task_ids: task_ids }.update options
839
+ post "#{BASE_PATH}/services/cloud_dl", query.update(base_query 'query_task')
840
+ end
841
+
842
+ # 查询离线下载任务列表
843
+ #
844
+ # 查询离线下载任务ID列表及任务信息
845
+ #
846
+ # @example 不包含任务信息,返回的原始 JSON
847
+ # {"task_info":[{"task_id":"26"}, {"task_id":"27"}],"total":"2","request_id":1283164486}
848
+ #
849
+ # @example 包含任务信息,返回的原始 JSON
850
+ # {
851
+ # "task_info": [
852
+ # {
853
+ # "task_id": "26",
854
+ # "source_url": "https://example.com/1.dmg",
855
+ # "save_path": "/apps/album/1.dmg",
856
+ # "rate_limit": "100",
857
+ # "timeout": "10000",
858
+ # "callback": "",
859
+ # "status": "1",
860
+ # "create_time": "1347449048"
861
+ # }
862
+ # ],
863
+ # "total": "1",
864
+ # "request_id": 1285732167
865
+ # }
866
+ #
867
+ # @param [Hash] options 皆为可选参数
868
+ # @option options [Fixnum] :start 查询任务起始位置,默认为0
869
+ # @option options [Fixnum] :limit 设定返回任务数量,默认为10
870
+ # @option options [Fixnum] :asc 0:降序;1:升序;默认为0
871
+ # @option options [Fixnum] :need_task_info 是否需要返回任务信息:0:不需要;1:需要;默认为1
872
+ # @option options [Fixnum] :status 任务状态
873
+ # @option options [Fixnum] :create_time 任务创建时间,note:此参数似乎无效
874
+ # @option options [String] :source_url 源地址URL
875
+ # @option options [String] :save_path 文件保存路径(相对于应用根目录)
876
+ # @option options [Fixnum] :expires 请求失效时间,如果有,则会校验
877
+ # @return [Hash]
878
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E6.9F.A5.E8.AF.A2.E7.A6.BB.E7.BA.BF.E4.B8.8B.E8.BD.BD.E4.BB.BB.E5.8A.A1.E5.88.97.E8.A1.A8 查询离线下载任务列表
879
+ def list_task(options={})
880
+ query = options.dup
881
+ post "#{BASE_PATH}/services/cloud_dl", query.update(base_query 'list_task')
882
+ end
883
+
884
+ # 取消离线下载任务
885
+ #
886
+ # @example 返回的原始 JSON
887
+ # {"request_id":12394838223}
888
+ #
889
+ # @param task_id [String] 要取消的任务ID号
890
+ # @param expires [Fixnum] 请求失效时间,如果有,则会校验
891
+ # @return [Hash]
892
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E5.8F.96.E6.B6.88.E7.A6.BB.E7.BA.BF.E4.B8.8B.E8.BD.BD.E4.BB.BB.E5.8A.A1 取消离线下载任务
893
+ def cancel_task(task_id, expires=nil)
894
+ query = { task_id: task_id, expires: expires }
895
+ post "#{BASE_PATH}/services/cloud_dl", query.update(base_query 'cancel_task')
896
+ end
897
+
898
+ # @!endgroup
899
+
900
+ # @!group 3 回收站
901
+
902
+ # 查询回收站文件
903
+ #
904
+ # 获取回收站中的文件及目录列表
905
+ #
906
+ # @example 返回的原始 JSON
907
+ # {
908
+ # "list": [
909
+ # {
910
+ # "fs_id": 1579174,
911
+ # "path": "/apps/album/2.7z",
912
+ # "ctime": 1361934614,
913
+ # "mtime": 1361934625,
914
+ # "md5": "1131170ac11cfbec411a5e8d4e111769",
915
+ # "size": 10730431,
916
+ # "isdir": 0
917
+ # },
918
+ # {
919
+ # "fs_id": 304521061,
920
+ # "path": "/apps/album/3.7z",
921
+ # "ctime": 1361934605,
922
+ # "mtime": 1361934625,
923
+ # "md5": "9552bf5e5abdf962e2de94be243bec7c",
924
+ # "size": 4287611,
925
+ # "isdir": 0
926
+ # }
927
+ # ],
928
+ # "request_id": 3779302504
929
+ # }
930
+ #
931
+ # :fs_id 目录在PCS上的临时唯一标识
932
+ # :path 该目录的绝对路径
933
+ # :ctime 文件在服务器上的创建时间
934
+ # :mtime 文件在服务器上的修改时间
935
+ # :md5 分片MD5
936
+ # :size 文件大小(byte)
937
+ # :isdir 是否是目录的标识符:
938
+ #
939
+ # @param start [Fixnum] 返回条目的起始值
940
+ # @param limit [Fixnum] 返回条目的长度
941
+ # @return [Hash]
942
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E6.9F.A5.E8.AF.A2.E5.9B.9E.E6.94.B6.E7.AB.99.E6.96.87.E4.BB.B6 查询回收站文件
943
+ def listrecycle(start=0, limit=1000)
944
+ query = { start:start, limit: limit }
945
+ get "#{BASE_PATH}/file", query.update(base_query 'listrecycle')
946
+ end
947
+
948
+ # 单个或批量还原文件/目录
949
+ # @note 非强一致接口,调用后请sleep 1秒读取
950
+ # @overload restore(fs_id)
951
+ # 还原单个文件或目录
952
+ #
953
+ # @example 还原成功返回的原始 JSON
954
+ # {"extra":{"list":[{"fs_id":"1356099017"}]},"request_id":3775323016}
955
+ #
956
+ # @param [String] fs_id 所还原的文件或目录在PCS的临时唯一标识ID
957
+ # @return [Hash]
958
+ #
959
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E8.BF.98.E5.8E.9F.E5.8D.95.E4.B8.AA.E6.96.87.E4.BB.B6.E6.88.96.E7.9B.AE.E5.BD.95 还原单个文件或目录
960
+ #
961
+ # @overload restore(fs_ids)
962
+ # 批量还原文件或目录
963
+ #
964
+ # @example 全部还原成功返回的原始 JSON
965
+ # {"extra":{"list":[{"fs_id":"3275514389"}]},"request_id":3859098573}
966
+ # @example 部分还原成功返回的原始 JSON
967
+ # {"error_code":31078,"error_msg":"invalid fs id","extra":{"list":[{"fs_id":"706533300"}]},"request_id":3825218191}
968
+ # @example 全部还原失败返回的原始 JSON
969
+ # {"error_code":31078,"error_msg":"invalid fs id","extra":{"list":[]},"request_id":805400333}
970
+ #
971
+ # @param [Array<String>] fs_ids 所还原的文件或目录在PCS的临时唯一标识ID的数组
972
+ # @return [Hash]
973
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E6.89.B9.E9.87.8F.E8.BF.98.E5.8E.9F.E6.96.87.E4.BB.B6.E6.88.96.E7.9B.AE.E5.BD.95 批量还原文件或目录
974
+ #
975
+ # @return [Hash]
976
+ def restore(fs_ids)
977
+ query = case fs_ids
978
+ when String
979
+ { fs_id: fs_ids }
980
+ when Array
981
+ fs_ids = fs_ids.map { |id| { fs_id: id } }
982
+ { param: JSON.dump({ list: fs_ids }) }
983
+ else
984
+ raise ArgumentError, 'fs_id(s) must be kind of String or Array'
985
+ end
986
+ post "#{BASE_PATH}/file", query.update(base_query 'restore')
987
+ end
988
+
989
+ # 清空回收站
990
+ #
991
+ # @example 返回的原始 JSON
992
+ # {"request_id":2307473052}
993
+ #
994
+ # @return [Hash]
995
+ # @see http://developer.baidu.com/wiki/index.php?title=docs/pcs/rest/file_data_apis_list#.E6.B8.85.E7.A9.BA.E5.9B.9E.E6.94.B6.E7.AB.99 清空回收站
996
+ def empty
997
+ query = { type: 'recycle' }
998
+ post "#{BASE_PATH}/file", query.update(base_query 'delete')
999
+ end
1000
+
1001
+ # @!endgroup
1002
+
1003
+ private
1004
+
1005
+ def base_query(method)
1006
+ { method: method, access_token: @access_token }
1007
+ end
1008
+
1009
+ # 构建上传 query hash
1010
+ # 如果 +path+ 中包含 +"\\ ? | " > < : *"+, 则被替换为 +"_"+
1011
+ # 如果 +path+ 开头结尾包含 +"."+ 或 空白符, 则将被删除
1012
+ #
1013
+ # [method] api 中 method 的固定值
1014
+ # [path] 上传文件路径(含上传的文件名称)
1015
+ # [overwrite] +true+: 表示覆盖同名文件
1016
+ # +false+: 表示生成文件副本并进行重命名,命名规则为“文件名_日期.后缀”
1017
+ # [tmpfile] +true+: 表示使用分块上传,此时将忽略上述四个参数
1018
+ # +false+: 表示正常上传,不使用分块上传
1019
+ def build_upload_query(method, path, overwrite=false, tmpfile=false)
1020
+ if tmpfile
1021
+ query = { type: 'tmpfile' }
1022
+ else
1023
+ path = build_path path, true
1024
+ ondup = overwrite ? 'overwrite' : 'newcopy'
1025
+ query = { path: path, ondup: ondup }
1026
+ end
1027
+
1028
+ #文档提醒:file需通过POST表单进行传递,其他参数则需通过query_string进行传递。
1029
+ query.update base_query(method)
1030
+ end
1031
+
1032
+ def build_path(path, edit=false)
1033
+ if Util.blank? path
1034
+ raise ArgumentError, 'path must not be blank'
1035
+ end
1036
+ if path.bytesize > 1000
1037
+ raise ArgumentError, 'path length must not be greater than 1000'
1038
+ end
1039
+ path = Util.edit_path path if edit # edit path to meet api rule
1040
+ File.join @dir_path, path # add app path
1041
+ end
1042
+
1043
+ def with_retries(max_retry_times, waitsec)
1044
+ retry_times = 0
1045
+ begin
1046
+ yield
1047
+ rescue
1048
+ raise if (retry_times += 1) > max_retry_times
1049
+ sleep waitsec
1050
+ retry
1051
+ end
1052
+ end
1053
+
1054
+ def move_or_copy(method, from, to)
1055
+ body = case from
1056
+ when String
1057
+ raise ArgumentError, 'from and to must have the same type' unless to.is_a? String
1058
+ from = build_path from
1059
+ to = build_path to, true
1060
+ { from: from, to: to }
1061
+ when Array
1062
+ raise ArgumentError, 'from and to must have the same type' unless to.is_a? Array
1063
+ raise ArgumentError, 'from or to must not be empty' if from.empty? || to.empty?
1064
+ raise ArgumentError, 'from and to must have the same size' unless from.size == to.size
1065
+ list = []
1066
+ from.zip(to) { |arr| list << { from: build_path(arr[0]), to: build_path(arr[1], true) } }
1067
+ { param: JSON.dump({ list: list }) }
1068
+ else
1069
+ raise ArgumentError, 'from and to must be kind of String or Array'
1070
+ end
1071
+ post "#{BASE_PATH}/file", base_query(method.to_s), body
1072
+ end
1073
+
1074
+ def meta_or_delete(method, path)
1075
+ query = case path
1076
+ when String
1077
+ path = build_path path
1078
+ { path: path }
1079
+ when Array
1080
+ raise ArgumentError, 'path(s) must not be empty' if path.empty?
1081
+ paths = path.map { |p| { path: build_path(p) } }
1082
+ { param: JSON.dump({ list: paths }) }
1083
+ else
1084
+ raise ArgumentError, 'path must be kind of String or Array'
1085
+ end
1086
+ get "#{BASE_PATH}/file", query.update(base_query method.to_s)
1087
+ end
1088
+ end
1089
+ end
1090
+ end