diff --git a/Gemfile.lock b/Gemfile.lock index fb6af3e..b92a415 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - qiniu-rs (2.2.1) + qiniu-rs (2.3.0) json (~> 1.7.3) mime-types (~> 1.19) rest-client (~> 1.6.7) diff --git a/docs/README.md b/docs/README.md index e1fe354..4ab6a77 100644 --- a/docs/README.md +++ b/docs/README.md @@ -14,8 +14,10 @@ title: Ruby SDK 使用指南 | 七牛云存储 - [使用](#Usage) - [应用接入](#establish_connection!) - [Ruby On Rails 应用初始化设置](#ror-init) - - [获取用于上传文件的临时授权URL](#put_auth) - [上传文件](#upload) + - [服务端上传流程](#upload-server-side) + - [客户端上传流程](#upload-client-side) + - [获取用于上传文件的临时授权URL](#put_auth) - [查看文件属性信息](#stat) - [获取文件下载链接(含文件属性信息)](#get) - [只获取文件下载链接](#download) @@ -88,9 +90,58 @@ title: Ruby SDK 使用指南 | 七牛云存储 接下来,我们会逐一介绍此 SDK 提供的其他方法。 + + +### 上传文件 + + + +#### 服务端上传流程 + +通过 `Qiniu::RS.put_file()` 方法可在客户方的业务服务器上直接往七牛云存储上传文件。该函数规格如下: + + Qiniu::RS.put_file :file => file_path, + :key => record_id, + :bucket => bucket_name, + :mime_type => file_mime_type, + :note => some_notes, + :enable_crc32_check => true + +**参数** + +:file +: 必须,字符串类型(String),本地文件可被读取的有效路径 + +:bucket +: 必须,字符串类型(String),类似传统数据库里边的表名称,我们暂且将其叫做“资源表”,指定将该数据属性信息存储到具体的资源表中 。 + +:key +: 必须,字符串类型(String),类似传统数据库里边某个表的主键ID,给每一个文件一个UUID用于进行标示。 + +:mime_type +: 可选,字符串类型(String),文件的 mime-type 值。如若不传入,SDK 会自行计算得出,若计算失败缺省使用 application/octet-stream 代替之。 + +:note +: 可选,字符串类型(String),备注信息。 + +:enable_crc32_check +: 可选,Boolean 类型,是否启用文件上传 crc32 校验,默认为 false 。 + +**返回值** + +上传成功,返回 `true`,否则返回 `false` 。 + +**针对 NotFound 处理** + +您可以上传一个应对 HTTP 404 出错处理的文件,当您 [创建公开外链](#publish) 后,若公开的外链找不到该文件,即可使用您上传的“自定义404文件”代替之。要这么做,您只须使用 `Qiniu::RS.put_file` 函数上传一个 `key` 为固定字符串类型的值 `errno-404` 即可。 + + + +#### 客户端上传流程 + -### 获取用于上传文件的临时授权URL +#### 获取用于上传文件的临时授权URL Qiniu::RS.put_auth(expires_in = nil, callback_url = nil) @@ -112,56 +163,8 @@ callback_url remote_upload_url = Qiniu::RS.put_auth(60, 'http://api.example.com/notifications/qiniu-rs') -如果您的网络程序是从云端(服务端程序)到终端(手持设备应用)的架构模型,且终端用户有使用您移动端App上传文件(比如照片或视频)的需求,可以把您服务器得到的此 `remote_upload_url` 返回给手持设备端的App,然后您的移动 App 可以使用 [七牛云存储 Objective-SDK (iOS)](http://docs.qiniutek.com/v2/sdk/objc/) 或 [七牛云存储 Java-SDK(Android)](http://docs.qiniutek.com/v2/sdk/java/) 的相关上传函数或参照 [七牛云存储API之文件上传](http://docs.qiniutek.com/v2/api/io/#rs-PutFile) 往该 `remote_upload_url` 上传文件。这样,您的终端用户即可把数据(比如图片或视频)直接上传到七牛云存储服务器上无须经由您的服务端中转,而且在上传之前,七牛云存储做了智能加速,终端用户上传数据始终是离他物理距离最近的存储节点。当终端用户上传成功后,七牛云存储服务端会向您指定的 `callback_url` 发送回调数据。在此示例程序中,七牛云存储服务端会将一组关于终端用户上传的数据的属性信息通过 HTTP POST 以 application/x-www-form-urlencoded 编码的方式发送到 `http://api.example.com/notifications/qiniu-rs` 这个地址,假设该地址是您业务服务器用于接收处理回调信息的地址。 +如果您的网络程序是从云端(服务端程序)到终端(手持设备应用)的架构模型,且终端用户有使用您移动端App上传文件(比如照片或视频)的需求,可以把您服务器得到的此 `remote_upload_url` 返回给手持设备端的App,然后您的移动 App 可以使用 [七牛云存储 Objective-SDK (iOS)](http://docs.qiniutek.com/v2/sdk/objc/) 或 [七牛云存储 Java-SDK(Android)](http://docs.qiniutek.com/v2/sdk/java/) 的相关上传函数或参照 [七牛云存储API之文件上传](http://docs.qiniutek.com/v2/api/io/#rs-PutFile) 往该 `remote_upload_url` 上传文件。这样,您的终端用户即可把数据(比如图片或视频)直接上传到七牛云存储服务器上无须经由您的服务端中转,而且在上传之前,七牛云存储做了智能加速,终端用户上传数据始终是离他物理距离最近的存储节点。当终端用户上传成功后,七牛云存储服务端会向您指定的 `callback_url` 发送回调数据。在此示例程序中,七牛云存储服务端会将一组关于终端用户上传的数据的属性信息通过 HTTP POST 以 `application/x-www-form-urlencoded` 编码的方式发送到 `http://api.example.com/notifications/qiniu-rs` 这个地址,假设该地址是您业务服务器用于接收处理回调信息的地址。 - - -### 上传文件 - -通过 `Qiniu::RS.put_auth` 函数取得 `remote_upload_url` 之后,即可往该 URL 上传 multipart/form-data 编码格式的数据流。前面讲解了移动 App 拿到 `remote_upload_url` 之后上传数据的流程,如果您的服务端需要上传数据,依然可以使用此 SDK 提供的 `Qiniu::RS.upload` 函数。该函数规格如下: - - Qiniu::RS.upload :url => remote_upload_url, - :file => file_path, - :key => record_id, - :bucket => bucket_name, - :mime_type => file_mime_type, - :note => some_notes, - :callback_params => {}, - :enable_crc32_check => true - -**参数** - -:url -: 必须,即通过 `Qiniu::RS.put_auth` 函数取得 `remote_upload_url` - -:file -: 必须,字符串类型(String),本地文件可被读取的有效路径 - -:bucket -: 必须,字符串类型(String),类似传统数据库里边的表名称,我们暂且将其叫做“资源表”,指定将该数据属性信息存储到具体的资源表中 。 - -:key -: 必须,字符串类型(String),类似传统数据库里边某个表的主键ID,给每一个文件一个UUID用于进行标示。 - -:mime_type -: 可选,字符串类型(String),文件的 mime-type 值。如若不传入,SDK 会自行计算得出,若计算失败缺省使用 application/octet-stream 代替之。 - -:note -: 可选,字符串类型(String),备注信息。 - -:callback_params -: 可选,k/v 对的 Hash 结构,缺省为:`{:bucket => bucket, :key => key, :mime_type => mime_type}`,用于七牛云存储服务端通过 HTTP POST 以 application/x-www-form-urlencoded 编码的方式发送到 `Qiniu::RS.put_auth` 函数指定的 `callback_url` 。 - -:enable_crc32_check -: 可选,Boolean 类型,是否启用文件上传 crc32 校验,默认为 false 。 - -**返回值** - -上传成功,返回 `true`,否则返回 `false` 。 - -**针对 NotFound 处理** - -您可以上传一个应对 HTTP 404 出错处理的文件,当您 [创建公开外链](#publish) 后,若公开的外链找不到该文件,即可使用您上传的“自定义404文件”代替之。要这么做,您只须使用 `Qiniu::RS.upload` 函数上传一个 `key` 为固定字符串类型的值 `errno-404` 即可。 diff --git a/lib/qiniu/rs.rb b/lib/qiniu/rs.rb index 08ccd60..0335d64 100755 --- a/lib/qiniu/rs.rb +++ b/lib/qiniu/rs.rb @@ -35,13 +35,23 @@ module Qiniu end def upload opts = {} - code, data = IO.put_file(opts[:url], - opts[:file], + code, data = IO.upload_file(opts[:url], + opts[:file], + opts[:bucket], + opts[:key], + opts[:mime_type], + opts[:note], + opts[:callback_params], + opts[:enable_crc32_check]) + code == StatusOK + end + + def put_file opts = {} + code, data = IO.put_file(opts[:file], opts[:bucket], opts[:key], opts[:mime_type], opts[:note], - opts[:callback_params], opts[:enable_crc32_check]) code == StatusOK end diff --git a/lib/qiniu/rs/io.rb b/lib/qiniu/rs/io.rb index fbc0499..54252ec 100755 --- a/lib/qiniu/rs/io.rb +++ b/lib/qiniu/rs/io.rb @@ -20,20 +20,34 @@ module Qiniu Auth.request(url) end - def put_file(url, local_file, bucket = nil, key = nil, mime_type = nil, custom_meta = nil, callback_params = nil, enable_crc32_check = false) + def upload_file(url, local_file, bucket, key = nil, mime_type = nil, custom_meta = nil, callback_params = nil, enable_crc32_check = false) + action_params = _generate_action_params(local_file, bucket, key, mime_type, custom_meta, enable_crc32_check) + callback_params = {:bucket => bucket, :key => key, :mime_type => mime_type} if callback_params.nil? + callback_query_string = Utils.generate_query_string(callback_params) + Utils.upload_multipart_data(url, local_file, action_params, callback_query_string) + end + + def put_file(local_file, bucket, key = nil, mime_type = nil, custom_meta = nil, enable_crc32_check = false) + action_params = _generate_action_params(local_file, bucket, key, mime_type, custom_meta, enable_crc32_check) + url = Config.settings[:io_host] + action_params + post_data = {:file => File.new(local_file, 'rb'), :multipart => true} + options = {:qbox_signature_token => Utils.generate_qbox_signature(url, nil)} + Utils.send_multipart_request url, post_data, options + end + + private + def _generate_action_params(local_file, bucket, key = nil, mime_type = nil, custom_meta = nil, enable_crc32_check = false) raise NoSuchFileError, local_file unless File.exist?(local_file) key = Digest::SHA1.hexdigest(local_file + Time.now.to_s) if key.nil? entry_uri = bucket + ':' + key - if mime_type.nil? + if mime_type.nil? || mime_type.empty? mime = MIME::Types.type_for local_file mime_type = mime.empty? ? 'application/octet-stream' : mime[0].content_type end action_params = '/rs-put/' + Utils.urlsafe_base64_encode(entry_uri) + '/mimeType/' + Utils.urlsafe_base64_encode(mime_type) action_params += '/meta/' + Utils.urlsafe_base64_encode(custom_meta) unless custom_meta.nil? action_params += '/crc32/' + Utils.crc32checksum(local_file).to_s if enable_crc32_check - callback_params = {:bucket => bucket, :key => key, :mime_type => mime_type} if callback_params.nil? - callback_query_string = Utils.generate_query_string(callback_params) - Utils.upload_multipart_data(url, local_file, action_params, callback_query_string) + action_params end end diff --git a/lib/qiniu/rs/utils.rb b/lib/qiniu/rs/utils.rb index 272115b..700e1bb 100755 --- a/lib/qiniu/rs/utils.rb +++ b/lib/qiniu/rs/utils.rb @@ -91,19 +91,22 @@ module Qiniu end end - def upload_multipart_data(url, filepath, action_string, callback_query_string = '') + def send_multipart_request(url, post_data, options = {}) code, data = 0, {} begin header_options = { :accept => :json, :user_agent => Config.settings[:user_agent] } - post_data = { - :file => File.new(filepath, 'rb'), - :params => callback_query_string, - :action => action_string, - :multipart => true - } + auth_token = nil + if !options[:qbox_signature_token].nil? && !options[:qbox_signature_token].empty? + auth_token = 'QBox ' + options[:qbox_signature_token] + #elsif !options[:upload_signature_token].nil? && !options[:upload_signature_token].empty? + #auth_token = 'UpToken ' + options[:upload_signature_token] + elsif options[:access_token] + auth_token = 'Bearer ' + options[:access_token] + end + header_options.merge!('Authorization' => auth_token) unless auth_token.nil? response = RestClient.post url, post_data, header_options body = response.respond_to?(:body) ? response.body : "" data = safe_json_parse(body) unless body.empty? @@ -123,6 +126,16 @@ module Qiniu [code, data] end + def upload_multipart_data(url, filepath, action_string, callback_query_string = '') + post_data = { + :file => File.new(filepath, 'rb'), + :params => callback_query_string, + :action => action_string, + :multipart => true + } + send_multipart_request(url, post_data) + end + def generate_query_string(params) return params if params.is_a?(String) total_param = params.map { |key, value| key.to_s+"="+value.to_s } diff --git a/lib/qiniu/rs/version.rb b/lib/qiniu/rs/version.rb index 2f080cf..43cf2ae 100755 --- a/lib/qiniu/rs/version.rb +++ b/lib/qiniu/rs/version.rb @@ -2,6 +2,6 @@ module Qiniu module RS - VERSION = "2.2.1" + VERSION = "2.3.0" end end diff --git a/spec/qiniu/rs/io_spec.rb b/spec/qiniu/rs/io_spec.rb index 0563ca8..fd88969 100755 --- a/spec/qiniu/rs/io_spec.rb +++ b/spec/qiniu/rs/io_spec.rb @@ -19,21 +19,26 @@ module Qiniu data["refresh_token"].should_not be_empty puts data.inspect =end - - code2, data2 = Qiniu::RS::IO.put_auth() - code2.should == 200 - data2["url"].should_not be_empty - data2["expiresIn"].should_not be_zero - puts data2.inspect - - @put_url = data2["url"] @bucket = "test" @key = Digest::SHA1.hexdigest (Time.now.to_i+rand(100)).to_s end + context ".upload_file" do + it "should works" do + code, data = Qiniu::RS::IO.put_auth() + code.should == 200 + data["url"].should_not be_empty + data["expiresIn"].should_not be_zero + puts data.inspect + code2, data2 = Qiniu::RS::IO.upload_file(data["url"], __FILE__, @bucket, @key) + code2.should == 200 + puts data2.inspect + end + end + context ".put_file" do it "should works" do - code, data = Qiniu::RS::IO.put_file(@put_url, __FILE__, @bucket, @key) + code, data = Qiniu::RS::IO.put_file(__FILE__, @bucket, @key, 'application/x-ruby', 'customMeta', true) code.should == 200 puts data.inspect end diff --git a/spec/qiniu/rs/rs_spec.rb b/spec/qiniu/rs/rs_spec.rb index 960ab85..6d2af22 100755 --- a/spec/qiniu/rs/rs_spec.rb +++ b/spec/qiniu/rs/rs_spec.rb @@ -33,9 +33,9 @@ module Qiniu @domain = 'cdn.example.com' end - context "IO.put_file" do + context "IO.upload_file" do it "should works" do - code, data = Qiniu::RS::IO.put_file(@put_url, __FILE__, @bucket, @key) + code, data = Qiniu::RS::IO.upload_file(@put_url, __FILE__, @bucket, @key) code.should == 200 puts data.inspect end diff --git a/spec/qiniu/rs_spec.rb b/spec/qiniu/rs_spec.rb index 732be23..d033d5e 100755 --- a/spec/qiniu/rs_spec.rb +++ b/spec/qiniu/rs_spec.rb @@ -43,11 +43,23 @@ module Qiniu :file => __FILE__, :bucket => @bucket, :key => @key, + :mime_type => 'application/x-ruby', :enable_crc32_check => true result.should be_true end end + context ".put_file" do + it "should works" do + result = Qiniu::RS.put_file :file => __FILE__, + :bucket => @bucket, + :key => @key, + :mime_type => 'application/x-ruby', + :enable_crc32_check => true + result.should be_true + end + end + context ".stat" do it "should works" do result = Qiniu::RS.stat(@bucket, @key)