Compare commits

...
243 Commits
Author SHA1 Message Date
Bai Long 5d07bc517c Merge pull request #90 from BluntBlade/master
去除已废弃的publish/unpublish接口。
2014-04-20 14:29:46 +08:00
Liang Tao ad579f402b 添加更新日志。 2014-04-20 09:29:01 +08:00
Liang Tao 34fc65bd99 去除已废弃的publish/unpublish接口。 2014-04-20 09:25:15 +08:00
Bai Long ecbeb51339 Merge pull request #86 from BluntBlade/master
添加pfop相关SDK内容。
2014-04-14 15:15:09 +08:00
Liang Tao ef0d79eb17 去掉不常见的Ruby版本,只测试必要的几个。 2014-04-08 14:05:00 +08:00
Liang Tao edc86e230e Merge branch 'http.renew' 2014-04-08 12:50:34 +08:00
Liang Tao 5718b720df 添加更多Debug日志以便追踪错误。 2014-04-08 12:50:28 +08:00
Liang Tao 5cc4db2231 添加pfop相关SDK内容。 2014-04-06 11:47:04 +08:00
longbai f823ff0659 add license, author 2014-04-05 23:35:21 +08:00
Bai Long 5a05fd9f8f Update README.md
add code climate
2014-04-04 23:35:02 +08:00
Bai Long aedb459b08 Merge pull request #85 from BluntBlade/master
将HTTP事务相关的代码提取到独立的类中,将逻辑理清并加上注释。
2014-04-04 18:45:47 +08:00
Liang Tao 1b464029f7 Merge branch 'http.renew' 2014-04-04 15:16:43 +08:00
Liang Tao 1d39aeec05 去掉对过时函数的调用。 2014-04-04 14:54:15 +08:00
Liang Tao efe6b50c50 将HTTP事务相关的代码提取到独立的类中。 2014-04-03 00:38:00 +08:00
LI Daobing 544d1069e2 Merge pull request #84 from BluntBlade/master
重写与授权相关的函数和类,并归入Qiniu::Auth空间。
2014-04-02 12:41:08 +08:00
Liang Tao 0ccda7cdb1 JRuby-Head环境有问题,已经向Travis-CI提交Issue,暂时停止在该环境的测试。 2014-04-02 11:44:32 +08:00
Liang Tao 3f4c79b78f 添加版本更新说明。 2014-04-01 10:10:30 +08:00
Liang Tao 26b46859a1 补充注释。 2014-03-31 15:02:34 +08:00
Liang Tao 1bc0476671 修正Auth中的Bug,并保证向下兼容性。 2014-03-28 16:57:35 +08:00
Liang Tao c91c5e4246 将测试用例中对Storage.upload_with_token_2的调用更换为Storage.upload_with_put_policy。 2014-03-28 16:05:48 +08:00
Liang Tao fe70576624 添加新的常量DEFAULT_AUTH_SECONDS。 2014-03-28 13:34:58 +08:00
Liang Tao 0c8a8e98e0 统一改为使用Auth.calculate_deadline()方法计算授权期。 2014-03-28 11:47:18 +08:00
Liang Tao e2b3162f1f 添加Auth.generate_uptoken()方法,厘清上传授权凭证签发算法。 2014-03-28 11:36:58 +08:00
Liang Tao b99ff384ef 将Utils.generate_qbox_signature()移至Auth.generate_acctoken()。 2014-03-27 13:34:58 +08:00
Liang Tao fc5afc332a 为authorize_download_url添加测试用例。 2014-03-27 11:34:36 +08:00
Liang Tao 5c9169b516 Merge branch 'master' into auth.renew 2014-03-27 11:08:13 +08:00
LI Daobing fee579a5ba Merge pull request #83 from BluntBlade/master
令http_request()输出HTTP Response Headers,并用于所有的上传测试用例。
2014-03-26 17:50:14 +08:00
Liang Tao bc1b8acea5 删除过时的Qiniu::Storage.put_file方法。 2014-03-26 17:07:34 +08:00
Liang Tao 11188ff682 Merge branch 'master' into auth.renew 2014-03-26 15:03:33 +08:00
Liang Tao 17c961da1c 改使用/mkbucket2接口来创建Bucket,避免手动绑定域名。 2014-03-26 15:03:28 +08:00
Liang Tao 476937bc60 去除对ak/sk的传参,仍然使用全局设定值。 2014-03-26 14:40:33 +08:00
Liang Tao cfad4ef53f 添加防御代码,对于不响应raw_headers方法的对象,直接返回空Hash,避免抛出意料之外的异常。 2014-03-26 14:23:27 +08:00
Liang Tao d9ea68efa8 更新Gemfile.lock,反映mime-types的downgrade变化。 2014-03-26 13:28:50 +08:00
Liang Tao 6aaacbe2e8 将mime-types的版本恢复到1.19,避免2.2版本在Ruby 1.8.7下无法安装导致测试不通过的问题。 2014-03-26 13:24:45 +08:00
Liang Tao 8ffee73524 调整版本依赖基线为6.1.0。 2014-03-25 16:14:56 +08:00
Liang Tao fa36f09765 修正合并冲突,并把版本改为6.1.0。 2014-03-25 15:58:20 +08:00
Liang Tao 8173b31b18 修正版本的Badge。 2014-03-25 15:43:47 +08:00
Liang Tao bcb8ad7392 添加更新说明。 2014-03-25 15:30:05 +08:00
Liang Tao 9539c665bb 合并最新代码。 2014-03-24 18:52:09 +08:00
Liang Tao 27eb25fc11 为send_request_with返回的HTTP Response Header添加对比数据。 2014-03-24 18:34:29 +08:00
Liang Tao 9374a451cd 去掉无意中添加的多余逗号,保证向下兼容性。 2014-03-24 18:26:56 +08:00
Liang Tao c49ea097b8 令http_request()输出HTTP Response Headers,并用于所有的上传测试用例。 2014-03-24 16:04:38 +08:00
Liang Tao 3753e759dd 添加authorize_download_url(),用于对私有资源下载URL进行授权。 2014-03-24 14:55:57 +08:00
LI Daobing 97d2c23b25 specify gem version 2014-03-24 11:16:01 +08:00
LI Daobing 4b7c5a2574 Merge pull request #81 from BluntBlade/master
修正测试用例,建立固定Bucket(用后不删除),避免631响应码问题。
2014-03-20 07:45:31 +08:00
Liang Tao 3cae9276b2 基于AK信息生成全网唯一的测试用Bucket,以及基于Ruby版本生成空间内唯一的测试用Key。 2014-03-19 17:17:32 +08:00
Liang Tao 8bd052e309 修正测试用例,建立固定Bucket(用后不删除),避免631响应码问题。 2014-03-19 14:35:20 +08:00
Liang Tao e97dc07ba3 修正测试用例,建立固定Bucket(用后不删除),避免631响应码问题。 2014-03-19 14:11:10 +08:00
Liang Tao 128e6f0fd7 修正资源管理测试用例,建立固定Bucket(用后不删除),避免631响应码问题。 2014-03-19 14:00:08 +08:00
Liang Tao 345fafd609 修正上传相关测试用例,建立固定Bucket(用后不删除),避免631响应码问题。 2014-03-19 11:16:21 +08:00
LI Daobing 5d4785c17d Update README.md 2014-03-17 11:28:40 +08:00
LI Daobing 2890272015 replace qiniu gem badge 2014-03-17 10:24:07 +08:00
LI Daobing eb3461a6bb Merge pull request #75 from BluntBlade/master
移除持久化代码,同时改为以4M为单位上传数据片(即直接上传整个数据块)。
2014-03-13 17:55:13 +08:00
Liang Tao 1a72e1055b 添加状态Badge。 2014-03-13 17:27:17 +08:00
Liang Tao cd66ce79ff 删除rbx测试项目,添加2.0.0和2.1.1测试项目。 2014-03-10 13:38:30 +08:00
Liang Tao ae0dbced1b 修正授权凭证算法和相关测试用例。 2014-03-10 12:21:15 +08:00
Liang Tao 8f5c6c0b0a 修正README与CHANGELOG。 2014-03-06 15:49:31 +08:00
Liang Tao d3ec623c3e 更新版本信息,修改GemSpec文件。 2014-03-06 15:40:29 +08:00
Liang Tao ab81095c0c 划分出Fop与Misc命名空间。 2014-03-06 15:33:53 +08:00
Liang Tao f1cb65d79a 调整所有的测试用例,使用新组织好的函数。 2014-03-03 17:17:42 +08:00
Liang Tao 36c492b9b9 去除io/up模块。 2014-03-03 16:40:04 +08:00
Liang Tao cd48eca8f6 以Storage为新的命名空间来组织与存储相关的SDK函数。 2014-03-03 16:13:43 +08:00
Liang Tao 51c719767f 修正gemspec文件。 2014-03-03 14:18:45 +08:00
Liang Tao de61097fde 去除统一的RS空间。 2014-02-25 16:58:18 +08:00
Liang Tao 3a3d026812 删除过期的preview接口。 2014-02-08 17:24:43 +08:00
Liang Tao 5e8e69a339 使用最新的imageInfo接口规格。 2014-02-08 17:11:25 +08:00
Liang Tao 1a1affc3d3 删除已经废弃的EU服务相关代码。 2014-02-08 15:14:47 +08:00
Liang Tao cb748d8857 添加最新的单文件直传实现:IO.upload_with_token_2。 2014-02-08 13:00:07 +08:00
Liang Tao 08f36221b7 删除过时的IO.put_auth与IO.upload_file组合。 2014-02-08 11:50:53 +08:00
Liang Tao ef9c90c7c5 删除废弃的登录方式实现代码。 2014-02-07 18:06:02 +08:00
Liang Tao 3302ecde6a 删除过时的测试案例,并为新改动逻辑添加新案例。 2014-01-28 16:04:55 +08:00
Liang Tao b0335577e5 以4M为单位上传数据片。 2014-01-28 12:20:31 +08:00
Liang Tao 0f3704c60f 删除非必需的初始化key参数。 2014-01-28 12:18:25 +08:00
Liang Tao 818c4bb5b3 移除持久化代码。 2014-01-27 18:39:50 +08:00
404 f1461c338e Merge pull request #66 from qiniu/v3
rm staus icon
2013-09-27 14:02:29 -07:00
why404 67280b714e rm status icons 2013-09-28 04:51:21 +08:00
404 8f86e6903a Merge pull request #65 from why404/hotfix/v3.4.6
Hotfix/v3.4.6
2013-09-27 12:46:15 -07:00
why404 47cf54069d Gemfile 2013-09-28 03:44:32 +08:00
why404 0bb4edcade fix :pattern requried 2013-09-28 03:42:00 +08:00
404 b1e6235b2e Merge pull request #57 from why404/hotfix/v3.4.5
Gemfile.lock ver fix
2013-07-24 18:24:51 -07:00
why404 e5d2367a27 ver fix 2013-07-25 09:22:56 +08:00
404 3186ba40df Merge pull request #56 from why404/release/v3.4.4
release v3.4.4
2013-07-23 11:12:30 -07:00
why404 c5708424b9 release v3.4.4 2013-07-24 02:09:19 +08:00
404 f85ee1159b Merge pull request #54 from qiniu/release/v3.4.3
callbackBody + returnUrl supported
2013-07-23 10:52:29 -07:00
why404 804c7609ef returnUrl 2013-07-24 01:47:50 +08:00
404 49ab640493 Merge pull request #53 from GaiaMagic/master
Support callback_body option
2013-07-23 10:37:48 -07:00
HungYuHei 85f8e68399 Support callback_body option 2013-07-22 23:18:54 +08:00
LI Daobing 7dbec75553 Merge pull request #50 from qiniu/develop
merge develop to master
2013-07-19 02:26:59 -07:00
LI Daobing 3299d2bd02 Merge remote-tracking branch 'origin/master' into develop
Conflicts:
	docs/README.md
2013-07-19 15:59:42 +08:00
LI Daobing ec8c50383b disable ruby-head on travis 2013-07-19 15:54:30 +08:00
LI Daobing 9cc157c21d Merge pull request #51 from longshanksmo/fix/test_account
use new account & correct test code
2013-07-18 22:33:25 -07:00
longshanksmo 3d40d4e967 use new account & correct test code 2013-07-19 13:23:45 +08:00
hantuo fa85e838fc Merge pull request #49 from longshanksmo/fix/lost_link
Fix/lost link
2013-07-17 22:02:33 -07:00
longshanksmo 38970b66e3 Update README.md 2013-07-17 20:33:46 +08:00
longshanksmo 63ccf3f2dc Update README.md
correct the title
2013-07-17 20:32:32 +08:00
longshanksmo 5c011aa816 fix links 2013-07-17 20:29:56 +08:00
xushiwei 7b507f15c6 Merge pull request #41 from why404/release/v3.4.2
Release/v3.4.2 - uploadToken[:return_body]
2013-02-05 03:45:03 -08:00
404 aab7277b50 update README.md 2013-02-05 14:59:26 +08:00
404 eaed72eeaa release v3.4.2 for uploadToken[:return_body] #40 . 2013-02-04 15:38:11 +08:00
404 60cc7274b1 Merge pull request #38 from why404/feature/fix_env_appkey
rm encrypted appkey
2013-01-24 18:38:37 -08:00
xushiwei 466a4ba07e Merge pull request #39 from why404/feature/return_body
add :return_body option for Qiniu::RS.generate_upload_token()
2013-01-23 06:23:49 -08:00
404 444956f161 add :return_body option for Qiniu::RS.generate_upload_token() 2013-01-23 21:30:15 +08:00
404 0e4b5d00bc used before_script instead of env vars in .travis.yml 2013-01-23 16:55:41 +08:00
404 5d2745d148 fix travis env syntax 2013-01-23 16:49:15 +08:00
404 3026a40d99 rm encrypted appkey 2013-01-23 16:38:42 +08:00
xushiwei 84f7cc6205 Merge pull request #36 from why404/feature/encrypt_appkey
mv appkey outside and encrypted
2013-01-22 23:58:50 -08:00
404 5562df4b28 mv appkey outside and encrypted 2013-01-23 15:37:13 +08:00
qiniu 70c4664e0d Merge pull request #35 from qiniu/master
v3.4.1 - add :async_options for uploadToken
2013-01-14 17:58:45 -08:00
404 2096bff7a7 Merge pull request #34 from why404/feature/upload_async_options
v3.4.1 - add :async_options for uploadToken
2013-01-14 09:20:44 -08:00
404 b0af3c1814 v3.4.1 - add :async_options for uploadToken 2013-01-15 00:10:19 +08:00
404 9bda6845a0 Merge pull request #33 from why404/feature/add_cp_and_mv_support
v3.4.0 - added copy and move methods
2013-01-08 09:59:13 -08:00
404 d9422cbcf4 added copy and move methods 2013-01-09 01:56:05 +08:00
404 38c054f24c Merge pull request #32 from why404/feature/uniq_bucket_for_multi_test_concurrency
using unique bucket for multi concurrency testing
2013-01-06 07:48:34 -08:00
404 f85c4fab41 updated CHANGELOG.md for v3.3.1 2013-01-06 23:39:28 +08:00
404 f9bbcfeec6 using unique bucket for multi concurrency testing 2013-01-06 23:29:23 +08:00
xushiwei e3d4a80b54 Merge pull request #31 from why404/feature/download_token
Feature/download token
2013-01-05 23:53:54 -08:00
404 61aae133cf updated Gemfile.lock 2013-01-06 15:20:50 +08:00
404 c3e2a3d44b rewrite docs 2013-01-06 14:48:35 +08:00
404 06b019008a fix encoding comments 2013-01-06 11:07:12 +08:00
404 957b5d1499 docs updated 2012-12-20 17:16:17 +08:00
404 5b0a561cc1 add Qiniu::RS.generate_download_token() 2012-12-19 17:53:26 +08:00
404 d1e60697c3 disabled wm 2012-12-18 10:57:16 +08:00
404 089b6bc7c6 Merge pull request #29 from zhouguangming/bug_fixed
refactor UP.upload_with_token
2012-12-09 17:53:22 -08:00
zhouguangming f1edde2e54 refactor UP.upload_with_token 2012-12-08 22:15:01 +08:00
404 8aca8bf33e Merge pull request #28 from zhouguangming/bug_fixed
MissingArgsError should get a key_list // very nice!
2012-12-07 00:07:43 -08:00
zhouguangming 69bb9cb913 MissingArgsError should get a key_list 2012-12-07 15:25:54 +08:00
404 cacdb4f926 Merge pull request #27 from zhouguangming/bug_fixed
感谢修正!不过由于历史遗留原因 lib/qiniu/tokens/qbox_token.rb 尚保留在sdk代码里边但并未实际使用。请求签名认证程序使用的是  
Qiniu::RS::Utils.generate_qbox_signature(url,params) , 所以不影响sdk正常使用。
2012-12-06 05:41:46 -08:00
zhouguangming 557b75f33f [bug_fixed] lib/qiniu/tokens/qbox_token.rb => method generate_token => undefined method signature 2012-12-06 17:19:14 +08:00
xushiwei 2cbb175423 Merge pull request #26 from why404/release/v3.2.2
fix up 701
2012-12-04 22:32:53 -08:00
404 a8a8f63887 fix up 701 2012-12-05 14:00:12 +08:00
404 64cf9b5d3f Merge pull request #24 from hayeah/master
Fixes Qiniu::RS::Exception Issue
2012-11-29 01:53:46 -08:00
Howard Yeh 1a73c39ffb Don't override to_s of Exception with inspect.
This causes infinite recursion.
2012-11-27 18:52:41 +08:00
xushiwei 864076b9ec Merge pull request #21 from why404/release/v3.2.0
Release/v3.2.0 - allow upload calback api
2012-11-08 03:01:27 -08:00
404 bb6efd4e0d upload auto-rorate supported 2012-11-07 17:28:48 +08:00
404 ef1739cb2a v3.2.0 2012-11-06 23:19:14 +08:00
404 611837b137 Merge branch 'feature/allow_upload_callback_api' into develop 2012-11-06 23:17:16 +08:00
404 000eef8870 update unit tests 2012-11-06 23:16:40 +08:00
404 bacf83dd3c allow upload callback api 2012-11-06 22:09:19 +08:00
xushiwei b8ad8bd7a1 Merge pull request #20 from why404/develop
v3.1.0 - resumable upload support
2012-10-15 08:51:52 -07:00
404 38a0877b66 Merge branch 'release/v3.1.0' 2012-10-08 13:04:30 +08:00
404 241a16c40b Merge branch 'feature/resumable_upload' into develop 2012-10-08 13:00:56 +08:00
404 9a5a27429d add resumable upload and all tests(63 examples) passed 2012-10-08 12:59:30 +08:00
404 8011d6d837 all resumable uploading tests are passed 2012-10-03 12:02:38 +08:00
404 2451b5b51f update qiniu/rs/exceptions 2012-10-02 23:40:26 +08:00
404 bd34ea428e add Qiniu::RS::UP module 2012-10-02 18:47:19 +08:00
404 fa40449b12 added UploadFailedError 2012-09-27 20:22:51 +08:00
404 cf8a38faa0 Merge branch 'master' into develop 2012-09-25 13:44:54 +08:00
xushiwei f3dd759a5b Merge pull request #18 from why404/master
避免gems依赖包冲突,调整为x.y(不加.z)更为宽松的依赖策略
2012-09-12 06:25:36 -07:00
404 1072b1ebab Merge branch 'hotfix/v3.0.6' into develop 2012-09-12 16:19:55 +08:00
404 df68455eed Merge branch 'hotfix/v3.0.6' 2012-09-12 16:19:44 +08:00
404 ec58895ca7 Gemfile update 2012-09-12 16:19:32 +08:00
xushiwei 4f9ca6e468 Merge pull request #16 from why404/master
publish v3.0.4 for hotfix
2012-09-03 03:58:02 -07:00
404 93a78047c1 v3.0.5 2012-09-03 18:18:21 +08:00
404 f304a2cb67 Merge branch 'master' into develop 2012-09-03 18:14:51 +08:00
404 c2a30d5a01 version 3.0.5 2012-09-03 18:14:42 +08:00
404 7432857c5a Merge branch 'hotfix/v3.0.5' into develop 2012-09-03 18:12:25 +08:00
404 1d50e3aa54 Merge branch 'hotfix/v3.0.5' 2012-09-03 18:12:00 +08:00
404 13e1051b87 fixed rescue for ruby 1.8 2012-09-03 18:10:58 +08:00
404 7c1fd4d207 Merge branch 'master' into develop 2012-08-19 02:03:33 +08:00
404 7b628281d3 changed test env 2012-08-19 02:03:23 +08:00
404 263ea64367 Merge remote-tracking branch 'qiniu/master' into develop 2012-08-19 01:59:21 +08:00
404 2dfd3da781 Merge remote-tracking branch 'qiniu/master' 2012-08-19 01:59:03 +08:00
xushiwei 1af173d04f Merge pull request #15 from huobazi/master
修正 Qiniu::RS.generate_upload_toke 对参数处理和文档描述不一致
2012-08-18 10:25:03 -07:00
huobazi b176c7a604 文档中描述 Qiniu::RS.generate_upload_toke的参数为可选,默认值为3600,但代码中实际的处理并没有考虑参数未传值时取默认值
ref: http://docs.qiniutek.com/v3/sdk/ruby/#upload
2012-08-18 23:22:37 +08:00
404 9c719cc8e5 Merge branch 'hotfix/v3.0.3' into develop 2012-08-18 00:00:18 +08:00
404 e8a1343328 Merge branch 'hotfix/v3.0.3' 2012-08-18 00:00:01 +08:00
xushiwei 88b56daabd Merge pull request #14 from why404/hotfix/v3.0.3
Hotfix/v3.0.3 - doc link version
2012-08-17 08:50:51 -07:00
404 bab0f4a602 verion updated 2012-08-17 23:22:03 +08:00
xushiwei 1b9077a7dd Merge pull request #13 from why404/hotfix/v3.0.2
update spec - removed pub2 test
2012-08-16 20:11:40 -07:00
404 64ddbbe831 Merge branch 'hotfix/v3.0.2' into develop 2012-08-17 09:44:12 +08:00
404 36d5fc0684 Merge branch 'hotfix/v3.0.2' 2012-08-17 09:43:43 +08:00
404 490f359a17 update spec 2012-08-17 09:40:15 +08:00
xushiwei 01bd208627 Merge pull request #12 from why404/develop
add up, pub, eu and rsplus supported
2012-08-15 06:26:44 -07:00
404 d0c69c2255 updated doc 2012-08-15 20:20:34 +08:00
404 7e4aff00a1 Merge branch 'develop' 2012-08-15 18:53:51 +08:00
404 93ca5c5009 changed unstyle to unset_style 2012-08-15 18:53:25 +08:00
404 46144f145c Merge branch 'release/v3.0.0' into develop 2012-08-14 18:02:59 +08:00
404 b2ddd186aa Merge branch 'release/v3.0.0' 2012-08-14 18:02:31 +08:00
404 9f07e8d236 added watermarking and all tests passed (staging env) 2012-08-14 18:02:05 +08:00
404 4f04812b5c Merge branch 'feature/doc' into develop 2012-08-14 17:58:50 +08:00
404 fcc6870f2e updated doc 2012-08-14 17:58:34 +08:00
404 2bd158c1fe doc 2012-08-14 11:16:48 +08:00
404 556210548b callback_body_type 2012-08-14 00:06:42 +08:00
404 3b58147e70 rs spec 2012-08-13 23:42:35 +08:00
404 6746a8eeeb updated gemlock 2012-08-13 23:25:39 +08:00
404 da0d8d5ef8 fixed digest post data 2012-08-13 16:50:40 +08:00
404 1a950e8f85 uptoken field rename 2012-08-03 17:08:19 +08:00
404 d1b3f3c080 fixed io spec 2012-08-03 09:49:07 +08:00
404 46eb6e3d58 upload_with_token spec 2012-08-02 14:23:43 +08:00
404 8e86a92080 updated watermark tests 2012-08-02 13:59:02 +08:00
404 33447b0d5f new uptoken 2012-08-02 13:36:46 +08:00
404 e7f64fabae fixed io_spec.rb 2012-08-01 18:04:10 +08:00
404 1d63970688 rsplus/pub/eu fix 2012-08-01 11:58:43 +08:00
404 83475e594a end user module 2012-07-31 16:26:43 +08:00
404 9210c7fc32 pub 2012-07-31 12:29:22 +08:00
404 1a9dfeb9a7 uptoken 2012-07-31 10:14:10 +08:00
404 da3bdd362e added buckets 2012-07-30 15:37:52 +08:00
xushiwei 2bbcfff82f Merge pull request #11 from why404/master
fixed Qiniu::RS.put_file() 401
2012-07-28 05:09:30 -07:00
404 b2b5b8b92d added Qiniu::RS::Image.exif() 2012-07-28 19:06:35 +08:00
404 e5c57d4d72 fixed put_file 401 2012-07-28 16:20:41 +08:00
404 9ff7228a8f fix put_file 401 2012-07-28 16:19:08 +08:00
xushiwei abadfe65ba Merge pull request #10 from why404/master
fixed Qiniu::RS.put_file()
2012-07-28 00:28:50 -07:00
404 006df109a7 fixed Qiniu::RS.put_file() 2012-07-28 15:16:26 +08:00
xushiwei c4b23a4d89 Merge pull request #9 from why404/master
新增服务端上传方法 Qiniu::RS.put_file(),并完美通过所有39个单元测试
2012-07-24 02:44:44 -07:00
404 02d25bf63b Merge branch 'release/v2.3.0' 2012-07-24 17:31:10 +08:00
404 28d4aa7743 Merge branch 'feature/rs_put' into develop 2012-07-24 17:30:36 +08:00
404 25fd1777f6 updated sdk doc 2012-07-24 17:29:57 +08:00
404 7df449cc73 added Qiniu::RS.put_file() 2012-07-24 16:54:09 +08:00
xushiwei 3765e72c8a Merge pull request #8 from why404/master
新增 高级图像处理方法(缩略、裁剪、旋转、转化),支持结果持久化存储
2012-07-23 05:44:13 -07:00
404 84157d35fa updated docs/README.md 2012-07-23 17:46:10 +08:00
404 4a83010548 Merge branch 'release/v2.2.0' 2012-07-23 17:11:51 +08:00
404 313e368602 added Qiniu::RS.image_mogrify_save_as() and Qiniu::RS::Image.mogrify_preview_url() 2012-07-23 17:10:17 +08:00
xushiwei dddbf793e5 Merge pull request #7 from why404/master
added source doc
2012-07-12 18:58:57 -07:00
404 28c71771e4 added docs 2012-07-13 00:46:02 +08:00
404 d92dc24416 update gem.homepage 2012-07-09 12:43:24 +08:00
xushiwei 1eb43cb00f Update master 2012-07-04 10:32:34 +08:00
qiniu b2f496551b Merge pull request #5 from why404/master
release/v2.1.2 - added generate upload token logic
2012-07-03 19:27:39 -07:00
404 07a01834d6 Merge branch 'release/v2.1.2' 2012-07-03 16:32:35 +08:00
404 c60cc6e4b8 updated Qiniu::RS.generate_upload_token() 2012-07-03 16:29:41 +08:00
404 0a4f8c1471 Merge branch 'hotfix/v2.1.1' into develop 2012-07-02 18:18:56 +08:00
404 9719aae614 Merge branch 'hotfix/v2.1.1' 2012-07-02 18:18:47 +08:00
404 f8c3f93d29 updated README 2012-07-02 18:18:34 +08:00
404 971fa3e92b Merge branch 'release/v2.1.0' 2012-07-02 18:08:08 +08:00
404 70d6d55e0c added Qiniu::RS.generate_upload_token() 2012-07-02 18:07:11 +08:00
404 8e90310b9c Update master 2012-06-29 16:45:17 +08:00
404 8cf75eecbd Merge branch 'hotfix/v2.0.5' 2012-06-29 11:46:12 +08:00
404 ccd46828c6 fixed raise NoSuchFileError bug. #3 2012-06-29 11:45:42 +08:00
404 2cc53fabdd Merge branch 'hotfix/v2.0.4' 2012-06-29 02:25:46 +08:00
404 7eb27d0deb fixed VERSION number 2012-06-29 02:25:31 +08:00
404 b0b568d0c7 Merge branch 'hotfix/v2.0.3' 2012-06-29 02:20:51 +08:00
404 cffec32884 updated gem source and Gemfile 2012-06-29 02:20:35 +08:00
404 0f59b15100 Merge branch 'hotfix/v2.0.2' 2012-06-29 02:12:28 +08:00
404 459c32c278 fixed sdk doc link 2012-06-29 02:12:12 +08:00
404 5dc205ab46 Merge branch 'release/v2.0.1' 2012-06-27 20:44:49 +08:00
404 87da8ed256 added access_key and secret_key auth supported 2012-06-27 20:43:35 +08:00
404 d9738e4190 Merge pull request #2 from lidaobing/master
add Gemfile.lock. thx lidaobing
2012-06-10 05:43:30 -07:00
LI Daobing 1e08f5bb17 add Gemfile.lock 2012-06-10 19:21:59 +08:00
404 38326cbb83 Merge branch 'release/v1.1.0' 2012-06-08 14:05:17 +08:00
404 853c3b537d added file uploading crc32 check 2012-06-08 14:03:04 +08:00
404 285b766819 Merge branch 'hotfix/v1.0.4' into develop 2012-05-28 21:34:24 +08:00
404 e54081dd88 Merge branch 'hotfix/v1.0.4' 2012-05-28 21:33:52 +08:00
404 a0657ee26f added jruby-openssl bundling if RUBY_PLATFORM is JAVA 2012-05-28 21:33:27 +08:00
404 0fa0839a70 Merge branch 'hotfix/v1.0.3' into develop 2012-05-28 21:22:32 +08:00
58 changed files with 3830 additions and 854 deletions
-1
View File
@@ -5,7 +5,6 @@
.bundle
.config
.yardoc
Gemfile.lock
InstalledFiles
_yardoc
coverage
+5 -8
View File
@@ -1,12 +1,9 @@
language: ruby
rvm:
- 1.8.7
- 1.9.2
- 1.9.3
- jruby-18mode
- 2.0.0
- 2.1.1
- jruby-19mode
- rbx-18mode
- rbx-19mode
- ruby-head
- jruby-head
- ree
before_script:
- export QINIU_ACCESS_KEY=LGRqJsjX7LAvtdtX6hhyxs861lS57HwnOQmJsOuX
- export QINIU_SECRET_KEY=g_OxhuN5iJ1O7BoA6YdXyF6zSszk34UZFKDfdIIW
+105
View File
@@ -0,0 +1,105 @@
## CHANGE LOG
### v6.2.1
- 去除已废弃的publish/unpublish接口。 [https://github.com/qiniu/ruby-sdk/pull/90](https://github.com/qiniu/ruby-sdk/pull/90) (#8143)
### v6.2.0
- 重写与授权相关的函数并归入Qiniu::Auth空间,原授权凭证生成类维持不变。
- 添加Qiniu::Storage::PutPolicy类和Qiniu::Storage#upload_with_put_policy方法,并推荐使用两者组合实现单文件上传。
### v6.1.0
- Qiniu::Storage所有上传接口返回第三个值raw_headers,类型为Hash,包含已解析的HTTP响应报文中的所有Header信息。
该返回值主要用于调试。当遇到难以理解或解释的错误时,请将其中的X-Log和X-Reqid两项信息[通过邮件反馈](mailto:support@qiniu.com?subject=Ruby-SDK-Bug-Report)给我们。
- 更新Qiniu::Storage所有上传接口的测试用例,打印HTTP响应Header信息。
- 删除过时的Qiniu::Storage#put_file方法和相关测试用例
该方法调用的API已过时并逐步废弃,建议用户尽快迁移到Qiniu::Storage#upload_with_token_2方法上
### v6.0.1
- 重新划分命名空间,存储相关归入Qiniu::Storage,数据处理相关归入Qiniu::Fop,杂项相关归入Qiniu::Misc。
### v3.4.6
- `Qiniu::RS.generate_download_token()` 方法的 `:pattern` 调整为必选项。
### v3.4.5
- `Qiniu::RS.generate_upload_token()` 方法新增 `:callback_body``return_url` 选项。
- 选项含义参考: <http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html#put-policy-callback-body>
### v3.4.2
- `Qiniu::RS.generate_upload_token()` 方法新增 `:return_body` 选项。
该选项(`:return_body`)可设置文件上传成功后,执行七牛云存储规定的回调API,并以 JSON 响应格式返回其执行结果。参考 [uploadToken 之 returnBody 说明](http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html#put-policy-return-body)。
### v3.4.1
增加为上传文件进行预转的选项,参见 [uploadToken 之 asyncOps 说明](http://docs.qiniu.com/api/v6/put.html#uploadToken-asyncOps)
- `Qiniu::RS.generate_upload_token()` 方法新增 `:async_options` 选项用于进行预转操作。
### v3.4.0
增加文件复制/移动方法,包括批量复制/移动文件
- `Qiniu::RS.copy(source_bucket, source_key, target_bucket, target_key)`
- `Qiniu::RS.move(source_bucket, source_key, target_bucket, target_key)`
- `Qiniu::RS.batch_copy [source_bucket, source_key, target_bucket, target_key], ...`
- `Qiniu::RS.batch_move [source_bucket, source_key, target_bucket, target_key], ...`
### v3.3.1
确保单元测试里边用到的测试 Bucket 全局唯一
使得10多种不通的 Ruby 宿主环境能隔离互不影响地执行单元/集成测试
Ruby 宿主环境如下
1.8.7, 1.9.2, 1.9.3, jruby-18mode, jruby-19mode, rbx-18mode, rbx-19mode, ruby-head, jruby-head, ree
详见 <https://travis-ci.org/qiniu/ruby-sdk>
### v3.3.0
私有资源下载新版实现,添加 Qiniu::RS.generate_download_token() 方法。参考 [downloadToken](http://developer.qiniu.com/docs/v6/api/reference/security/download-token.html)
详见 [Ruby SDK 使用文档之私有资源下载](http://developer.qiniu.com/docs/v6/api/overview/dn/security.html)
### v3.2.2
fixed E701 error
断点续上传根据 mkblk 返回的 host 字段进行 bput 和 mkfile ,规避由于DNS智能解析造成的分布式并行块上传会出现上下文不连贯导致的 E701 问题。
### v3.2.1
allow images uploaded auto-orient.
允许图片上传成功后自动旋转。
参考:
1. [[API] multipart/form-data 上传文件之 action 字段详解](http://docs.qiniutek.com/v3/api/io/#upload-action)
2. [[SDK] Qiniu::RS.upload_file() 方法中的参数增加了 :rotate 选项](http://docs.qiniutek.com/v3/sdk/ruby/#upload-server-side)
### v3.2.0
2012-11-06
allow files uploaded auto callback some APIs (like imageInfo, exif, etc…), and add those APIs callback results as part of the custom data for POST biz-server.
允许上传文件(图片)成功后执行回调指定的 API (比如 imageInfo, exif 接口等),并将指定API的回调结果一并 POST 发送给客户方的业务服务器。
参考:
1. [[API] 生成上传授权凭证 uploadToken 之 escape 参数详解](http://docs.qiniutek.com/v3/api/io/#escape-expression)
2. [[SDK] Qiniu::RS.generate_upload_token() 方法中的参数增加了 :escape 选项](http://docs.qiniutek.com/v3/sdk/ruby/#generate-upload-token)
+4
View File
@@ -2,3 +2,7 @@ source 'https://rubygems.org'
# Specify your gem's dependencies in qiniu-s3.gemspec
gemspec
# platforms :jruby do
# gem "jruby-openssl"
# end
+37
View File
@@ -0,0 +1,37 @@
PATH
remote: .
specs:
qiniu (6.2.1)
json (~> 1.7)
mime-types (~> 1.19)
rest-client (~> 1.6)
ruby-hmac (~> 0.4)
GEM
remote: https://rubygems.org/
specs:
diff-lcs (1.1.3)
fakeweb (1.3.0)
json (1.8.1)
mime-types (1.23)
rake (10.0.3)
rest-client (1.6.7)
mime-types (>= 1.16)
rspec (2.12.0)
rspec-core (~> 2.12.0)
rspec-expectations (~> 2.12.0)
rspec-mocks (~> 2.12.0)
rspec-core (2.12.2)
rspec-expectations (2.12.1)
diff-lcs (~> 1.1.3)
rspec-mocks (2.12.2)
ruby-hmac (0.4.0)
PLATFORMS
ruby
DEPENDENCIES
fakeweb (~> 1.3)
qiniu!
rake (>= 0.9)
rspec (>= 2.11)
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2012 why404
Copyright (c) 2012-2013 qiniutek.com
MIT License
+14 -7
View File
@@ -1,14 +1,21 @@
# Qiniu Resource (Cloud) Storage SDK for Ruby - [![Build Status](https://secure.travis-ci.org/why404/qiniu-rs.png?branch=master)](http://travis-ci.org/why404/qiniu-rs) [![Dependency Status](https://gemnasium.com/why404/qiniu-rs.png)](https://gemnasium.com/why404/qiniu-rs)
# Qiniu Resource (Cloud) Storage SDK for Ruby
# 关于
## 关于
此 Ruby SDK 适用于 Ruby 1.8.x, 1.9.x 版本,基于 [七牛云存储官方API](http://docs.qiniutek.com/v1/api/) 构建。使用此 SDK 构建您的网络应用程序,能让您以非常便捷地方式将数据安全地存储到七牛云存储上。无论您的网络应用是一个网站程序,还是包括从云端(服务端程序)到终端(手持设备应用)的架构的服务或应用,通过七牛云存储及其 SDK,都能让您应用程序的终端用户高速上传和下载,同时也让您的服务端更加轻盈。
此 Ruby SDK 适用于 Ruby 1.8.x, 1.9.x, 2.0.x, jruby, rbx, ree 版本,基于 [七牛云存储官方API](http://developer.qiniu.com/docs/v6/index.html) 构建。使用此 SDK 构建您的网络应用程序,能让您以非常便捷地方式将数据安全地存储到七牛云存储上。无论您的网络应用是一个网站程序,还是包括从云端(服务端程序)到终端(手持设备应用)的架构的服务或应用,通过七牛云存储及其 SDK,都能让您应用程序的终端用户高速上传和下载,同时也让您的服务端更加轻盈。
## 状态
[![Build Status](https://api.travis-ci.org/qiniu/ruby-sdk.png?branch=master)](https://travis-ci.org/qiniu/ruby-sdk)
[![Gem Version](https://badge.fury.io/rb/qiniu.png)](http://badge.fury.io/rb/qiniu)
[![Dependency Status](https://gemnasium.com/qiniu/ruby-sdk.png)](https://gemnasium.com/qiniu/ruby-sdk)
[![Code Climate](https://codeclimate.com/github/qiniu/ruby-sdk.png)](https://codeclimate.com/github/qiniu/ruby-sdk)
## 安装
在您 Ruby 应用程序的 `Gemfile` 文件中,添加如下一行代码:
gem 'qiniu-rs'
gem 'qiniu', '~> 6.1.0'
然后,在应用程序所在的目录下,可以运行 `bundle` 安装依赖包:
@@ -16,11 +23,11 @@
或者,可以使用 Ruby 的包管理器 `gem` 进行安装:
$ gem install qiniu-rs
$ gem install qiniu
## 使用
参考文档:[七牛云存储 Ruby SDK 使用指南](http://docs.qiniutek.com/v1/sdk/ruby/)
参考文档:[七牛云存储 Ruby SDK 使用指南](http://developer.qiniu.com/docs/v6/sdk/ruby-sdk.html)
## 贡献代码
@@ -32,7 +39,7 @@
## 许可证
Copyright (c) 2012 why404
Copyright (c) 2012-2014 qiniu.com
基于 MIT 协议发布:
+790
View File
@@ -0,0 +1,790 @@
---
title: Ruby SDK
---
此 Ruby SDK 适用于 Ruby 1.8.x, 1.9.x, jruby, rbx, ree 版本,基于 [七牛云存储官方API](http://docs.qiniu.com/) 构建。使用此 SDK 构建您的网络应用程序,能让您以非常便捷地方式将数据安全地存储到七牛云存储上。无论您的网络应用是一个网站程序,还是包括从云端(服务端程序)到终端(手持设备应用)的架构的服务或应用,通过七牛云存储及其 SDK,都能让您应用程序的终端用户高速上传和下载,同时也让您的服务端更加轻盈。
七牛云存储 Ruby SDK 源码地址:<https://github.com/qiniu/ruby-sdk>
**目录**
- [安装](#Installation)
- [接入](#turn-on)
- [配置密钥(AccessKey / SecretKey](#establish_connection!)
- [针对 Ruby On Rails 网站应用初始化设置](#ror-init)
- [使用](#Usage)
- [文件上传](#upload)
- [生成上传授权凭证(uploadToken](#generate-upload-token)
- [Ruby 服务端上传文件](#upload-server-side)
- [开启断点续上传](#resumable-upload)
- [iOS / Android / Web 端直传文件说明](#upload-client-side)
- [文件下载](#download)
- [公有资源下载](#download-public-files)
- [私有资源下载](#download-private-files)
- [生成下载授权凭证(downloadToken](#download-token)
- [高级特性](#other-download-features)
- [断点续下载](#resumable-download)
- [自定义 404 NotFound](#upload-file-for-not-found)
- [文件管理](#file-management)
- [查看单个文件属性信息](#stat)
- [复制单个文件](#copy)
- [移动单个文件](#move)
- [删除单个文件](#delete)
- [批量操作](#batch)
- [批量获取文件属性信息](#batch-get)
- [批量复制文件](#batch-copy)
- [批量移动文件](#batch-move)
- [批量删除文件](#batch-delete)
- [云处理](#cloud-processing)
- [图像](#image-processing)
- [查看图片属性信息](#image-info)
- [查看图片EXIF信息](#image-exif)
- [图像在线处理(缩略、裁剪、旋转、转化)](#image-mogrify-for-preview)
- [图像在线处理(缩略、裁剪、旋转、转化)后并持久化存储](#image-mogrify-for-save-as)
- 音频(TODO)
- 视频(TODO)
- [贡献代码](#Contributing)
- [许可证](#License)
<a name="Installation"></a>
## 安装
在您 Ruby 应用程序的 `Gemfile` 文件中,添加如下一行代码:
gem 'qiniu-rs'
然后,在应用程序所在的目录下,可以运行 `bundle` 安装依赖包:
$ bundle
或者,可以使用 Ruby 的包管理器 `gem` 进行安装:
$ gem install qiniu-rs
<a name="turn-on"></a>
## 接入
<a name="establish_connection!"></a>
### 配置密钥(AccessKey / SecretKey
要接入七牛云存储,您需要拥有一对有效的 Access Key 和 Secret Key 用来进行签名认证。可以通过如下步骤获得:
1. [开通七牛开发者帐号](https://portal.qiniu.com/signup)
2. [登录七牛开发者自助平台,查看 Access Key 和 Secret Key](https://portal.qiniu.com/setting/key)
在获取到 Access Key 和 Secret Key 之后,您可以在您的程序中调用如下两行代码进行初始化对接:
Qiniu::RS.establish_connection! :access_key => YOUR_APP_ACCESS_KEY,
:secret_key => YOUR_APP_SECRET_KEY
<a name="ror-init"></a>
### 针对 Ruby On Rails 网站应用初始化设置
如果您使用的是 [Ruby on Rails](http://rubyonrails.org/) 框架,我们建议您在应用初始化启动的过程中,依次调用上述两个函数即可,操作如下:
首先,在应用初始化脚本加载的目录中新建一个文件:`YOUR_RAILS_APP/config/initializers/qiniu-rs.rb`
然后,编辑 `YOUR_RAILS_APP/config/initializers/qiniu-rs.rb` 文件内容如下:
Qiniu::RS.establish_connection! :access_key => YOUR_APP_ACCESS_KEY,
:secret_key => YOUR_APP_SECRET_KEY
这样,您就可以在您的 `RAILS_APP` 中使用七牛云存储 Ruby SDK 提供的其他任意方法了。
接下来,我们会逐一介绍此 SDK 提供的其他方法。
<a name="Usage"></a>
## 使用
<a name="upload"></a>
### 文件上传
**注意**:如果您只是想要上传已存在您电脑本地或者是服务器上的文件到七牛云存储,可以直接使用七牛提供的 [qrsync](http://docs.qiniu.com/tools/qrsync.html) 上传工具。如果是需要通过您的网站或是移动应用(App)上传文件,则可以接入使用此 SDK,详情参考如下文档说明。
<a name="generate-upload-token"></a>
#### 生成上传授权凭证(uploadToken
要上传一个文件,首先需要调用 SDK 提供的 `Qiniu::RS.generate_upload_token` 函数来获取一个经过授权用于临时匿名上传的 `upload_token`——经过数字签名的一组数据信息,该 `upload_token` 作为文件上传流中 `multipart/form-data` 的一部分进行传输。
`Qiniu::RS.generate_upload_token` 函数原型如下:
Qiniu::RS.generate_upload_token :scope => target_bucket,
:expires_in => expires_in_seconds,
:callback_url => callback_url,
:callback_body => callback_body,
:callback_body_type => callback_body_type,
:customer => end_user_id,
:escape => allow_upload_callback_api,
:async_options => async_callback_api_commands,
:return_body => custom_response_body
**参数**
:scope
: 必须,字符串类型(String),设定文件要上传到的目标 `bucket`
:expires_in
: 可选,数字类型,用于设置上传 URL 的有效期,单位:秒,缺省为 3600 秒,即 1 小时后该上传链接不再有效(但该上传URL在其生成之后的59分59秒都是可用的)。
:callback_url
: 可选,字符串类型(String),用于设置文件上传成功后,七牛云存储服务端要回调客户方的业务服务器地址。
:callback_body
: 可选,字符串类型(String),用于设置文件上传成功后,七牛云存储服务端向客户方的业务服务器发送回调请求的值。
:callback_body_type
: 可选,字符串类型(String),用于设置文件上传成功后,七牛云存储服务端向客户方的业务服务器发送回调请求的 `Content-Type`
:customer
: 可选,字符串类型(String),客户方终端用户(End User)的ID,该字段可以用来标示一个文件的属主,这在一些特殊场景下(比如给终端用户上传的图片打上名字水印)非常有用。
:escape
: 可选,数字类型,可选值 0 或者 1,缺省为 0 。值为 1 表示 callback 传递的自定义数据中允许存在转义符号 `$(VarExpression)`,参考 [VarExpression](http://docs.qiniutek.com/v3/api/words/#VarExpression)。
`escape` 的值为 `1` 时,常见的转义语法如下:
-`callbackBodyType``application/json` 时,一个典型的自定义回调数据([CallbackParams](http://docs.qiniutek.com/v3/api/io/#CallbackParams))为:
`{foo: "bar", w: $(imageInfo.width), h: $(imageInfo.height), exif: $(exif)}`
-`callbackBodyType``application/x-www-form-urlencoded` 时,一个典型的自定义回调数据([CallbackParams](http://docs.qiniutek.com/v3/api/io/#CallbackParams))为:
`foo=bar&w=$(imageInfo.width)&h=$(imageInfo.height)&exif=$(exif)`
:async_options
: 可选,字符串类型(String),用于设置文件上传成功后,执行指定的预转指令。参考 [uploadToken 之 asyncOps 说明](http://docs.qiniutek.com/v3/api/io/#uploadToken-asyncOps)
:return_body
: 可选,字符串类型(String),用于设置文件上传成功后,执行七牛云存储规定的回调API,并以 JSON 响应格式返回其执行结果。参考 [uploadToken 之 returnBody 说明](http://docs.qiniutek.com/v3/api/io/#uploadToken-returnBody)。
**返回值**
返回一个字符串类型(String)的用于上传文件用的临时授权 `upload_token`
<a name="upload-server-side"></a>
#### Ruby 服务端上传文件
通过 `Qiniu::RS.upload_file()` 方法可在客户方的业务服务器上直接往七牛云存储上传文件。该函数规格如下:
Qiniu::RS.upload_file :uptoken => upload_token,
:file => file_path,
:bucket => bucket_name,
:key => record_id,
:mime_type => file_mime_type,
:note => some_notes,
:callback_params => callback_params,
:enable_crc32_check => false,
:rotate => auto_rotate
**参数**
:uptoken
: 必须,字符串类型(String),调用 `Qiniu::RS.generate_upload_token` 生成的 [用于上传文件的临时授权凭证](#generate-upload-token)
:file
: 必须,字符串类型(String),本地文件可被读取的有效路径
:bucket
: 必须,字符串类型(String),空间名称。
:key
: 必须,字符串类型(String),若把 Bucket 理解为关系性数据库的某个表,那么 key 类似数据库里边某个表的主键ID,需给每一个文件一个UUID用于进行标示。
:mime_type
: 可选,字符串类型(String),文件的 mime-type 值。如若不传入,SDK 会自行计算得出,若计算失败缺省使用 application/octet-stream 代替之。
:note
: 可选,字符串类型(String),为文件添加备注信息。
:callback_params
: 可选,String 或者 Hash 类型,文件上传成功后,七牛云存储向客户方业务服务器发送的回调参数。
:enable_crc32_check
: 可选,Boolean 类型,是否启用文件上传 crc32 校验,缺省为 false 。
:rotate
: 可选,数字类型,上传图片时专用,可针对图片上传后进行旋转。该参数值为 0 :表示根据图像EXIF信息自动旋转;值为 1 : 右转90度;值为 2 :右转180度;值为 3 : 右转270度。
**返回值**
上传成功,返回如下一个 Hash
{"hash"=>"FgHk-_iqpnZji6PsNr4ghsK5qEwR"}
上传失败,会抛出 `UploadFailedError` 异常。
<a name="resumable-upload"></a>
##### 开启断点续上传
无需任何额外改动,SDK 提供的 `Qiniu::RS.upload_file()` 方法缺省支持断点续上传。默认情况下,SDK 会自动启用断点续上传的方式来上传超过 4MB 大小的文件。您也可以在 [应用接入](http://docs.qiniutek.com/v3/sdk/ruby/#establish_connection!) 时通过修改缺省配置来设置该阀值:
Qiniu::RS.establish_connection! :access_key => YOUR_APP_ACCESS_KEY,
:secret_key => YOUR_APP_SECRET_KEY,
:block_size => 1024*1024*4,
:chunk_size => 1024*256,
:tmpdir => Dir.tmpdir + File::SEPARATOR + 'Qiniu-RS-Ruby-SDK',
:enable_debug => true,
:auto_reconnect => true,
:max_retry_times => 3
**参数详解**
应用接入初始化时,以下配置参数均为可选:
:block_size
: 整型,指定断点续上传针对大文件所使用的分块大小,缺省为 4MB ,小于该阀值的文件不启用断点续上传。
:chunk_size
: 整型,指定断点续上传每次http请求上传的数据块大小,缺省为 256KB。该设置尽量不要超过实际使用的上行带宽,且不能超过 `:block_size` 定义的值。
:tmpdir
: 字符串类型,指定持久化保存断点续上传进度状态临时文件的目录,缺省放置于操作系统的临时目录中。
:enable_debug
: 布尔值,是否启用调试模式,缺省启用(true),启用后会打印相关日志。该参数 SDK 全局有效。
:auto_reconnect
: 布尔值,指定每次 http 若请求失败是否启用重试,缺省启用(true)。该参数 SDK 全局有效。
:max_retry_times
: 整型,指定每次 http 若请求失败最多可以重试的次数,缺省为3次。该参数 SDK 全局有效。
<a name="upload-client-side"></a>
#### iOS / Android / Web 端直传文件说明
客户端 iOS / Android / Web 上传流程和服务端上传类似,差别在于:客户端直传文件所需的 `uploadToken` 选择在客户方的业务服务器端生成,然后将其生成的 `uploadToken` 颁发给客户端。
简单来讲,客户端上传流程分为两步:
1. [服务端生成上传授权凭证(uploadToken](#generate-upload-token)
2. 客户端程序调用 [iOS](http://docs.qiniu.com/ios-sdk/index.html) / [Android](http://docs.qiniu.com/android-sdk/index.html) SDK 的文件上传方法进行上传
如果是网页直传文件到七牛云存储,网页可以使用 JavaScript 动态实现 [七牛云存储上传API](http://docs.qiniutek.com/v3/api/io/#upload-file-by-html-form)。
通过客户端直传文件,您的终端用户即可把数据(比如图片或视频)直接上传到七牛云存储服务器上,而无须经由您的服务端中转,终端用户上传数据始终是离他物理距离最近的七牛存储节点。当终端用户上传成功后,七牛云存储服务端会向您指定的 `callback_url` (一般在 [uploadToken](#generate-upload-token) 里边指定)发送回调数据(回调数据在客户端程序里边指定)。如果 `callback_url` 所指向的服务端处理完毕后输出 `JSON` 格式的数据,七牛云存储服务端会将该回调请求所得的 JSON 响应信息原封不动地返回给客户端应用程序。
<a name="download"></a>
### 文件下载
七牛云存储上的资源下载分为 [公有资源下载](#download-public-files) 和 [私有资源下载](#download-private-files) 。
私有(private)是 Bucket(空间)的一个属性,一个私有 Bucket 中的资源为私有资源,私有资源不可匿名下载。
新创建的空间(Bucket)缺省为私有,也可以将某个 Bucket 设为公有,公有 Bucket 中的资源为公有资源,公有资源可以匿名下载。
<a name="download-public-files"></a>
#### 公有资源下载
[GET] http://<bucket>.qiniudn.com/<key>
或者,
[GET] http://<绑定域名>/<key>
绑定域名可以是自定义域名,可以在 [七牛云存储开发者自助网站](https://portal.qiniu.com/) 进行域名绑定操作。
**注意**,尖括号不是必需,代表替换项。
<a name="download-private-files"></a>
#### 私有资源下载
私有资源只能通过临时下载授权凭证(downloadToken)下载,下载链接格式如下:
[GET] http://<bucket>.qiniudn.com/<key>?token=<downloadToken>
或者,
[GET] http://<绑定域名>/<key>?token=<downloadToken>
<a name="download-token"></a>
##### 生成下载授权凭证(downloadToken
`<downloadToken>` 可以使用 SDK 提供的如下方法生成:
Qiniu::RS.generate_download_token :expires_in => expires_in_seconds,
:pattern => download_url_patterns
**参数**
expires_in
: 可选,数字类型,用于设置上传 URL 的有效期,单位:秒,缺省为 3600 秒,即 1 小时后该下载授权凭证(downloadToken)不再有效。
pattern
: 必选,字符串类型,用于设置可匹配的下载链接。参考:[downloadToken pattern 详解](http://docs.qiniutek.com/v3/api/io/#download-token-pattern)
<a name="other-download-features"></a>
#### 高级特性
<a name="resumable-download"></a>
##### 断点续下载
七牛云存储支持标准的断点续下载,参考:[云存储API之断点续下载](http://docs.qiniu.com/api/get.html#download-by-range-bytes)
<a name="upload-file-for-not-found"></a>
##### 自定义 404 NotFound
您可以上传一个应对 HTTP 404 出错处理的文件,当用户访问一个不存在的文件时,即可使用您上传的“自定义404文件”代替之。要这么做,您只须使用 `Qiniu::RS.upload_file` 函数上传一个 `key` 为固定字符串类型的值 `errno-404` 即可。
除了使用 SDK 提供的方法,同样也可以借助七牛云存储提供的命令行辅助工具 [qboxrsctl](http://docs.qiniu.com/tools/qboxrsctl.html) 达到同样的目的:
qboxrsctl put <Bucket> <Key> <LocalFile>
将其中的 `<Key>` 换作 `errno-404` 即可。
注意,每个 `<Bucket>` 里边有且只有一个 `errno-404` 文件,上传多个,最后的那一个会覆盖前面所有的。
<a name="file-management"></a>
### 文件管理
文件管理包括对存储在七牛云存储上的文件进行查看、复制、移动和删除处理。
<a name="stat"></a>
#### 查看单个文件属性信息
Qiniu::RS.stat(bucket, key)
可以通过 SDK 提供的 `Qiniu::RS.stat` 函数,来查看某个已上传文件的属性信息。
**参数**
bucket
: 必须,字符串类型(String),空间名称。
key
: 必须,字符串类型(String),若把 Bucket 理解为关系性数据库的某个表,那么 key 类似数据库里边某个表的主键ID,需给每一个文件一个UUID用于进行标示。
**返回值**
如果请求失败,返回 `false`;否则返回如下一个 `Hash` 类型的结构:
{
"fsize" => 3053,
"hash" => "Fu9lBSwQKbWNlBLActdx8-toAajv",
"mimeType" => "application/x-ruby",
"putTime" => 13372775859344500
}
fsize
: 表示文件总大小,单位是 Byte
hash
: 文件的特征值,可以看做是基版本号
mimeType
: 文件的 mime-type
putTime
: 上传时间,单位是 百纳秒
<a name="copy"></a>
#### 复制单个文件
Qiniu::RS.copy(source_bucket, source_key, target_bucket, target_key)
可以通过 SDK 提供的 `Qiniu::RS.copy` 函数进行文件复制操作。
**参数**
source_bucket
: 必须,字符串类型(String),指定源空间。
source_key
: 必须,字符串类型(String),指定源文件。
target_bucket
: 必须,字符串类型(String),指定要复制到的目标空间。
target_key
: 必须,字符串类型(String),指定要复制到目标空间的目标文件名。
**返回值**
如果请求失败,返回 `false`;否则返回 `true`
<a name="move"></a>
#### 移动单个文件
Qiniu::RS.move(source_bucket, source_key, target_bucket, target_key)
可以通过 SDK 提供的 `Qiniu::RS.move` 函数进行文件移动操作。
**参数**
source_bucket
: 必须,字符串类型(String),指定源空间。
source_key
: 必须,字符串类型(String),指定源文件。
target_bucket
: 必须,字符串类型(String),指定要移动到的目标空间。
target_key
: 必须,字符串类型(String),指定要移动到目标空间的目标文件名。
**返回值**
如果请求失败,返回 `false`;否则返回 `true`
<a name="delete"></a>
### 删除单个文件
Qiniu::RS.delete(bucket, key)
`Qiniu::RS.delete` 函数提供了从指定的 `bucket` 中删除指定的 `key`,即删除 `key` 索引关联的具体文件。
**参数**
bucket
: 必须,字符串类型(String),空间名称。
key
: 必须,字符串类型(String),若把 Bucket 理解为关系性数据库的某个表,那么 key 类似数据库里边某个表的主键ID,需给每一个文件一个UUID用于进行标示。
**返回值**
如果删除成功,返回 `true`,否则返回 `false`
<a name="batch"></a>
### 批量操作
Qiniu::RS.batch(command, bucket, keys)
SDK 还提供了 `Qiniu::RS.batch` 函数来提供批量处理 `Qiniu::RS.stat` 或是 `Qiniu::RS.get``Qiniu::RS.delete` 的相应功能。
**参数**
command
: 必须,字符串类型(String),其值可以是 `stat`, `get`, `delete` 中的一种
bucket
: 必须,字符串类型(String),空间名称。
keys
: 必须,数组类型(Array),所要操作 `key` 的集合。
**返回值**
如果请求失败,返回 `false`,否则返回一个 `Array` 类型的结构,其中每个元素是一个 `Hash` 类型的结构。例如批量`get`
[
{
"code" => 200,
"data" => {
"expires" => 3600,
"fsize" => 3053,
"hash" => "Fu9lBSwQKbWNlBLActdx8-toAajv",
"mimeType" => "application/x-ruby",
"url" => "http://iovip.qbox.me/file/<an-authorized-token>"
}
},
...
]
<a name="batch-get"></a>
#### 批量获取文件属性信息
Qiniu::RS.batch_get(bucket, keys)
`Qiniu::RS.batch_get` 函数是在 `Qiniu::RS.batch` 之上的封装,提供批量获取文件属性信息(含下载链接)的功能。
**参数**
bucket
: 必须,字符串类型(String),空间名称。
keys
: 必须,数组类型(Array),所要操作 `key` 的集合。
**返回值**
如果请求失败,返回 `false`,否则返回一个 `Array` 类型的结构,其中每个元素是一个 `Hash` 类型的结构。`Hash` 类型的值同 `Qiniu::RS.get` 函数的返回值类似,只多出一个 `code` 字段,`code` 为 200 表示所有 keys 全部获取成功,`code` 若为 298 表示部分获取成功。
[
{
"code" => 200,
"data" => {
"expires" => 3600,
"fsize" => 3053,
"hash" => "Fu9lBSwQKbWNlBLActdx8-toAajv",
"mimeType" => "application/x-ruby",
"url" => "http://iovip.qbox.me/file/<an-authorized-token>"
}
},
...
]
<a name="batch-copy"></a>
#### 批量复制文件
Qiniu::RS.batch_copy(*args)
等价于,
Qiniu::RS.batch_copy [source_bucket, source_key, target_bucket, target_key], ...
`Qiniu::RS.batch_copy()` 的参数是一个可变长数组,其中每个数组其元素含义同 `Qiniu::RS.copy` 的参数一样。
**返回值**
如果批量删除成功,返回 `true` ,否则为 `false`
<a name="batch-move"></a>
#### 批量移动文件
Qiniu::RS.batch_move(*args)
等价于,
Qiniu::RS.batch_move [source_bucket, source_key, target_bucket, target_key], ...
`Qiniu::RS.batch_move()` 的参数是一个可变长数组,其中每个数组其元素含义同 `Qiniu::RS.move` 的参数一样。
**返回值**
如果批量删除成功,返回 `true` ,否则为 `false`
<a name="batch-delete"></a>
#### 批量删除文件
Qiniu::RS.batch_delete(bucket, keys)
`Qiniu::RS.batch_download` 函数也是在 `Qiniu::RS.batch` 之上的封装,提供批量删除文件的功能。
参数同 `Qiniu::RS.batch_get` 的参数一样。
**返回值**
如果批量删除成功,返回 `true` ,否则为 `false`
<a name="cloud-processing"></a>
### 云处理
<a name="image-processing"></a>
#### 图像
<a name="image-info"></a>
##### 查看图片属性信息
Qiniu::RS.image_info(url)
使用 SDK 提供的 `Qiniu::RS.image_info` 方法,可以基于一张存储于七牛云存储服务器上的图片,针对其下载链接来获取该张图片的属性信息。
**参数**
url
: 必须,字符串类型(String),图片的下载链接,需是 `Qiniu::RS.get`(或`Qiniu::RS.batch_get`)函数返回值中 `url` 字段的值,或者是 `Qiniu::RS.download`(或`Qiniu::RS.batch_download`)函数返回的下载链接。且文件本身必须是图片。
**返回值**
如果请求失败,返回 `false`;否则,返回如下一个 `Hash` 类型的结构:
{
"format" => "jpeg",
"width" => 640,
"height" => 425,
"colorModel" => "ycbcr"
}
format
: 原始图片类型
width
: 原始图片宽度,单位像素
height
: 原始图片高度,单位像素
colorModel
: 原始图片着色模式
<a name="image-exif"></a>
##### 查看图片EXIF信息
Qiniu::RS.image_exif(url)
使用 SDK 提供的 `Qiniu::RS.image_exif` 方法,可以基于一张存储于七牛云存储服务器上的原始图片图片,取到该图片的 EXIF 信息。
**参数**
url
: 必须,字符串类型(String),原图的下载链接,需是 `Qiniu::RS.get`(或`Qiniu::RS.batch_get`)函数返回值中 `url` 字段的值,或者是 `Qiniu::RS.download`(或`Qiniu::RS.batch_download`)函数返回的下载链接。且文件本身必须是图片。
**返回值**
如果参数 `url` 所代表的图片没有 EXIF 信息,返回 `false`。否则,返回一个包含 EXIF 信息的 Hash 结构。
<a name="image-mogrify-for-preview"></a>
##### 图像在线处理(缩略、裁剪、旋转、转化)
`Qiniu::RS.image_mogrify_preview_url()` 方法支持将一个存储在七牛云存储的图片进行缩略、裁剪、旋转和格式转化处理,该方法返回一个可以直接预览缩略图的URL。
image_mogrify_preview_url = Qiniu::RS.image_mogrify_preview_url(source_image_url, mogrify_options)
**参数**
source_image_url
: 必须,字符串类型(string),指定原始图片的下载链接,可以根据 rs.get() 获取到。
mogrify_options
: 必须,Hash Map 格式的图像处理参数。
`mogrify_options` 对象具体的规格如下:
mogrify_options = {
:thumbnail => <ImageSizeGeometry>,
:gravity => <GravityType>, =NorthWest, North, NorthEast, West, Center, East, SouthWest, South, SouthEast
:crop => <ImageSizeAndOffsetGeometry>,
:quality => <ImageQuality>,
:rotate => <RotateDegree>,
:format => <DestinationImageFormat>, =jpg, gif, png, tif, etc.
:auto_orient => <TrueOrFalse>
}
`Qiniu::RS.image_mogrify_preview_url()` 方法是对七牛云存储图像处理高级接口的完整包装,关于 `mogrify_options` 参数里边的具体含义和使用方式,可以参考文档:[图像处理高级接口](http://docs.qiniu.com/api/image-process.html#imageMogr)。
**返回值**
返回一个可以预览最终缩略图的URL,String 类型。
<a name="image-mogrify-for-save-as"></a>
#### 图像在线处理(缩略、裁剪、旋转、转化)后并持久化存储
`Qiniu::RS.image_mogrify_save_as()` 方法支持将一个存储在七牛云存储的图片进行缩略、裁剪、旋转和格式转化处理,并且将处理后的缩略图作为一个新文件持久化存储到七牛云存储服务器上,这样就可以供后续直接使用而不用每次都传入参数进行图像处理。
result = Qiniu::RS.image_mogrify_save_as(target_bucket, target_key, src_img_url, mogrify_options)
**参数**
target_bucket
: 必须,字符串类型(string),指定最终缩略图要存放的 bucket 。
target_key
: 必须,字符串类型(string),指定最终缩略图存放在云存储服务端的唯一文件ID。
src_img_url
: 必须,字符串类型(string),指定原始图片的下载链接,可以根据 rs.get() 获取到。
mogrify_options
: 必须,Hash Map 格式的图像处理参数。
`mogrify_options` 对象具体的规格如下:
mogrify_options = {
:thumbnail => <ImageSizeGeometry>,
:gravity => <GravityType>, =NorthWest, North, NorthEast, West, Center, East, SouthWest, South, SouthEast
:crop => <ImageSizeAndOffsetGeometry>,
:quality => <ImageQuality>,
:rotate => <RotateDegree>,
:format => <DestinationImageFormat>, =jpg, gif, png, tif, etc.
:auto_orient => <TrueOrFalse>
}
`Qiniu::RS::Image.mogrify_preview_url()` 方法是对七牛云存储图像处理高级接口的完整包装,关于 `mogrify_options` 参数里边的具体含义和使用方式,可以参考文档:[图像处理高级接口](http://docs.qiniu.com/api/image-process.html#imageMogr)
**返回值**
如果请求失败,返回 `false`;否则,返回如下一个 `Hash` 类型的结构:
{"hash" => "FrOXNat8VhBVmcMF3uGrILpTu8Cs"}
示例代码:
data = Qiniu::RS.get("<test_image_bucket>", "<test_image_key>")
src_img_url = data["url"]
target_bucket = "test_thumbnails_bucket"
target_key = "cropped-" + @test_image_key
mogrify_options = {
:thumbnail => "!120x120r",
:gravity => "center",
:crop => "!120x120a0a0",
:quality => 85,
:rotate => 45,
:format => "jpg",
:auto_orient => true
}
result = Qiniu::RS.image_mogrify_save_as(target_bucket, target_key, src_img_url, mogrify_options)
if result
thumbnail = Qiniu::RS.get(target_bucket, target_key)
puts thumbnail["url"]
# 您可以选择将存放缩略图的 bucket 公开,这样就可以直接以外链的形式访问到缩略图,而不用走API获取下载URL,就可以直接以如下方式访问缩略图:
# [GET] http://<bucket>.qiniudn.com/<target_key>
end
<a name="Contributing"></a>
## 贡献代码
七牛云存储 Ruby SDK 源码地址:<https://github.com/qiniu/ruby-sdk>
1. 登录 <https://github.com>
2. Fork <https://github.com/qiniu/ruby-sdk>
3. 创建您的特性分支 (`git checkout -b my-new-feature`)
4. 提交您的改动 (`git commit -am 'Added some feature'`)
5. 将您的改动记录提交到远程 `git` 仓库 (`git push origin my-new-feature`)
6. 然后到 github 网站的该 `git` 远程仓库的 `my-new-feature` 分支下发起 Pull Request
<a name="License"></a>
## 许可证
Copyright (c) 2012 qiniutek.com
基于 MIT 协议发布:
* [www.opensource.org/licenses/MIT](http://www.opensource.org/licenses/MIT)
+1 -1
View File
@@ -1,2 +1,2 @@
# More logical way to require 'qiniu-rs'
require File.join(File.dirname(__FILE__), 'qiniu', 'rs')
require File.join(File.dirname(__FILE__), 'qiniu', 'qiniu')
Executable
+209
View File
@@ -0,0 +1,209 @@
# -*- encoding: utf-8 -*-
module Qiniu
autoload :Version, 'qiniu/version'
autoload :Utils, 'qiniu/utils'
autoload :Auth, 'qiniu/auth'
autoload :Config, 'qiniu/config'
autoload :Log, 'qiniu/log'
autoload :Exception, 'qiniu/exceptions'
autoload :AccessToken, 'qiniu/tokens/access_token'
autoload :QboxToken, 'qiniu/tokens/qbox_token'
autoload :UploadToken, 'qiniu/tokens/upload_token'
autoload :DownloadToken, 'qiniu/tokens/download_token'
autoload :Abstract, 'qiniu/abstract'
autoload :Storage, 'qiniu/storage'
autoload :Fop, 'qiniu/fop'
autoload :Misc, 'qiniu/misc'
class << self
StatusOK = 200
def establish_connection!(opts = {})
Config.initialize_connect opts
end
def mkbucket(bucket_name)
code, data = Storage.mkbucket(bucket_name)
code == StatusOK
end
def buckets
code, data = Storage.buckets
code == StatusOK ? data : false
end
def set_protected(bucket, protected_mode)
code, data = Misc.set_protected(bucket, protected_mode)
code == StatusOK
end
def set_separator(bucket, separator)
code, data = Misc.set_separator(bucket, separator)
code == StatusOK
end
def set_style(bucket, name, style)
code, data = Misc.set_style(bucket, name, style)
code == StatusOK
end
def unset_style(bucket, name)
code, data = Misc.unset_style(bucket, name)
code == StatusOK
end
def upload_file opts = {}
uncontained_opts = [:uptoken, :file, :bucket, :key] - opts.keys
raise MissingArgsError, uncontained_opts unless uncontained_opts.empty?
source_file = opts[:file]
raise NoSuchFileError, source_file unless File.exist?(source_file)
opts[:enable_resumable_upload] = true unless opts.has_key?(:enable_resumable_upload)
if opts[:enable_resumable_upload] && File::size(source_file) > Config.settings[:block_size]
code, data, raw_headers = Storage.upload_with_token(opts[:uptoken],
opts[:file],
opts[:bucket],
opts[:key],
opts[:mime_type],
opts[:note],
opts[:customer],
opts[:callback_params],
opts[:rotate])
else
code, data, raw_headers = Storage.upload_with_token(opts[:uptoken],
opts[:file],
opts[:bucket],
opts[:key],
opts[:mime_type],
opts[:note],
opts[:callback_params],
opts[:enable_crc32_check],
opts[:rotate])
end
raise UploadFailedError.new(code, data) if code != StatusOK
return data
end
def stat(bucket, key)
code, data = Storage.stat(bucket, key)
code == StatusOK ? data : false
end
def get(bucket, key, save_as = nil, expires_in = nil, version = nil)
code, data = Storage.get(bucket, key, save_as, expires_in, version)
code == StatusOK ? data : false
end
def download(bucket, key, save_as = nil, expires_in = nil, version = nil)
code, data = Storage.get(bucket, key, save_as, expires_in, version)
code == StatusOK ? data["url"] : false
end
def copy(source_bucket, source_key, target_bucket, target_key)
code, data = Storage.copy(source_bucket, source_key, target_bucket, target_key)
code == StatusOK
end
def move(source_bucket, source_key, target_bucket, target_key)
code, data = Storage.move(source_bucket, source_key, target_bucket, target_key)
code == StatusOK
end
def delete(bucket, key)
code, data = Storage.delete(bucket, key)
code == StatusOK
end
def batch(command, bucket, keys)
code, data = Storage.batch(command, bucket, keys)
code == StatusOK ? data : false
end
def batch_stat(bucket, keys)
code, data = Storage.batch_stat(bucket, keys)
code == StatusOK ? data : false
end
def batch_get(bucket, keys)
code, data = Storage.batch_get(bucket, keys)
code == StatusOK ? data : false
end
def batch_copy(*args)
code, data = Storage.batch_copy(args)
code == StatusOK
end
def batch_move(*args)
code, data = Storage.batch_move(args)
code == StatusOK
end
def batch_download(bucket, keys)
code, data = Storage.batch_get(bucket, keys)
return false unless code == StatusOK
links = []
data.each { |e| links << e["data"]["url"] }
links
end
def batch_delete(bucket, keys)
code, data = Storage.batch_delete(bucket, keys)
code == StatusOK ? data : false
end
def drop(bucket)
code, data = Storage.drop(bucket)
code == StatusOK
end
def image_info(url)
code, data = Fop::Image.info(url)
code == StatusOK ? data : false
end
def image_exif(url)
code, data = Fop::Image.exif(url)
code == StatusOK ? data : false
end
def image_mogrify_preview_url(source_image_url, options)
Fop::Image.mogrify_preview_url(source_image_url, options)
end
def image_mogrify_save_as(bucket, key, source_image_url, options)
code, data = Storage.image_mogrify_save_as(bucket, key, source_image_url, options)
code == StatusOK ? data : false
end
def generate_upload_token(opts = {})
token_obj = UploadToken.new(opts)
token_obj.access_key = Config.settings[:access_key]
token_obj.secret_key = Config.settings[:secret_key]
#token_obj.scope = opts[:scope]
#token_obj.expires_in = opts[:expires_in]
#token_obj.callback_url = opts[:callback_url]
#token_obj.callback_body_type = opts[:callback_body_type]
#token_obj.customer = opts[:customer]
#token_obj.escape = opts[:escape]
#token_obj.async_options = opts[:async_options]
#token_obj.return_body = opts[:return_body]
token_obj.generate_token
end
def generate_download_token(opts = {})
token_obj = DownloadToken.new(opts)
token_obj.access_key = Config.settings[:access_key]
token_obj.secret_key = Config.settings[:secret_key]
#token_obj.expires_in = opts[:expires_in]
#token_obj.pattern = opts[:pattern]
token_obj.generate_token
end
end
end # module Qiniu
+22
View File
@@ -0,0 +1,22 @@
# -*- encoding: utf-8 -*-
module Qiniu
module Abstract
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def abstract_methods(*args)
args.each do |name|
class_eval <<-END
def #{name}(*args)
errmsg = %Q(class \#{self.class.name} must implement abstract method #{self.name}##{name}().)
raise NotImplementedError.new(errmsg)
end
END
end
end
end
end # module Abstract
end # module Qiniu
+46
View File
@@ -0,0 +1,46 @@
# -*- encoding: utf-8 -*-
# vim: sw=2 ts=2
require 'uri'
module Qiniu
module ADT
class ApiSpecification
public
def to_str; return ""; end
end # class ApiSpecification
module Policy
public
def to_json
args = {}
self.params.each_pair do |key, fld|
val = self.__send__(key)
if !val.nil? then
args[fld] = val
end
end
return args.to_json
end # to_json
def to_query_string
args = []
self.params.each_pair do |key, fld|
val = self.__send__(key)
if !val.nil? then
new_fld = CGI.escape(fld.to_s)
new_val = CGI.escape(val.to_s).gsub('+', '%20')
args.push("#{new_fld}=#{new_val}")
end
end
return args.join("&")
end # to_query_string
end # module Policy
end # module ADT
end # module Qiniu
+222
View File
@@ -0,0 +1,222 @@
# -*- encoding: utf-8 -*-
# vim: sw=2 ts=2
require 'hmac-sha1'
require 'uri'
require 'qiniu/exceptions'
module Qiniu
module Auth
DEFAULT_AUTH_SECONDS = 3600
class << self
def calculate_deadline(expires_in, deadline = nil)
### 授权期计算
if expires_in.is_a?(Integer) && expires_in > 0 then
# 指定相对时间,单位:秒
return Time.now.to_i + expires_in
elsif deadline.is_a?(Integer) then
# 指定绝对时间,常用于调试和单元测试
return deadline
end
# 默认授权期1小时
return Time.now.to_i + DEFAULT_AUTH_SECONDS
end # calculate_deadline
end # class << self
class PutPolicy
private
def initialize(bucket,
key = nil,
expires_in = DEFAULT_AUTH_SECONDS,
deadline = nil)
### 设定scope参数(必填项目)
self.scope!(bucket, key)
### 设定deadline参数(必填项目)
@expires_in = expires_in
@deadline = Auth.calculate_deadline(expires_in, deadline)
end # initialize
PARAMS = {
# 字符串类型参数
:scope => "scope" ,
:save_key => "saveKey" ,
:end_user => "endUser" ,
:return_url => "returnUrl" ,
:return_body => "returnBody" ,
:callback_url => "callbackUrl" ,
:callback_body => "callbackBody" ,
:persistent_ops => "persistentOps" ,
:persistent_notify_url => "persistentNotifyUrl" ,
:transform => "transform" ,
# 数值类型参数
:deadline => "deadline" ,
:insert_only => "insertOnly" ,
:fsize_limit => "fsizeLimit" ,
:detect_mime => "detectMime" ,
:mime_limit => "mimeLimit" ,
:fop_timeout => "fopTimeout"
} # PARAMS
public
attr_reader :bucket, :key
def scope!(bucket, key = nil)
@bucket = bucket
@key = key
if key.nil? then
# 新增语义,文件已存在则失败
@scope = bucket
else
# 覆盖语义,文件已存在则直接覆盖
@scope = "#{bucket}:#{key}"
end
end # scope!
def expires_in!(seconds)
if !seconds.nil? then
return @expires_in
end
@epires_in = seconds
@deadline = Auth.calculate_deadline(seconds)
return @expires_in
end # expires_in!
def expires_in=(seconds)
return expires_in!(seconds)
end # expires_in=
def expires_in
return @expires_in
end # expires_in
def allow_mime_list! (list)
@mime_limit = list
end # allow_mime_list!
def deny_mime_list! (list)
@mime_limit = "!#{list}"
end # deny_mime_list!
def insert_only!
@insert_only = 1
end # insert_only!
def detect_mime!
@detect_mime = 1
end # detect_mime!
def to_json
args = {}
PARAMS.each_pair do |key, fld|
val = self.__send__(key)
if !val.nil? then
args[fld] = val
end
end
return args.to_json
end # to_json
PARAMS.each_pair do |key, fld|
attr_accessor key
end
end # class PutPolicy
class << self
EMPTY_ARGS = {}
### 生成下载授权URL
def authorize_download_url(url, args = EMPTY_ARGS)
### 提取AK/SK信息
access_key = Config.settings[:access_key]
secret_key = Config.settings[:secret_key]
### 授权期计算
e = Auth.calculate_deadline(args[:expires_in], args[:deadline])
### URL变换:追加授权期参数
if url.index('?').is_a?(Fixnum) then
# 已有参数
download_url = "#{url}&e=#{e}"
else
# 尚无参数
download_url = "#{url}?e=#{e}"
end
### 生成数字签名
sign = HMAC::SHA1.new(secret_key).update(download_url).digest
encoded_sign = Utils.urlsafe_base64_encode(sign)
### 生成下载授权凭证
dntoken = "#{access_key}:#{encoded_sign}"
### 返回下载授权URL
return "#{download_url}&token=#{dntoken}"
end # authorize_download_url
def generate_acctoken(url, body = '')
### 提取AK/SK信息
access_key = Config.settings[:access_key]
secret_key = Config.settings[:secret_key]
### 解析URL,生成待签名字符串
uri = URI.parse(url)
signing_str = uri.path
# 如有QueryString部分,则需要加上
query_string = uri.query
if query_string.is_a?(String) && !query_string.empty?
signing_str += '?' + query_string
end
# 追加换行符
signing_str += "\n"
# 如果有Body,则也加上
# (仅限于mime == "application/x-www-form-urlencoded"的情况)
if body.is_a?(String) && !body.empty?
signing_str += body
end
### 生成数字签名
sign = HMAC::SHA1.new(secret_key).update(signing_str).digest
encoded_sign = Utils.urlsafe_base64_encode(sign)
### 生成管理授权凭证
acctoken = "#{access_key}:#{encoded_sign}"
### 返回管理授权凭证
return acctoken
end # generate_acctoken
def generate_uptoken(put_policy)
### 提取AK/SK信息
access_key = Config.settings[:access_key]
secret_key = Config.settings[:secret_key]
### 生成待签名字符串
encoded_put_policy = Utils.urlsafe_base64_encode(put_policy.to_json)
### 生成数字签名
sign = HMAC::SHA1.new(secret_key).update(encoded_put_policy).digest
encoded_sign = Utils.urlsafe_base64_encode(sign)
### 生成上传授权凭证
uptoken = "#{access_key}:#{encoded_sign}:#{encoded_put_policy}"
### 返回上传授权凭证
return uptoken
end # generate_uptoken
end # class << self
end # module Auth
end # module Qiniu
+18 -12
View File
@@ -8,27 +8,34 @@
# Qbox::Config.load "path/to/your_project/config/qiniu.yml"
#
require "qiniu/rs/version"
require 'tmpdir'
module Qiniu
module RS
module Config
class << self
DEFAULT_OPTIONS = {
:user_agent => 'Qiniu-RS-Ruby-SDK-' + VERSION + '()',
:user_agent => 'Qiniu-RS-Ruby-SDK-' + Version.to_s + '()',
:method => :post,
:content_type => 'application/x-www-form-urlencoded',
:auth_url => "https://acc.qbox.me/oauth2/token",
:rs_host => "http://rs.qbox.me:10100",
:io_host => "http://io.qbox.me",
:client_id => "<YOUR_APP_CLIENT_ID>",
:client_secret => "<YOUR_APP_CLIENT_SECRET>",
:rs_host => "http://rs.qiniu.com",
:up_host => "http://up.qiniu.com",
:pub_host => "http://pu.qbox.me:10200",
:eu_host => "http://eu.qbox.me",
:client_id => "a75604760c4da4caaa456c0c5895c061c3065c5a",
:client_secret => "75df554a39f58accb7eb293b550fa59618674b7d",
:access_key => "",
:secret_key => "",
:auto_reconnect => true,
:max_retry_times => 5
:max_retry_times => 3,
:block_size => 1024*1024*4,
:chunk_size => 1024*256,
:enable_debug => true,
:tmpdir => Dir.tmpdir + File::SEPARATOR + 'Qiniu-RS-Ruby-SDK'
}
REQUIRED_OPTION_KEYS = [:client_id, :client_secret, :auth_url, :rs_host, :io_host]
REQUIRED_OPTION_KEYS = [:access_key, :secret_key]
attr_reader :settings, :default_params
@@ -49,6 +56,5 @@ module Qiniu
end
end
end
end
end
end # module Config
end # module Qiniu
@@ -1,12 +1,8 @@
# -*- encoding: utf-8 -*-
module Qiniu
module RS
class Exception < RuntimeError
def to_s
inspect
end
end
class ResponseError < Exception
@@ -32,7 +28,7 @@ module Qiniu
class RequestFailed < ResponseError
def message
"HTTP status code #{http_code}"
"HTTP status code: #{http_code}. Response body: #{http_body}"
end
def to_s
@@ -40,6 +36,43 @@ module Qiniu
end
end
class UploadFailedError < Exception
def initialize(status_code, response_data)
data_string = response_data.map { |key, value| %Q(:#{key.to_s} => #{value.to_s}) }
msg = %Q(Uploading Failed. HTTP Status Code: #{status_code}. HTTP response body: #{data_string.join(', ')}.)
super(msg)
end
end
class FileSeekReadError < Exception
def initialize(fpath, block_index, seek_pos, read_length, result_length)
msg = "Reading file: #{fpath}, "
msg += "at block index: #{block_index}. "
msg += "Expected seek_pos:#{seek_pos} and read_length:#{read_length}, "
msg += "but got result_length: #{result_length}."
super(msg)
end
end
class BlockSizeNotMathchError < Exception
def initialize(fpath, block_index, offset, restsize, block_size)
msg = "Reading file: #{fpath}, "
msg += "at block index: #{block_index}. "
msg += "Expected offset: #{offset}, restsize: #{restsize} and block_size: #{block_size}, "
msg += "but got offset+restsize=#{offset+restsize}."
super(msg)
end
end
class BlockCountNotMathchError < Exception
def initialize(fpath, block_count, checksum_count, progress_count)
msg = "Reading file: #{fpath}, "
msg += "Expected block_count, checksum_count, progress_count is: #{block_count}, "
msg += "but got checksum_count: #{checksum_count}, progress_count: #{progress_count}."
super(msg)
end
end
class MissingArgsError < Exception
def initialize(missing_keys)
key_list = missing_keys.map {|key| key.to_s}.join(' and the ')
@@ -84,5 +117,4 @@ module Qiniu
end
end
end
end
end # module Qiniu
+4
View File
@@ -0,0 +1,4 @@
# -*- encoding: utf-8 -*-
require 'qiniu/image'
require 'qiniu/pfop'
+137
View File
@@ -0,0 +1,137 @@
# -*- encoding: utf-8 -*-
# vim: sw=2 ts=2
module Qiniu
module HTTP
class << self
public
def is_response_ok?(http_code)
return 200 <= http_code && http_code <= 299
end # is_response_ok?
def generate_query_string(params)
if params.is_a?(Hash)
total_param = params.map { |key, value| %Q(#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s).gsub('+', '%20')}) }
return total_param.join("&")
end
return params
end # generate_query_string
def get (url, opts = {})
### 配置请求Header
req_headers = {
:connection => 'close',
:accept => '*/*',
:user_agent => Config.settings[:user_agent]
}
# 优先使用外部Header,覆盖任何特定Header
if opts[:headers].is_a?(Hash) then
req_headers.merge!(opts[:headers])
end
### 发送请求
response = RestClient.get(url, req_headers)
return response.code.to_i, response.body, response.raw_headers
rescue => e
Log.logger.warn "#{e.message} => Qiniu::HTTP.get('#{url}')"
if e.respond_to?(:response) && e.response.respond_to?(:code) then
return e.response.code, e.response.body, e.response.raw_headers
end
return nil, nil, nil
end # get
API_RESULT_MIMETYPE = 'application/json'
def api_get (url, opts = {})
### 配置请求Header
headers = {
:accept => API_RESULT_MIMETYPE
}
# 将特定Header混入外部Header中
if opts[:headers].is_a?(Hash) then
opts[:headers] = opts[:headers].dup.merge!(headers)
else
opts[:headers] = headers
end
### 发送请求,然后转换返回值
resp_code, resp_body, resp_headers = get(url, opts)
if resp_code.nil? then
return 0, {}, {}
end
content_type = resp_headers["content-type"][0]
if !content_type.nil? && content_type == API_RESULT_MIMETYPE then
# 如果是JSON格式,则反序列化
resp_body = Utils.safe_json_parse(resp_body)
end
return resp_code, resp_body, resp_headers
end # api_get
def post (url, req_body = nil, opts = {})
### 配置请求Header
req_headers = {
:connection => 'close',
:accept => '*/*',
:user_agent => Config.settings[:user_agent]
}
# 优先使用外部Header,覆盖任何特定Header
if opts[:headers].is_a?(Hash) then
req_headers.merge!(opts[:headers])
end
### 发送请求
response = RestClient.post(url, req_body, req_headers)
return response.code.to_i, response.body, response.raw_headers
rescue => e
Log.logger.warn "#{e.message} => Qiniu::HTTP.post('#{url}')"
if e.respond_to?(:response) && e.response.respond_to?(:code) then
return e.response.code, e.response.body, e.response.raw_headers
end
return nil, nil, nil
end # post
def api_post (url, req_body = nil, opts = {})
### 配置请求Header
headers = {
:accept => API_RESULT_MIMETYPE
}
# 将特定Header混入外部Header中
if opts[:headers].is_a?(Hash) then
opts[:headers] = opts[:headers].dup.merge!(headers)
else
opts[:headers] = headers
end
### 发送请求,然后转换返回值
resp_code, resp_body, resp_headers = post(url, req_body, opts)
if resp_code.nil? then
return 0, {}, {}
end
content_type = resp_headers["content-type"][0]
if !content_type.nil? && content_type == API_RESULT_MIMETYPE then
# 如果是JSON格式,则反序列化
resp_body = Utils.safe_json_parse(resp_body)
end
return resp_code, resp_body, resp_headers
end # api_post
def management_post (url, body = '')
### 授权并执行管理操作
return HTTP.api_post(url, body, {
:headers => { 'Authorization' => 'QBox ' + Auth.generate_acctoken(url, body) }
})
end # management_post
end # class << self
end # module HTTP
end # module Qiniu
+38
View File
@@ -0,0 +1,38 @@
# -*- encoding: utf-8 -*-
module Qiniu
module Fop
module Image
class << self
include Utils
def info(url)
return HTTP.api_get(url + '?imageInfo')
end # info
def exif(url)
return HTTP.api_get(url + '?exif')
end # exif
def mogrify_preview_url(source_image_url, options)
source_image_url + '?' + generate_mogrify_params_string(options)
end
def generate_mogrify_params_string(options = {})
opts = {}
options.each do |k, v|
opts[k.to_s] = v
end
params_string = ""
keys = ["thumbnail", "gravity", "crop", "quality", "rotate", "format"]
keys.each do |key|
params_string += %Q(/#{key}/#{opts[key]}) unless opts[key].nil?
end
params_string += '/auto-orient' unless opts["auto_orient"].nil?
'imageMogr' + URI.escape(params_string)
end
end
end # module Image
end # module Fop
end # module Qiniu
+2 -4
View File
@@ -3,7 +3,6 @@
require 'logger'
module Qiniu
module RS
module Log
class << self
attr_accessor :logger
@@ -12,6 +11,5 @@ module Qiniu
@logger ||= Logger.new(STDERR)
end
end
end
end
end
end # module Log
end # module Qiniu
+128
View File
@@ -0,0 +1,128 @@
# -*- encoding: utf-8 -*-
# vim: sw=2 ts=2
require 'qiniu/http'
module Qiniu
module Storage
class << self
include Utils
public
def buckets
url = Config.settings[:rs_host] + '/buckets'
return HTTP.management_post(url)
end # buckets
PRIVATE_BUCKET = 0
PUBLIC_BUCKET = 1
def mkbucket(bucket_name, is_public = PUBLIC_BUCKET)
url = Config.settings[:rs_host] + '/mkbucket2/' + bucket_name + '/public/' + is_public.to_s
return HTTP.management_post(url)
end # mkbucket
def make_a_private_bucket(bucket_name)
return mkbucket(bucket_name, PRIVATE_BUCKET)
end # make_a_private_bucket
def make_a_public_bucket(bucket_name)
return mkbucket(bucket_name, PUBLIC_BUCKET)
end # make_a_public_bucket
def stat(bucket, key)
url = Config.settings[:rs_host] + '/stat/' + encode_entry_uri(bucket, key)
return HTTP.management_post(url)
end # stat
def get(bucket, key, save_as = nil, expires_in = nil, version = nil)
url = Config.settings[:rs_host] + '/get/' + encode_entry_uri(bucket, key)
url += '/base/' + version unless version.nil?
url += '/attName/' + Utils.urlsafe_base64_encode(save_as) unless save_as.nil?
url += '/expires/' + expires_in.to_s if !expires_in.nil? && expires_in > 0
return HTTP.management_post(url)
end # get
def copy(source_bucket, source_key, target_bucket, target_key)
uri = _generate_cp_or_mv_opstr('copy', source_bucket, source_key, target_bucket, target_key)
url = Config.settings[:rs_host] + uri
return HTTP.management_post(url)
end # copy
def move(source_bucket, source_key, target_bucket, target_key)
uri = _generate_cp_or_mv_opstr('move', source_bucket, source_key, target_bucket, target_key)
url = Config.settings[:rs_host] + uri
return HTTP.management_post(url)
end # move
def delete(bucket, key)
url = Config.settings[:rs_host] + '/delete/' + encode_entry_uri(bucket, key)
return HTTP.management_post(url)
end # delete
def drop(bucket)
url = Config.settings[:rs_host] + "/drop/#{bucket}"
return HTTP.management_post(url)
end # drop
def batch(command, bucket, keys)
execs = []
keys.each do |key|
encoded_uri = encode_entry_uri(bucket, key)
execs << "op=/#{command}/#{encoded_uri}"
end
url = Config.settings[:rs_host] + "/batch"
return HTTP.management_post(url, execs.join("&"))
end # batch
def batch_get(bucket, keys)
batch("get", bucket, keys)
end # batch_get
def batch_stat(bucket, keys)
batch("stat", bucket, keys)
end # batch_stat
def batch_copy(*args)
_batch_cp_or_mv('copy', args)
end # batch_copy
def batch_move(*args)
_batch_cp_or_mv('move', args)
end # batch_move
def batch_delete(bucket, keys)
batch("delete", bucket, keys)
end # batch_delete
def save_as(bucket, key, source_url, op_params_string)
encoded_uri = encode_entry_uri(bucket, key)
save_as_string = '/save-as/' + encoded_uri
new_url = source_url + '?' + op_params_string + save_as_string
return HTTP.management_post(new_url)
end # save_as
def image_mogrify_save_as(bucket, key, source_image_url, options)
mogrify_params_string = Fop::Image.generate_mogrify_params_string(options)
save_as(bucket, key, source_image_url, mogrify_params_string)
end # image_mogrify_save_as
private
def _generate_cp_or_mv_opstr(command, source_bucket, source_key, target_bucket, target_key)
source_encoded_entry_uri = encode_entry_uri(source_bucket, source_key)
target_encoded_entry_uri = encode_entry_uri(target_bucket, target_key)
%Q(/#{command}/#{source_encoded_entry_uri}/#{target_encoded_entry_uri})
end # _generate_cp_or_mv_opstr
def _batch_cp_or_mv(command, *op_args)
execs = []
op_args.each do |e|
execs << 'op=' + _generate_cp_or_mv_opstr(command, e[0], e[1], e[2], e[3]) if e.size == 4
end
url = Config.settings[:rs_host] + "/batch"
return HTTP.management_post(url, execs.join("&"))
end # _batch_cp_or_mv
end
end # module Storage
end # module Qiniu
+33
View File
@@ -0,0 +1,33 @@
# -*- encoding: utf-8 -*-
module Qiniu
module Misc
class << self
def set_protected(bucket, protected_mode)
url = Config.settings[:pub_host] + %Q(/accessMode/#{bucket}/mode/#{protected_mode})
return HTTP.management_post(url)
end # set_protected
def set_separator(bucket, separator)
encoded_separator = Utils.urlsafe_base64_encode(separator)
url = Config.settings[:pub_host] + %Q(/separator/#{bucket}/sep/#{encoded_separator})
return HTTP.management_post(url)
end # set_separator
def set_style(bucket, name, style)
encoded_name = Utils.urlsafe_base64_encode(name)
encoded_style = Utils.urlsafe_base64_encode(style)
url = Config.settings[:pub_host] + %Q(/style/#{bucket}/name/#{encoded_name}/style/#{encoded_style})
return HTTP.management_post(url)
end # set_style
def unset_style(bucket, name)
encoded_name = Utils.urlsafe_base64_encode(name)
url = Config.settings[:pub_host] + %Q(/unstyle/#{bucket}/name/#{encoded_name})
return HTTP.management_post(url)
end # unset_style
end # class << self
end # module Misc
end # module Qiniu
+123
View File
@@ -0,0 +1,123 @@
# -*- encoding: utf-8 -*-
# vim: sw=2 ts=2
require 'qiniu/adt'
require 'qiniu/http'
module Qiniu
module Fop
module Persistance
class PfopPolicy
include ADT::Policy
private
def initialize(bucket,
key,
fops,
notify_url)
@bucket = bucket
@key = key
@notify_url = notify_url
self.fops!(fops)
end # initialize
public
PARAMS = {
# 字符串类型参数
:bucket => "bucket",
:key => "key",
:fops => "fops",
:notify_url => "notifyURL",
# 数值类型参数
:force => "force"
} # PARAMS
PARAMS.each_pair do |key, fld|
attr_accessor key
end
def params
return PARAMS
end # params
def fops! (fops)
if fops.is_a?(Hash) then
fops = fops.values
end
if fops.is_a?(Array) then
new_fops = []
fops.each do |v|
if v.is_a?(ApiSpecification) then
new_fops.push(v.to_s)
end
end
@fops = new_fops.join(";")
else
@fops = fops.to_s
end
end # fops!
def force!
@force = 1
end # force!
alias :to_s :to_json
end # class PfopPolicy
class << self
API_HOST = 'http://api.qiniu.com'
PFOP_URL = API_HOST + '/pfop/'
def pfop (args)
### 生成fop指令串
if args.is_a?(PfopPolicy) then
# PfopPolicy的各个字段按固定顺序组织
body = args.to_query_string()
elsif args.is_a?(Hash) then
# 无法保证固定字段顺序
body = HTTP.generate_query_string(args)
else
# 由调用者保证固定字段顺序
body = args.to_s
end
### 发送请求
return HTTP.management_post(PFOP_URL, body)
end # pfop
PREFOP_URL = API_HOST + '/status/get/prefop?id='
def prefop (persistent_id)
### 抽取persistentId
if persistent_id.is_a?(Hash) then
pid = persistent_id['persistentId']
else
pid = persistent_id.to_s
end
### 发送请求
url = PREFOP_URL + pid
return HTTP.api_get(url)
end # prefop
def generate_p1_url (url, fop)
# 如果fop是ApiSpecification,则各字段按固定顺序组织,保证一致性
# 否则由调用者保证固定字段顺序
fop = CGI.escape(fop.to_s).gsub('+', '%20')
### 生成url
return url + '?p/1/' + fop
end # generate_pl_url
end # class << self
end # module Persistance
end # module Fop
end # module Qiniu
+319
View File
@@ -0,0 +1,319 @@
# -*- encoding: utf-8 -*-
require 'zlib'
require 'yaml'
require 'tmpdir'
require 'fileutils'
require 'mime/types'
require 'digest/sha1'
require 'qiniu/abstract'
require 'qiniu/exceptions'
module Qiniu
module Storage
module AbstractClass
class ChunkProgressNotifier
include Qiniu::Abstract
abstract_methods :notify
# def notify(block_index, block_put_progress); end
end
class BlockProgressNotifier
include Qiniu::Abstract
abstract_methods :notify
# def notify(block_index, checksum); end
end
end # module AbstractClass
class ChunkProgressNotifier < AbstractClass::ChunkProgressNotifier
def notify(index, progress)
logmsg = "chunk #{progress[:offset]/Config.settings[:chunk_size]} in block #{index} successfully uploaded.\n" + progress.to_s
Utils.debug(logmsg)
end
end # class ChunkProgressNotifier
class BlockProgressNotifier < AbstractClass::BlockProgressNotifier
def notify(index, checksum)
Utils.debug "block #{index}: {ctx: #{checksum}} successfully uploaded."
Utils.debug "block #{index}: {checksum: #{checksum}} successfully uploaded."
end
end # class BlockProgressNotifier
class << self
include Utils
def resumable_upload_with_token(uptoken,
local_file,
bucket,
key = nil,
mime_type = nil,
custom_meta = nil,
customer = nil,
callback_params = nil,
rotate = nil)
begin
ifile = File.open(local_file, 'rb')
fh = FileData.new(ifile)
fsize = fh.data_size
key = Digest::SHA1.hexdigest(local_file + fh.mtime.to_s) if key.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
code, data = _resumable_upload(uptoken, fh, fsize, bucket, key, mime_type, custom_meta, customer, callback_params, rotate)
[code, data]
ensure
ifile.close unless ifile.nil?
end
end # resumable_upload_with_token
private
class FileData
attr_accessor :fh
def initialize(fh)
@fh = fh
end
def data_size
@fh.stat.size
end
def get_data(offset, length)
@fh.seek(offset)
@fh.read(length)
end
def path
@fh.path
end
def mtime
@fh.mtime
end
#delegate :path, :mtime, :to => :fh
end # class FileData
def _new_block_put_progress_data
{:ctx => nil, :offset => 0, :restsize => nil, :status_code => nil, :host => nil}
end # _new_block_put_progress_data
def _call_binary_with_token(uptoken, url, data, content_type = nil, retry_times = 0)
options = {
:headers => {
:content_type => 'application/octet-stream',
'Authorization' => 'UpToken ' + uptoken
}
}
if !content_type.nil? && !content_type.empty? then
options[:headers][:content_type] = content_type
end
code, data, raw_headers = HTTP.api_post(url, data, options)
unless HTTP.is_response_ok?(code)
retry_times += 1
if Config.settings[:auto_reconnect] && retry_times < Config.settings[:max_retry_times]
return _call_binary_with_token(uptoken, url, data, options[:content_type], retry_times)
end
end
return code, data, raw_headers
end # _call_binary_with_token
def _mkblock(uptoken, block_size, body)
url = Config.settings[:up_host] + "/mkblk/#{block_size}"
_call_binary_with_token(uptoken, url, body)
end # _mkblock
def _putblock(uphost, uptoken, ctx, offset, body)
url = uphost + "/bput/#{ctx}/#{offset}"
_call_binary_with_token(uptoken, url, body)
end # _putblock
def _resumable_put_block(uptoken,
fh,
block_index,
block_size,
chunk_size,
progress,
retry_times,
notifier)
code, data = 0, {}
fpath = fh.path
# this block has never been uploaded.
if progress[:ctx] == nil || progress[:ctx].empty?
progress[:offset] = 0
progress[:restsize] = block_size
# choose the smaller one
body_length = [block_size, chunk_size].min
for i in 1..retry_times
seek_pos = block_index*Config.settings[:block_size]
body = fh.get_data(seek_pos, body_length)
result_length = body.length
if result_length != body_length
raise FileSeekReadError.new(fpath, block_index, seek_pos, body_length, result_length)
end
code, data, raw_headers = _mkblock(uptoken, block_size, body)
Utils.debug "Mkblk : #{code.inspect} #{data.inspect} #{raw_headers.inspect}"
body_crc32 = Zlib.crc32(body)
if HTTP.is_response_ok?(code) && data["crc32"] == body_crc32
progress[:ctx] = data["ctx"]
progress[:offset] = body_length
progress[:restsize] = block_size - body_length
progress[:status_code] = code
progress[:host] = data["host"]
if !notifier.nil? && notifier.respond_to?("notify")
notifier.notify(block_index, progress)
end
break
elsif i == retry_times && data["crc32"] != body_crc32
Log.logger.error %Q(Uploading block error. Expected crc32: #{body_crc32}, but got: #{data["crc32"]})
return code, data, raw_headers
end
end
elsif progress[:offset] + progress[:restsize] != block_size
raise BlockSizeNotMathchError.new(fpath, block_index, progress[:offset], progress[:restsize], block_size)
end
# loop uploading other chunks except the first one
while progress[:restsize].to_i > 0 && progress[:restsize] < block_size
# choose the smaller one
body_length = [progress[:restsize], chunk_size].min
for i in 1..retry_times
seek_pos = block_index*Config.settings[:block_size] + progress[:offset]
body = fh.get_data(seek_pos, body_length)
result_length = body.length
if result_length != body_length
raise FileSeekReadError.new(fpath, block_index, seek_pos, body_length, result_length)
end
code, data, raw_headers = _putblock(progress[:host], uptoken, progress[:ctx], progress[:offset], body)
Utils.debug "Bput : #{code.inspect} #{data.inspect} #{raw_headers.inspect}"
body_crc32 = Zlib.crc32(body)
if HTTP.is_response_ok?(code) && data["crc32"] == body_crc32
progress[:ctx] = data["ctx"]
progress[:offset] += body_length
progress[:restsize] -= body_length
progress[:status_code] = code
progress[:host] = data["host"]
if !notifier.nil? && notifier.respond_to?("notify")
notifier.notify(block_index, progress)
end
break
elsif i == retry_times && data["crc32"] != body_crc32
Log.logger.error %Q(Uploading block error. Expected crc32: #{body_crc32}, but got: #{data["crc32"]})
return code, data, raw_headers
end
end
end
# return
return code, data, raw_headers
end # _resumable_put_block
def _block_count(fsize)
((fsize + Config.settings[:block_size] - 1) / Config.settings[:block_size]).to_i
end # _block_count
def _resumable_put(uptoken,
fh,
checksums,
progresses,
block_notifier = nil,
chunk_notifier = nil)
code, data = 0, {}
fsize = fh.data_size
block_count = _block_count(fsize)
checksum_count = checksums.length
progress_count = progresses.length
if checksum_count != block_count || progress_count != block_count
raise BlockCountNotMathchError.new(fh.path, block_count, checksum_count, progress_count)
end
0.upto(block_count-1).each do |block_index|
if checksums[block_index].nil? || checksums[block_index].empty?
block_size = Config.settings[:block_size]
if block_index == block_count - 1
block_size = fsize - block_index*Config.settings[:block_size]
end
if progresses[block_index].nil?
progresses[block_index] = _new_block_put_progress_data
end
#code, data = _resumable_put_block(uptoken, fh, block_index, block_size, Config.settings[:chunk_size], progresses[block_index], Config.settings[:max_retry_times], chunk_notifier)
# Put the whole block as a chunk
code, data = _resumable_put_block(uptoken, fh, block_index, block_size, block_size, progresses[block_index], Config.settings[:max_retry_times], chunk_notifier)
if HTTP.is_response_ok?(code)
#checksums[block_index] = data["checksum"]
checksums[block_index] = data["ctx"]
if !block_notifier.nil? && block_notifier.respond_to?("notify")
block_notifier.notify(block_index, checksums[block_index])
end
end
end
end
return [code, data]
end # _resumable_put
def _mkfile(uphost,
uptoken,
entry_uri,
fsize,
checksums,
mime_type = nil,
custom_meta = nil,
customer = nil,
callback_params = nil,
rotate = nil)
path = '/rs-mkfile/' + Utils.urlsafe_base64_encode(entry_uri) + "/fsize/#{fsize}"
path += '/mimeType/' + Utils.urlsafe_base64_encode(mime_type) if !mime_type.nil? && !mime_type.empty?
path += '/meta/' + Utils.urlsafe_base64_encode(custom_meta) if !custom_meta.nil? && !custom_meta.empty?
path += '/customer/' + customer if !customer.nil? && !customer.empty?
callback_query_string = HTTP.generate_query_string(callback_params) if !callback_params.nil? && !callback_params.empty?
path += '/params/' + Utils.urlsafe_base64_encode(callback_query_string) if !callback_query_string.nil? && !callback_query_string.empty?
path += '/rotate/' + rotate if !rotate.nil? && rotate.to_i >= 0
url = uphost + path
#body = ''
#checksums.each do |checksum|
# body += Utils.urlsafe_base64_decode(checksum)
#end
body = checksums.join(',')
_call_binary_with_token(uptoken, url, body, 'text/plain')
end # _mkfile
def _resumable_upload(uptoken,
fh,
fsize,
bucket,
key,
mime_type = nil,
custom_meta = nil,
customer = nil,
callback_params = nil,
rotate = nil)
block_count = _block_count(fsize)
chunk_notifier = ChunkProgressNotifier.new()
block_notifier = BlockProgressNotifier.new()
progresses = []
block_count.times{progresses << _new_block_put_progress_data}
checksums = []
block_count.times{checksums << ''}
code, data, raw_headers = _resumable_put(uptoken, fh, checksums, progresses, block_notifier, chunk_notifier)
if HTTP.is_response_ok?(code)
uphost = data["host"]
entry_uri = bucket + ':' + key
code, data, raw_headers = _mkfile(uphost, uptoken, entry_uri, fsize, checksums, mime_type, custom_meta, customer, callback_params, rotate)
Utils.debug "Mkfile : #{code.inspect} #{data.inspect} #{raw_headers.inspect}"
end
if HTTP.is_response_ok?(code)
Utils.debug "File #{fh.path} {size: #{fsize}} successfully uploaded."
end
return code, data, raw_headers
end # _resumable_upload
end # self class
end # module Storage
end # module Qiniu
-114
View File
@@ -1,114 +0,0 @@
# -*- encoding: utf-8 -*-
require 'qiniu/rs/version'
module Qiniu
module RS
autoload :Config, 'qiniu/rs/config'
autoload :Log, 'qiniu/rs/log'
autoload :Exception, 'qiniu/rs/exceptions'
autoload :Utils, 'qiniu/rs/utils'
autoload :Auth, 'qiniu/rs/auth'
autoload :IO, 'qiniu/rs/io'
autoload :RS, 'qiniu/rs/rs'
autoload :Image, 'qiniu/rs/image'
class << self
StatusOK = 200
def establish_connection!(opts = {})
Config.initialize_connect opts
end
def login!(user, pwd)
code, data = Auth.exchange_by_password!(user, pwd)
code == StatusOK
end
def put_auth(expires_in = nil, callback_url = nil)
code, data = IO.put_auth(expires_in, callback_url)
code == StatusOK ? data["url"] : false
end
def upload(url, local_file, bucket = '', key = '', mime_type = '', custom_meta = '', callback_params = {})
code, data = IO.put_file(url, local_file, bucket, key, mime_type, custom_meta, callback_params)
code == StatusOK
end
def stat(bucket, key)
code, data = RS.stat(bucket, key)
code == StatusOK ? data : false
end
def get(bucket, key, save_as = nil, expires_in = nil, version = nil)
code, data = RS.get(bucket, key, save_as, expires_in, version)
code == StatusOK ? data : false
end
def download(bucket, key, save_as = nil, expires_in = nil, version = nil)
code, data = RS.get(bucket, key, save_as, expires_in, version)
code == StatusOK ? data["url"] : false
end
def delete(bucket, key)
code, data = RS.delete(bucket, key)
code == StatusOK
end
def batch(command, bucket, keys)
code, data = RS.batch(command, bucket, keys)
code == StatusOK ? data : false
end
def batch_stat(bucket, keys)
code, data = RS.batch_stat(bucket, keys)
code == StatusOK ? data : false
end
def batch_get(bucket, keys)
code, data = RS.batch_get(bucket, keys)
code == StatusOK ? data : false
end
def batch_download(bucket, keys)
code, data = RS.batch_get(bucket, keys)
return false unless code == StatusOK
links = []
data.each { |e| links << e["data"]["url"] }
links
end
def batch_delete(bucket, keys)
code, data = RS.batch_delete(bucket, keys)
code == StatusOK ? data : false
end
def publish(domain, bucket)
code, data = RS.publish(domain, bucket)
code == StatusOK
end
def unpublish(domain)
code, data = RS.unpublish(domain)
code == StatusOK
end
def drop(bucket)
code, data = RS.drop(bucket)
code == StatusOK
end
def image_info(url)
code, data = Image.info(url)
code == StatusOK ? data : false
end
def image_preview_url(url, spec)
Image.preivew_url(url, spec)
end
end
end
end
-74
View File
@@ -1,74 +0,0 @@
# -*- encoding: utf-8 -*-
require 'qiniu/rs/exceptions'
module Qiniu
module RS
module Auth
class << self
include Utils
def exchange_by_password!(username, password)
@username = username
@password = password
post_data = {
:client_id => Config.settings[:client_id],
:grant_type => "password",
:username => username,
:password => password
}
code, data = http_request Config.settings[:auth_url], post_data
reset_token(data["access_token"], data["refresh_token"]) if code == 200
[code, data]
end
def exchange_by_refresh_token!(refresh_token)
post_data = {
:client_id => Config.settings[:client_id],
:grant_type => "refresh_token",
:refresh_token => refresh_token
}
code, data = http_request Config.settings[:auth_url], post_data
reset_token(data["access_token"], data["refresh_token"]) if code == 200
[code, data]
end
def reset_token(access_token, refresh_token)
@access_token = access_token
@refresh_token = refresh_token
end
def call(url, data, retry_times = 0)
raise MissingAccessToken if @access_token.nil?
code, data = http_request url, data, {:access_token => @access_token}
if code == 401
raise MissingRefreshToken if @refresh_token.nil?
code, data = exchange_by_refresh_token!(@refresh_token)
if code == 401
raise MissingUsernameOrPassword if (@username.nil? || @password.nil?)
code, data = exchange_by_password!(@username, @password)
end
if code == 200
retry_times += 1
if Config.settings[:auto_reconnect] && retry_times < Config.settings[:max_retry_times]
return call(url, data, retry_times)
end
end
end
[code, data]
end
def request(url, data = nil)
begin
code, data = Auth.call(url, data)
rescue [MissingAccessToken, MissingRefreshToken, MissingUsernameOrPassword] => e
Log.logger.error e
code, data = 401, {}
end
[code, data]
end
end
end
end
end
-20
View File
@@ -1,20 +0,0 @@
# -*- encoding: utf-8 -*-
module Qiniu
module RS
module Image
class << self
include Utils
def info(url)
Utils.http_request url + '/imageInfo', nil, {:method => :get}
end
def preivew_url(url, spec)
url + '/imagePreview/' + spec.to_s
end
end
end
end
end
-41
View File
@@ -1,41 +0,0 @@
# -*- encoding: utf-8 -*-
require 'mime/types'
require 'digest/sha1'
require 'qiniu/rs/exceptions'
module Qiniu
module RS
module IO
class << self
include Utils
def put_auth(expires_in = nil, callback_url = nil)
url = Config.settings[:io_host] + "/put-auth/"
url += "#{expires_in}" if !expires_in.nil? && expires_in > 0
if !callback_url.nil? && !callback_url.empty?
encoded_callback_url = Utils.urlsafe_base64_encode(callback_url)
url += "/callback/#{encoded_callback_url}"
end
Auth.request(url)
end
def put_file(url, local_file, bucket = '', key = '', mime_type = '', custom_meta = '', callback_params = '')
raise NoSuchFileError unless File.exist?(local_file)
key = Digest::SHA1.hexdigest(local_file + Time.now.to_s) if key.empty?
entry_uri = bucket + ':' + key
if 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.empty?
callback_params = {:bucket => bucket, :key => key, :mime_type => mime_type} if callback_params.empty?
callback_query_string = Utils.generate_query_string(callback_params)
Utils.upload_multipart_data(url, local_file, action_params, callback_query_string)
end
end
end
end
end
-63
View File
@@ -1,63 +0,0 @@
# -*- encoding: utf-8 -*-
module Qiniu
module RS
module RS
class << self
include Utils
def stat(bucket, key)
Auth.request Config.settings[:rs_host] + '/stat/' + encode_entry_uri(bucket, key)
end
def get(bucket, key, save_as = nil, expires_in = nil, version = nil)
url = Config.settings[:rs_host] + '/get/' + encode_entry_uri(bucket, key)
url += '/base/' + version unless version.nil?
url += '/attName/' + Utils.urlsafe_base64_encode(save_as) unless save_as.nil?
url += '/expires/' + expires_in.to_s if !expires_in.nil? && expires_in > 0
Auth.request url
end
def delete(bucket, key)
Auth.request Config.settings[:rs_host] + '/delete/' + encode_entry_uri(bucket, key)
end
def publish(domain, bucket)
encoded_domain = Utils.urlsafe_base64_encode(domain)
Auth.request Config.settings[:rs_host] + "/publish/#{encoded_domain}/from/#{bucket}"
end
def unpublish(domain)
encoded_domain = Utils.urlsafe_base64_encode(domain)
Auth.request Config.settings[:rs_host] + "/unpublish/#{encoded_domain}"
end
def drop(bucket)
Auth.request Config.settings[:rs_host] + "/drop/#{bucket}"
end
def batch(command, bucket, keys)
execs = []
keys.each do |key|
encoded_uri = encode_entry_uri(bucket, key)
execs << "op=/#{command}/#{encoded_uri}"
end
Auth.request Config.settings[:rs_host] + "/batch?" + execs.join("&")
end
def batch_get(bucket, keys)
batch("get", bucket, keys)
end
def batch_stat(bucket, keys)
batch("stat", bucket, keys)
end
def batch_delete(bucket, keys)
batch("delete", bucket, keys)
end
end
end
end
end
-7
View File
@@ -1,7 +0,0 @@
# -*- encoding: utf-8 -*-
module Qiniu
module RS
VERSION = "1.0.3"
end
end
+5
View File
@@ -0,0 +1,5 @@
# -*- encoding: utf-8 -*-
require 'qiniu/upload'
require 'qiniu/resumable_upload'
require 'qiniu/management'
+21
View File
@@ -0,0 +1,21 @@
# -*- encoding: utf-8 -*-
require 'hmac-sha1'
require 'qiniu/config'
require 'qiniu/utils'
module Qiniu
class AccessToken
include Utils
attr_accessor :access_key, :secret_key
def generate_encoded_digest(signature)
hmac = HMAC::SHA1.new(@secret_key)
hmac.update(signature)
urlsafe_base64_encode(hmac.digest)
end
end # AccessToken
end # module Qiniu
+31
View File
@@ -0,0 +1,31 @@
# -*- encoding: utf-8 -*-
require 'json'
require 'qiniu/tokens/access_token'
require 'qiniu/utils'
module Qiniu
class DownloadToken < AccessToken
include Utils
attr_accessor :pattern, :expires_in
def initialize(opts = {})
@pattern = opts[:pattern]
@expires_in = opts[:expires_in] || 3600
end
def generate_signature
params = {"S" => @pattern, "E" => Time.now.to_i + @expires_in}
Utils.urlsafe_base64_encode(params.to_json)
end
def generate_token
signature = generate_signature
encoded_digest = generate_encoded_digest(signature)
%Q(#{@access_key}:#{encoded_digest}:#{signature})
end
end # moidule DownloadToken
end # module Qiniu
+38
View File
@@ -0,0 +1,38 @@
# -*- encoding: utf-8 -*-
require 'cgi'
require 'json'
require 'qiniu/tokens/access_token'
module Qiniu
class QboxToken < AccessToken
include Utils
attr_accessor :url, :params
def initialize(opts = {})
@url = opts[:url]
@params = opts[:params]
end
def generate_signature
uri = URI.parse(@url)
signature = uri.path
query_string = uri.query
signature += '?' + query_string if !query_string.nil? && !query_string.empty?
signature += "\n"
if @params.is_a?(Hash)
params_string = HTTP.generate_query_string(@params)
signature += params_string
end
signature
end
def generate_token
encoded_digest = generate_encoded_digest(generate_signature)
%Q(#{@access_key}:#{encoded_digest})
end
end # module QboxToken
end # module Qiniu
+47
View File
@@ -0,0 +1,47 @@
# -*- encoding: utf-8 -*-
require 'json'
require 'qiniu/tokens/access_token'
require 'qiniu/utils'
module Qiniu
class UploadToken < AccessToken
include Utils
attr_accessor :scope, :expires_in, :callback_url, :callback_body, :callback_body_type, :customer, :escape, :async_options, :return_body, :return_url
def initialize(opts = {})
@scope = opts[:scope]
@expires_in = opts[:expires_in] || 3600
@callback_url = opts[:callback_url]
@callback_body = opts[:callback_body]
@callback_body_type = opts[:callback_body_type]
@customer = opts[:customer]
@escape = opts[:escape]
@async_options = opts[:async_options]
@return_body = opts[:return_body]
@return_url = opts[:return_url]
end
def generate_signature
params = {:scope => @scope, :deadline => Time.now.to_i + @expires_in}
params[:callbackUrl] = @callback_url if !@callback_url.nil? && !@callback_url.empty?
params[:callbackBody] = @callback_body if !@callback_body.nil? && !@callback_body.empty?
params[:callbackBodyType] = @callback_body_type if !@callback_body_type.nil? && !@callback_body_type.empty?
params[:customer] = @customer if !@customer.nil? && !@customer.empty?
params[:escape] = 1 if @escape == 1 || @escape == true
params[:asyncOps] = @async_options if !@async_options.nil? && !@async_options.empty?
params[:returnBody] = @return_body if !@return_body.nil? && !@return_body.empty?
params[:returnUrl] = @return_url if !@return_url.nil? && !@return_url.empty?
Utils.urlsafe_base64_encode(params.to_json)
end
def generate_token
signature = generate_signature
encoded_digest = generate_encoded_digest(signature)
%Q(#{@access_key}:#{encoded_digest}:#{signature})
end
end # module UploadToken
end # module Qiniu
+125
View File
@@ -0,0 +1,125 @@
# -*- encoding: utf-8 -*-
# vim: sw=2 ts=2
module Qiniu
module Storage
class << self
include Utils
def upload_with_token(uptoken,
local_file,
bucket,
key = nil,
mime_type = nil,
custom_meta = nil,
callback_params = nil,
enable_crc32_check = false,
rotate = nil)
action_params = _generate_action_params(
local_file,
bucket,
key,
mime_type,
custom_meta,
enable_crc32_check,
rotate
)
if callback_params.nil?
callback_params = {:bucket => bucket, :key => key, :mime_type => mime_type}
end
callback_query_string = HTTP.generate_query_string(callback_params)
url = Config.settings[:up_host] + '/upload'
post_data = {
:params => callback_query_string,
:action => action_params,
:file => File.new(local_file, 'rb'),
:multipart => true
}
if !uptoken.nil? then
post_data[:auth] = uptoken unless uptoken.nil?
end
return HTTP.api_post(url, post_data)
end # upload_with_token
def upload_with_token_2(uptoken,
local_file,
key = nil,
x_vars = nil)
### 构造URL
url = Config.settings[:up_host]
url[/\/*$/] = ''
url += '/'
### 构造HTTP Body
post_data = {
:file => File.new(local_file, 'rb'),
:multipart => true,
}
if not uptoken.nil?
post_data[:token] = uptoken
end
if not key.nil?
post_data[:key] = key
end
if x_vars.is_a?(Hash)
post_data.merge!(x_vars)
end
### 发送请求
HTTP.api_post(url, post_data)
end # upload_with_token_2
### 授权举例
# put_policy.bucket | put_policy.key | key | 语义 | 授权
# :---------------- | :------------- | :------ | :--- | :---
# trivial_bucket | <nil> | <nil> | 新增 | 允许,最终key为1)使用put_policy.save_key生成的值或2)资源内容的Hash值
# trivial_bucket | <nil> | foo.txt | 新增 | 允许
# trivial_bucket | <nil> | bar.jpg | 新增 | 允许
# trivial_bucket | foo.txt | <nil> | 覆盖 | 允许,由SDK将put_policy.key赋值给key实现
# trivial_bucket | foo.txt | foo.txt | 覆盖 | 允许
# trivial_bucket | foo.txt | bar.jpg | 覆盖 | 禁止,put_policy.key与key不一致
def upload_with_put_policy(put_policy,
local_file,
key = nil,
x_vars = nil)
uptoken = Auth.generate_uptoken(put_policy)
if key.nil? then
key = put_policy.key
end
return upload_with_token_2(uptoken, local_file, key, x_vars)
end # upload_with_put_policy
private
def _generate_action_params(local_file,
bucket,
key = nil,
mime_type = nil,
custom_meta = nil,
enable_crc32_check = false,
rotate = nil)
raise NoSuchFileError, local_file unless File.exist?(local_file)
if key.nil?
key = Digest::SHA1.hexdigest(local_file + Time.now.to_s)
end
entry_uri = bucket + ':' + key
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
action_params += '/rotate/' + rotate if !rotate.nil? && rotate.to_i >= 0
action_params
end # _generate_action_params
end # class << self
end # module Storage
end # module Qiniu
+34 -49
View File
@@ -1,13 +1,15 @@
# -*- encoding: utf-8 -*-
require 'uri'
require 'cgi'
require 'json'
require 'zlib'
require 'base64'
require 'rest_client'
require 'qiniu/rs/exceptions'
require 'hmac-sha1'
require 'qiniu/exceptions'
module Qiniu
module RS
module Utils extend self
def urlsafe_base64_encode content
@@ -29,6 +31,13 @@ module Qiniu
{}
end
def debug(msg)
if Config.settings[:enable_debug]
Log.logger.debug(msg)
end
end
### 已过时,仅作为兼容接口保留
def send_request_with url, data = nil, options = {}
options[:method] = Config.settings[:method] unless options[:method]
options[:content_type] = Config.settings[:content_type] unless options[:content_type]
@@ -36,25 +45,35 @@ module Qiniu
:accept => :json,
:user_agent => Config.settings[:user_agent]
}
header_options.merge!('Authorization' => "Bearer #{options[:access_token]}") if options[:access_token]
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?
case options[:method]
when :get
response = RestClient.get url, header_options
response = RestClient.get(url, header_options)
when :post
header_options.merge!(:content_type => options[:content_type])
response = RestClient.post url, data, header_options
response = RestClient.post(url, data, header_options)
end
code = response.respond_to?(:code) ? response.code.to_i : 0
if code != 200
raise RequestFailed.new(response)
unless HTTP.is_response_ok?(code)
raise RequestFailed.new("Request Failed", response)
else
data = {}
body = response.respond_to?(:body) ? response.body : {}
raw_headers = response.respond_to?(:raw_headers) ? response.raw_headers : {}
data = safe_json_parse(body) unless body.empty?
end
[code, data]
end
[code, data, raw_headers]
end # send_request_with
### 已过时,仅作为兼容接口保留
def http_request url, data = nil, options = {}
retry_times = 0
begin
@@ -75,50 +94,16 @@ module Qiniu
res = e.response
code = res.code.to_i if res.respond_to? :code
body = res.respond_to?(:body) ? res.body : ""
raw_headers = res.respond_to?(:raw_headers) ? res.raw_headers : {}
data = safe_json_parse(body) unless body.empty?
end
[code, data]
[code, data, raw_headers]
end
end
def upload_multipart_data(url, filepath, action_string, callback_query_string = '')
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
}
response = RestClient.post url, post_data, header_options
body = response.respond_to?(:body) ? response.body : ""
data = safe_json_parse(body) unless body.empty?
code = response.code.to_i if response.respond_to?(:code)
rescue Errno::ECONNRESET => err
Log.logger.error err
rescue => e
Log.logger.warn "#{e.message} => Utils.http_request('#{url}')"
res = e.response
if e.respond_to? :response
res = e.response
code = res.code.to_i if res.respond_to? :code
body = res.respond_to?(:body) ? res.body : ""
data = safe_json_parse(body) unless body.empty?
end
end
[code, data]
def crc32checksum(filepath)
File.open(filepath, "rb") { |f| Zlib.crc32 f.read }
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 }
URI.escape(total_param.join("&"))
end
end
end
end
end # module Utils
end # module Qiniu
+17
View File
@@ -0,0 +1,17 @@
# -*- encoding: utf-8 -*-
module Qiniu
module Version
MAJOR = 6
MINOR = 2
PATCH = 1
# Returns a version string by joining <tt>MAJOR</tt>, <tt>MINOR</tt>, and <tt>PATCH</tt> with <tt>'.'</tt>
#
# Example
#
# Version.to_s # '1.0.2'
def self.to_s
[MAJOR, MINOR, PATCH].join('.')
end
end # Version
end # module Qiniu
-26
View File
@@ -1,26 +0,0 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../lib/qiniu/rs/version', __FILE__)
Gem::Specification.new do |gem|
gem.authors = ["why404"]
gem.email = ["why404@gmail.com"]
gem.description = %q{Qiniu Cloud Storage SDK for Ruby. See: http://docs.qiniutek.com/v1/sdk/ruby/}
gem.summary = %q{Qiniu Cloud Storage SDK for Ruby}
gem.homepage = "https://github.com/why404/qiniu-rs"
gem.files = `git ls-files`.split($\)
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
gem.name = "qiniu-rs"
gem.require_paths = ["lib"]
gem.version = Qiniu::RS::VERSION
# specify any dependencies here; for example:
gem.add_development_dependency "rake"
gem.add_development_dependency "rspec"
gem.add_development_dependency "fakeweb"
gem.add_runtime_dependency "json"
gem.add_runtime_dependency "rest-client"
gem.add_runtime_dependency "mime-types"
end
Executable
+29
View File
@@ -0,0 +1,29 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../lib/qiniu/version', __FILE__)
Gem::Specification.new do |gem|
gem.authors = ["why404","BluntBlade"]
gem.email = ["sdk@qiniu.com"]
gem.description = %q{Qiniu Resource (Cloud) Storage SDK for Ruby. See: http://developer.qiniu.com/docs/v6/sdk/ruby-sdk.html}
gem.summary = %q{Qiniu Resource (Cloud) Storage SDK for Ruby}
gem.homepage = "https://github.com/qiniu/ruby-sdk"
gem.files = `git ls-files`.split($\)
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
gem.name = "qiniu"
gem.require_paths = ["lib"]
gem.version = Qiniu::Version.to_s
gem.license = "MIT"
# specify any dependencies here; for example:
gem.add_development_dependency "rake", ">= 0.9"
gem.add_development_dependency "rspec", ">= 2.11"
gem.add_development_dependency "fakeweb", "~> 1.3"
gem.add_runtime_dependency "json", "~> 1.7"
gem.add_runtime_dependency "rest-client", "~> 1.6"
gem.add_runtime_dependency "mime-types", "~> 1.19"
gem.add_runtime_dependency "ruby-hmac", "~> 0.4"
gem.add_runtime_dependency "jruby-openssl", "~> 0.7" if RUBY_PLATFORM == "java"
end
+30
View File
@@ -0,0 +1,30 @@
# -*- encoding: utf-8 -*-
require 'spec_helper'
require 'qiniu/abstract'
describe Qiniu::Abstract do
before(:each) do
@klass = Class.new do
include Qiniu::Abstract
abstract_methods :foo, :bar
end
end
it "raises NotImplementedError" do
proc {
@klass.new.foo
}.should raise_error(NotImplementedError)
end
it "can be overridden" do
subclass = Class.new(@klass) do
def foo
:overridden
end
end
subclass.new.foo.should == :overridden
end
end
+75
View File
@@ -0,0 +1,75 @@
# -*- encoding: utf-8 -*-
require 'spec_helper'
require 'qiniu/auth'
require 'qiniu/storage'
require 'digest/sha1'
module Qiniu
module Auth
describe Auth do
before :all do
@bucket = 'RubySDK-Test-Private'
@bucket = make_unique_bucket(@bucket)
### 尝试创建Bucket
result = Qiniu::Storage.make_a_private_bucket(@bucket)
puts result.inspect
end
after :all do
### 不删除Bucket以备下次使用
end
### 测试私有资源下载
context ".download_private_file" do
it "should works" do
### 生成Key
key = 'a_private_file'
key = make_unique_key_in_bucket(key)
puts "key=#{key}"
### 上传测试文件
pp = Auth::PutPolicy.new(@bucket, key)
code, data, raw_headers = Qiniu::Storage.upload_with_put_policy(
pp,
__FILE__
)
code.should == 200
puts data.inspect
puts raw_headers.inspect
### 获取下载地址
code, data = Qiniu::Storage.get(@bucket, key)
code.should == 200
puts data.inspect
url = data['url']
### 授权下载地址(不带参数)
download_url = Qiniu::Auth.authorize_download_url(url)
puts "download_url=#{download_url}"
result = RestClient.get(download_url)
result.code.should == 200
result.body.should_not be_empty
### 授权下载地址(带参数)
download_url = Qiniu::Auth.authorize_download_url(url + '?download/a.m3u8')
puts "download_url=#{download_url}"
result = RestClient.get(download_url)
result.code.should == 200
result.body.should_not be_empty
### 删除文件
code, data = Qiniu::Storage.delete(@bucket, key)
code.should == 200
puts data.inspect
end
end
end
end # module Storage
end # module Qiniu
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

+89
View File
@@ -0,0 +1,89 @@
# -*- encoding: utf-8 -*-
require 'spec_helper'
require 'qiniu/auth'
require 'qiniu'
require 'qiniu/fop'
module Qiniu
module Fop
describe Fop do
before :all do
### 复用RubySDK-Test-Storage空间
@bucket = 'RubySDK-Test-Storage'
@bucket = make_unique_bucket(@bucket)
### 尝试创建空间
result = Qiniu.mkbucket(@bucket)
puts result.inspect
pic_fname = "image_logo_for_test.png"
@key = make_unique_key_in_bucket(pic_fname)
local_file = File.expand_path('../' + pic_fname, __FILE__)
upopts = {
:scope => @bucket,
:expires_in => 3600,
:customer => "why404@gmail.com",
:async_options => "imageView/1/w/120/h/120",
:return_body => '{"size":$(fsize), "hash":$(etag), "width":$(imageInfo.width), "height":$(imageInfo.height)}'
}
uptoken = Qiniu.generate_upload_token(upopts)
data = Qiniu.upload_file :uptoken => uptoken, :file => local_file, :bucket => @bucket, :key => @key
puts data.inspect
data["size"].should_not be_zero
data["hash"].should_not be_empty
data["width"].should_not be_zero
data["height"].should_not be_zero
result = Qiniu.get(@bucket, @key)
result["url"].should_not be_empty
puts result.inspect
@source_image_url = result["url"]
@mogrify_options = {
:thumbnail => "!120x120>",
:gravity => "center",
:crop => "!120x120a0a0",
:quality => 85,
:rotate => 45,
:format => "jpg",
:auto_orient => true
}
end
after :all do
### 不删除Bucket以备下次使用
end
context ".info" do
it "should works" do
code, data = Qiniu::Fop::Image.info(@source_image_url)
code.should == 200
puts data.inspect
end
end
=begin
context ".exif" do
it "should works" do
code, data = Qiniu::Fop::Image.exif(@source_image_url)
puts data.inspect
end
end
=end
context ".mogrify_preview_url" do
it "should works" do
mogrify_preview_url = Qiniu::Fop::Image.mogrify_preview_url(@source_image_url, @mogrify_options)
puts mogrify_preview_url.inspect
end
end
end
end # module Fop
end # module Qiniu
+156
View File
@@ -0,0 +1,156 @@
# -*- encoding: utf-8 -*-
# vim: sw=2 ts=2
require 'digest/sha1'
require 'spec_helper'
require 'qiniu/auth'
require 'qiniu/management'
require 'qiniu'
module Qiniu
module Storage
describe Storage do
before :all do
@bucket = 'RubySDK-Test-Management'
@bucket = make_unique_bucket(@bucket)
### 尝试创建Bucket
result = Qiniu.mkbucket(@bucket)
puts result.inspect
@key = Digest::SHA1.hexdigest((Time.now.to_i+rand(100)).to_s)
@key = make_unique_key_in_bucket(@key)
@key2 = @key + rand(100).to_s
end
after :all do
### 不删除Bucket以备下次使用
end
### 准备数据
context ".prepare_file" do
it "should works" do
pp = Auth::PutPolicy.new(@bucket, @key)
code, data, raw_headers = Qiniu::Storage.upload_with_put_policy(
pp,
__FILE__
)
code.should == 200
puts data.inspect
end
end
### 列举Bucket
context ".buckets" do
it "should works" do
code, data = Storage.buckets
code.should == 200
puts data.inspect
end
end
context ".stat" do
it "should works" do
code, data = Storage.stat(@bucket, @key)
code.should == 200
puts data.inspect
end
end
context ".get" do
it "should works" do
code, data = Storage.get(@bucket, @key)
code.should == 200
puts data.inspect
end
end
context ".batch" do
it "should works" do
code, data = Storage.batch("stat", @bucket, [@key])
code.should == 200
puts data.inspect
end
end
context ".batch_stat" do
it "should works" do
code, data = Storage.batch_stat(@bucket, [@key])
code.should == 200
puts data.inspect
end
end
context ".batch_get" do
it "should works" do
code, data = Storage.batch_get(@bucket, [@key])
code.should == 200
puts data.inspect
end
end
context ".batch_copy" do
it "should works" do
code, data = Storage.batch_copy @bucket, @key, @bucket, @key2
code.should == 200
puts data.inspect
code, data = Storage.delete @bucket, @key2
code.should == 200
puts data.inspect
end
end
context ".batch_move" do
it "should works" do
code, data = Storage.batch_move @bucket, @key, @bucket, @key2
code.should == 200
puts data.inspect
code3, data3 = Storage.batch_move @bucket, @key2, @bucket, @key
code3.should == 200
puts data3.inspect
end
end
context ".move" do
it "should works" do
code, data = Storage.move(@bucket, @key, @bucket, @key2)
code.should == 200
puts data.inspect
code2, data2 = Storage.stat(@bucket, @key2)
code2.should == 200
puts data2.inspect
code3, data3 = Storage.move(@bucket, @key2, @bucket, @key)
code3.should == 200
puts data3.inspect
end
end
context ".copy" do
it "should works" do
code, data = Storage.copy(@bucket, @key, @bucket, @key2)
code.should == 200
puts data.inspect
code, data = Storage.delete(@bucket, @key2)
code.should == 200
puts data.inspect
end
end
context ".delete" do
it "should works" do
code, data = Storage.delete(@bucket, @key)
code.should == 200
puts data.inspect
end
end
end
end # module Storage
end # module Qiniu
+59
View File
@@ -0,0 +1,59 @@
# -*- encoding: utf-8 -*-
require 'spec_helper'
require 'qiniu/auth'
require 'qiniu'
require 'qiniu/misc'
module Qiniu
module Misc
describe Misc do
before :all do
### 复用RubySDK-Test-Management空间
@bucket = 'RubySDK-Test-Management'
@bucket = make_unique_bucket(@bucket)
result = Qiniu.mkbucket(@bucket)
puts result.inspect
end
after :all do
### 不删除Bucket以备下次使用
end
context ".set_protected" do
it "should works" do
code, data = Qiniu::Misc.set_protected(@bucket, 1)
code.should == 200
puts data.inspect
end
end
context ".set_separator" do
it "should works" do
code, data = Qiniu::Misc.set_separator(@bucket, "-")
code.should == 200
puts data.inspect
end
end
context ".set_style" do
it "should works" do
code, data = Qiniu::Misc.set_style(@bucket, "small.jpg", "imageMogr/auto-orient/thumbnail/!120x120r/gravity/center/crop/!120x120/quality/80")
code.should == 200
puts data.inspect
end
end
context ".unset_style" do
it "should works" do
code, data = Qiniu::Misc.unset_style(@bucket, "small.jpg")
code.should == 200
puts data.inspect
end
end
end
end # module Misc
end # module Qiniu
+89
View File
@@ -0,0 +1,89 @@
# -*- encoding: utf-8 -*-
# vim: sw=2 ts=2
require 'spec_helper'
require 'qiniu/auth'
require 'qiniu'
require 'qiniu/fop'
module Qiniu
module Fop
module Persistance
describe Persistance do
before :all do
### 复用RubySDK-Test-Storage空间
@bucket = 'RubySDK-Test-Storage'
@bucket = make_unique_bucket(@bucket)
### 尝试创建空间
code, data, headers = Qiniu::Storage.make_a_public_bucket(@bucket)
puts code.inspect
puts data.inspect
puts headers.inspect
pic_fname = "image_logo_for_test.png"
@key = make_unique_key_in_bucket(pic_fname)
local_file = File.expand_path('../' + pic_fname, __FILE__)
### 检查测试文件存在性
code, body, headers = Qiniu::Storage.stat(@bucket, @key)
if code == 404 || code == 612 then
# 文件不存在,尝试上传
pp = Qiniu::Auth.PutPolicy.new(@bucket, @key)
code, body, headers = Qiniu::Storage.upload_with_put_policy(
pp,
local_file
)
puts "Put a test file for Persistance cases"
puts code.inspect
puts body.inspect
puts header.inspect
end
end
after :all do
### 不删除Bucket以备下次使用
end
context ".pfop" do
it "should works" do
pp = Persistance::PfopPolicy.new(
@bucket,
@key,
'imageView2/1/w/80/h/80', # fops
'www.baidu.com' # notify_url
)
code, data, headers = Qiniu::Fop::Persistance.pfop(pp)
code.should == 200
puts data.inspect
end
end
context ".prefop" do
it "should works" do
code, data, headers = Qiniu::Fop::Persistance.prefop('fakePersistentId')
code.should == 612
puts code.inspect
puts data.inspect
puts headers.inspect
end
end
context ".p1" do
it "should works" do
url = 'http://fake.qiniudn.com/fake.jpg'
fop = 'imageView2/1/w/80/h/80'
target_url = "#{url}?p/1/#{CGI.escape(fop).gsub('+', '%20')}"
p1_url = Qiniu::Fop::Persistance.generate_p1_url(url, fop)
p1_url.should == target_url
puts p1_url.inspect
end
end
end
end # module Persistance
end # module Fop
end # module Qiniu
+329
View File
@@ -0,0 +1,329 @@
# -*- encoding: utf-8 -*-
require 'digest/sha1'
require 'spec_helper'
require 'qiniu'
require 'qiniu/exceptions'
module Qiniu
describe Qiniu do
before :all do
@bucket = 'RubySDK-Test'
@bucket = make_unique_bucket(@bucket)
@test_image_bucket = @bucket
### 尝试创建Bucket
result = Qiniu.mkbucket(@bucket)
puts result.inspect
@key = Digest::SHA1.hexdigest Time.now.to_s
@key = make_unique_key_in_bucket(@key)
@key2 = @key + rand(100).to_s
#@domain = @bucket + '.dn.qbox.me'
@test_image_key = 'image_logo_for_test.png'
local_file = File.expand_path('./' + @test_image_key, File.dirname(__FILE__))
puts local_file.inspect
upopts = {:scope => @test_image_bucket, :expires_in => 3600, :customer => "why404@gmail.com"}
uptoken = Qiniu.generate_upload_token(upopts)
data = Qiniu.upload_file :uptoken => uptoken, :file => local_file, :bucket => @test_image_bucket, :key => @test_image_key
puts data.inspect
end
after :all do
### 不删除Bucket以备下次使用
end
context ".buckets" do
it "should works" do
result = Qiniu.buckets
result.should_not be_false
puts result.inspect
end
end
context ".set_protected" do
it "should works" do
result = Qiniu.set_protected(@bucket, 1)
result.should_not be_false
puts result.inspect
end
end
context ".set_separator" do
it "should works" do
result = Qiniu.set_separator(@bucket, "-")
result.should_not be_false
puts result.inspect
end
end
context ".set_style" do
it "should works" do
result = Qiniu.set_style(@bucket, "small.jpg", "imageMogr/auto-orient/thumbnail/!120x120r/gravity/center/crop/!120x120/quality/80")
result.should_not be_false
puts result.inspect
end
end
context ".unset_style" do
it "should works" do
result = Qiniu.unset_style(@bucket, "small.jpg")
result.should_not be_false
puts result.inspect
end
end
context ".upload_file" do
it "should works" do
uptoken_opts = {:scope => @bucket, :escape => 0}
upload_opts = {
:uptoken => Qiniu.generate_upload_token(uptoken_opts),
:file => __FILE__,
:bucket => @bucket,
:key => @key,
:enable_crc32_check => true
}
result = Qiniu.upload_file(upload_opts)
result.should_not be_false
puts result.inspect
end
it "should raise MissingArgsError" do
uptoken_opts = {:scope => @bucket, :escape => 0}
upload_opts = {
:uptoken => Qiniu.generate_upload_token(uptoken_opts),
:file => __FILE__,
:key => @key,
:enable_crc32_check => true
}
lambda { Qiniu.upload_file(upload_opts) }.should raise_error(MissingArgsError)
end
it "should raise NoSuchFileError" do
uptoken_opts = {:scope => @bucket, :escape => 0}
upload_opts = {
:uptoken => Qiniu.generate_upload_token(uptoken_opts),
:file => 'no_this_file',
:bucket => @bucket,
:key => @key,
:enable_crc32_check => true
}
lambda { Qiniu.upload_file(upload_opts) }.should raise_error(NoSuchFileError)
end
end
context ".resumable_upload_file" do
it "should works" do
# generate bigfile for testing
localfile = "test_bigfile"
File.open(localfile, "w"){|f| 5242888.times{f.write(rand(9).to_s)}}
key = Digest::SHA1.hexdigest(localfile+Time.now.to_s)
# generate the upload token
uptoken_opts = {:scope => @bucket, :expires_in => 3600, :customer => "why404@gmail.com", :escape => 0}
uptoken = Qiniu.generate_upload_token(uptoken_opts)
# uploading
upload_opts = {
:uptoken => uptoken,
:file => localfile,
:bucket => @bucket,
:key => key
}
#uploading
result1 = Qiniu.upload_file(upload_opts)
#drop the bigfile
File.unlink(localfile) if File.exists?(localfile)
#expect
puts result1.inspect
result1.should_not be_false
result1.should_not be_empty
#stat
result2 = Qiniu.stat(@bucket, key)
puts result2.inspect
result2.should_not be_false
#delete
result3 = Qiniu.delete(@bucket, key)
puts result3.inspect
result3.should_not be_false
end
end
context ".stat" do
it "should works" do
result = Qiniu.stat(@bucket, @key)
result.should_not be_false
result.should_not be_empty
puts result.inspect
end
end
context ".get" do
it "should works" do
result = Qiniu.get(@bucket, @key, "qiniu_spec.rb", 10)
result.should_not be_false
result.should_not be_empty
puts result.inspect
end
end
context ".download" do
it "should works" do
result = Qiniu.download(@bucket, @key, "rs_spec.rb", 10)
result.should_not be_false
result.should_not be_empty
puts result.inspect
end
end
context ".batch" do
it "should works" do
result = Qiniu.batch("stat", @bucket, [@key])
result.should_not be_false
result.should_not be_empty
puts result.inspect
end
end
context ".batch_stat" do
it "should works" do
result = Qiniu.batch_stat(@bucket, [@key])
result.should_not be_false
result.should_not be_empty
puts result.inspect
end
end
context ".batch_get" do
it "should works" do
result = Qiniu.batch_get(@bucket, [@key])
result.should_not be_false
result.should_not be_empty
puts result.inspect
end
end
context ".batch_download" do
it "should works" do
result = Qiniu.batch_download(@bucket, [@key])
result.should_not be_false
result.should_not be_empty
puts result.inspect
end
end
=begin
context ".batch_copy" do
it "should works" do
result = Qiniu.batch_copy [@bucket, @key, @bucket, @key2]
result.should_not be_false
#result2 = Qiniu.stat(@bucket, @key2)
#result2.should_not be_false
end
end
context ".batch_move" do
it "should works" do
result = Qiniu.batch_move [@bucket, @key, @bucket, @key2]
result.should_not be_false
#result2 = Qiniu.stat(@bucket, @key2)
#result2.should_not be_false
result3 = Qiniu.batch_move [@bucket, @key2, @bucket, @key]
result3.should_not be_false
end
end
=end
context ".move" do
it "should works" do
result = Qiniu.move(@bucket, @key, @bucket, @key2)
result.should_not be_false
result2 = Qiniu.stat(@bucket, @key2)
result2.should_not be_false
result3 = Qiniu.move(@bucket, @key2, @bucket, @key)
result3.should_not be_false
end
end
context ".copy" do
it "should works" do
result = Qiniu.copy(@bucket, @key, @bucket, @key2)
result.should_not be_false
result3 = Qiniu.delete(@bucket, @key2)
result3.should_not be_false
end
end
context ".delete" do
it "should works" do
result = Qiniu.delete(@bucket, @key)
result.should_not be_false
end
end
context ".image_info" do
it "should works" do
data = Qiniu.get(@test_image_bucket, @test_image_key)
data.should_not be_false
data.should_not be_empty
puts data.inspect
result = Qiniu.image_info(data["url"])
result.should_not be_false
result.should_not be_empty
puts result.inspect
end
end
context ".image_mogrify_save_as" do
it "should works" do
data = Qiniu.get(@test_image_bucket, @test_image_key)
data.should_not be_false
data.should_not be_empty
puts data.inspect
dest_key = "cropped-" + @test_image_key
src_img_url = data["url"]
mogrify_options = {
:thumbnail => "!120x120>",
:gravity => "center",
:crop => "!120x120a0a0",
:quality => 85,
:rotate => 45,
:format => "jpg",
:auto_orient => true
}
result2 = Qiniu.image_mogrify_save_as(@test_image_bucket, dest_key, src_img_url, mogrify_options)
result2.should_not be_false
result2.should_not be_empty
puts result2.inspect
end
end
context ".generate_upload_token" do
it "should works" do
data = Qiniu.generate_upload_token({:scope => @bucket, :expires_in => 3600, :escape => 0})
data.should_not be_empty
puts data.inspect
data.split(":").length.should == 3
end
end
context ".generate_download_token" do
it "should works" do
data = Qiniu.generate_download_token({:expires_in => 1, :pattern => 'http://*.dn.qbox.me/*'})
data.should_not be_empty
puts data.inspect
data.split(":").length.should == 3
end
end
end
end # module Qiniu
-58
View File
@@ -1,58 +0,0 @@
# -*- encoding: utf-8 -*-
require 'spec_helper'
require 'qiniu/rs/auth'
module Qiniu
module RS
describe Auth do
before :all do
@username = "test@qbox.net"
@password = "test"
code, data = Qiniu::RS::Auth.exchange_by_password!(@username, @password)
code.should == 200
data.should be_an_instance_of(Hash)
data["access_token"].should_not be_empty
data["refresh_token"].should_not be_empty
data["refresh_token"].should_not be_empty
@access_token = data["access_token"]
@refresh_token = data["refresh_token"]
puts data.inspect
end
context ".exchange_by_password" do
it "should sign in failed when pass a non-existent username" do
code, data = Qiniu::RS::Auth.exchange_by_password!("a_non_existent_user@example.com", "password")
code.should == 401
data["error_code"].should == 11
data["error"].should == "failed_authentication"
puts data.inspect
end
it "should sign in failed when pass a wrong password" do
code, data = Qiniu::RS::Auth.exchange_by_password!(@username, "a-wrong-password")
code.should == 401
data["error_code"].should == 11
data["error"].should == "failed_authentication"
puts data.inspect
end
end
context ".exchange_by_refresh_token" do
it "should works" do
@refresh_token.should_not be_empty
code, data = Qiniu::RS::Auth.exchange_by_refresh_token!(@refresh_token)
code.should == 200
data["access_token"].should_not be_empty
data["refresh_token"].should_not be_empty
data["expires_in"].should_not be_zero
puts data.inspect
end
end
end
end
end
-41
View File
@@ -1,41 +0,0 @@
# -*- encoding: utf-8 -*-
require 'spec_helper'
require 'qiniu/rs/auth'
require 'qiniu/rs/rs'
require 'qiniu/rs/image'
module Qiniu
module RS
describe Image do
before :all do
code, data = Qiniu::RS::Auth.exchange_by_password!("test@qbox.net", "test")
code.should == 200
data.should be_an_instance_of(Hash)
data["access_token"].should_not be_empty
data["refresh_token"].should_not be_empty
data["refresh_token"].should_not be_empty
puts data.inspect
@bucket = "test_images"
@key = "test_image.jpg"
code2, data2 = Qiniu::RS::RS.get(@bucket, @key)
code2.should == 200
data2["url"].should_not be_empty
puts data2.inspect
@download_url = data2["url"]
end
context ".info" do
it "should works" do
code, data = Qiniu::RS::Image.info(@download_url)
code.should == 200
puts data.inspect
end
end
end
end
end
-42
View File
@@ -1,42 +0,0 @@
# -*- encoding: utf-8 -*-
require 'spec_helper'
require 'qiniu/rs/auth'
require 'qiniu/rs/io'
require 'digest/sha1'
module Qiniu
module RS
describe IO do
before :all do
code, data = Qiniu::RS::Auth.exchange_by_password!("test@qbox.net", "test")
code.should == 200
data.should be_an_instance_of(Hash)
data["access_token"].should_not be_empty
data["refresh_token"].should_not be_empty
data["refresh_token"].should_not be_empty
puts data.inspect
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 ".put_file" do
it "should works" do
code, data = Qiniu::RS::IO.put_file(@put_url, __FILE__, @bucket, @key)
code.should == 200
puts data.inspect
end
end
end
end
end
-116
View File
@@ -1,116 +0,0 @@
# -*- encoding: utf-8 -*-
require 'digest/sha1'
require 'spec_helper'
require 'qiniu/rs/auth'
require 'qiniu/rs/io'
require 'qiniu/rs/rs'
module Qiniu
module RS
describe RS do
before :all do
code, data = Qiniu::RS::Auth.exchange_by_password!("test@qbox.net", "test")
code.should == 200
data.should be_an_instance_of(Hash)
data["access_token"].should_not be_empty
data["refresh_token"].should_not be_empty
data["refresh_token"].should_not be_empty
puts data.inspect
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
@domain = 'cdn.example.com'
end
context "IO.put_file" do
it "should works" do
code, data = Qiniu::RS::IO.put_file(@put_url, __FILE__, @bucket, @key)
code.should == 200
puts data.inspect
end
end
context ".stat" do
it "should works" do
code, data = Qiniu::RS::RS.stat(@bucket, @key)
code.should == 200
puts data.inspect
end
end
context ".get" do
it "should works" do
code, data = Qiniu::RS::RS.get(@bucket, @key, "rs_spec.rb", 1)
code.should == 200
puts data.inspect
end
end
context ".batch" do
it "should works" do
code, data = Qiniu::RS::RS.batch("stat", @bucket, [@key])
code.should == 200
puts data.inspect
end
end
context ".batch_stat" do
it "should works" do
code, data = Qiniu::RS::RS.batch_stat(@bucket, [@key])
code.should == 200
puts data.inspect
end
end
context ".batch_get" do
it "should works" do
code, data = Qiniu::RS::RS.batch_get(@bucket, [@key])
code.should == 200
puts data.inspect
end
end
context ".publish" do
it "should works" do
code, data = Qiniu::RS::RS.publish(@domain, @bucket)
code.should == 200
puts data.inspect
end
end
context ".unpublish" do
it "should works" do
code, data = Qiniu::RS::RS.unpublish(@domain)
code.should == 200
puts data.inspect
end
end
context ".delete" do
it "should works" do
code, data = Qiniu::RS::RS.delete(@bucket, @key)
code.should == 200
puts data.inspect
end
end
context ".drop" do
it "should works" do
code, data = Qiniu::RS::RS.drop(@bucket)
code.should == 200
puts data.inspect
end
end
end
end
end
-10
View File
@@ -1,10 +0,0 @@
# -*- encoding: utf-8 -*-
require 'spec_helper'
require 'qiniu/rs/version'
describe Qiniu::RS do
it "should has a VERSION" do
Qiniu::RS::VERSION.should =~ /^\d+\.\d+\.\d+?$/
end
end
-147
View File
@@ -1,147 +0,0 @@
# -*- encoding: utf-8 -*-
require 'spec_helper'
require 'qiniu/rs'
module Qiniu
describe RS do
before :all do
@bucket = 'qiniu_rs_test'
@key = Digest::SHA1.hexdigest Time.now.to_s
@domain = 'cdn.example.com'
end
context ".login!" do
it "should works" do
result = Qiniu::RS.login!("test@qbox.net", "test")
result.should be_true
end
end
context ".put_auth" do
it "should works" do
result = Qiniu::RS.put_auth(10)
result.should_not be_false
result.should_not be_empty
puts result.inspect
end
end
context ".upload" do
it "should works" do
put_url = Qiniu::RS.put_auth(10)
put_url.should_not be_false
put_url.should_not be_empty
puts put_url.inspect
result = Qiniu::RS.upload(put_url, __FILE__, @bucket, @key)
result.should be_true
end
end
context ".stat" do
it "should works" do
result = Qiniu::RS.stat(@bucket, @key)
result.should_not be_false
result.should_not be_empty
puts result.inspect
end
end
context ".get" do
it "should works" do
result = Qiniu::RS.get(@bucket, @key, "rs_spec.rb", 10)
result.should_not be_false
result.should_not be_empty
puts result.inspect
end
end
context ".download" do
it "should works" do
result = Qiniu::RS.download(@bucket, @key, "rs_spec.rb", 10)
result.should_not be_false
result.should_not be_empty
puts result.inspect
end
end
context ".batch" do
it "should works" do
result = Qiniu::RS.batch("stat", @bucket, [@key])
result.should_not be_false
result.should_not be_empty
puts result.inspect
end
end
context ".batch_stat" do
it "should works" do
result = Qiniu::RS.batch_stat(@bucket, [@key])
result.should_not be_false
result.should_not be_empty
puts result.inspect
end
end
context ".batch_get" do
it "should works" do
result = Qiniu::RS.batch_get(@bucket, [@key])
result.should_not be_false
result.should_not be_empty
puts result.inspect
end
end
context ".batch_download" do
it "should works" do
result = Qiniu::RS.batch_download(@bucket, [@key])
result.should_not be_false
result.should_not be_empty
puts result.inspect
end
end
context ".publish" do
it "should works" do
result = Qiniu::RS.publish(@domain, @bucket)
result.should_not be_false
end
end
context ".unpublish" do
it "should works" do
result = Qiniu::RS.unpublish(@domain)
result.should_not be_false
end
end
context ".delete" do
it "should works" do
result = Qiniu::RS.delete(@bucket, @key)
result.should_not be_false
end
end
context ".drop" do
it "should works" do
result = Qiniu::RS.drop(@bucket)
result.should_not be_false
end
end
context ".image_info" do
it "should works" do
data = Qiniu::RS.get("test_images", "test_image.jpg")
data.should_not be_false
data.should_not be_empty
puts data.inspect
result = Qiniu::RS.image_info(data["url"])
result.should_not be_false
result.should_not be_empty
puts result.inspect
end
end
end
end
+29
View File
@@ -0,0 +1,29 @@
# -*- encoding: utf-8 -*-
require 'spec_helper'
require 'qiniu/tokens/qbox_token'
module Qiniu
module RS
describe QboxToken do
before :all do
@qbox_token = QboxToken.new(:url => 'www.qiniutek.com?key1=value1',
:params => { :key2 => 'value2' })
@qbox_token.access_key = 'access_key'
@qbox_token.secret_key = 'secret_key'
end
context "#generate_token" do
it "should generate token" do
@qbox_token.generate_token.should_not be_empty
end
end
context "#generate_signature" do
it "should generate signature" do
@qbox_token.generate_signature.should == "www.qiniutek.com?key1=value1\nkey2=value2"
end
end
end
end
end
+308
View File
@@ -0,0 +1,308 @@
# -*- encoding: utf-8 -*-
# vim: sw=2 ts=2
require 'spec_helper'
require 'qiniu/auth'
require 'qiniu/storage'
require 'digest/sha1'
module Qiniu
module Storage
describe Storage do
before :all do
@bucket = 'RubySDK-Test-Storage'
@bucket = make_unique_bucket(@bucket)
### 尝试创建Bucket
result = Qiniu.mkbucket(@bucket)
puts result.inspect
@key = Digest::SHA1.hexdigest((Time.now.to_i+rand(100)).to_s)
@key = make_unique_key_in_bucket(@key)
puts "key=#{@key}"
@localfile_5m = "5M.txt"
File.open(@localfile_5m, "w"){|f| 5242888.times{ f.write(rand(9).to_s) }}
@key_5m = Digest::SHA1.hexdigest(@localfile_5m+Time.now.to_s)
@key_5m = make_unique_key_in_bucket(@key_5m)
puts "key_5m=#{@key_5m}"
@localfile_4m = "4M.txt"
File.open(@localfile_4m, "w"){|f| (1 << 22).times{ f.write(rand(9).to_s) }}
@key_4m = Digest::SHA1.hexdigest(@localfile_4m+Time.now.to_s)
@key_4m = make_unique_key_in_bucket(@key_4m)
puts "key_4m=#{@key_4m}"
@localfile_8m = "8M.txt"
File.open(@localfile_8m, "w"){|f| (1 << 23).times{ f.write(rand(9).to_s) }}
@key_8m = Digest::SHA1.hexdigest(@localfile_8m+Time.now.to_s)
@key_8m = make_unique_key_in_bucket(@key_8m)
puts "key_8m=#{@key_8m}"
@localfile_1m = "1M.txt"
File.open(@localfile_1m, "w"){|f| (1 << 20).times{ f.write(rand(9).to_s) }}
@key_1m = Digest::SHA1.hexdigest(@localfile_1m+Time.now.to_s)
@key_1m = make_unique_key_in_bucket(@key_1m)
puts "key_1m=#{@key_1m}"
end
after :all do
### 清除本地临时文件
File.unlink(@localfile_5m) if File.exists?(@localfile_5m)
File.unlink(@localfile_4m) if File.exists?(@localfile_4m)
File.unlink(@localfile_8m) if File.exists?(@localfile_8m)
File.unlink(@localfile_1m) if File.exists?(@localfile_1m)
### 不删除Bucket以备下次使用
end
### 测试单文件直传
context ".upload_with_token" do
it "should works" do
upopts = {:scope => @bucket, :expires_in => 3600, :customer => "why404@gmail.com"}
uptoken = Qiniu.generate_upload_token(upopts)
code, data, raw_headers = Qiniu::Storage.upload_with_token(
uptoken,
__FILE__,
@bucket,
@key,
nil,
nil,
nil,
true
)
code.should == 200
puts data.inspect
puts raw_headers.inspect
end
end
context ".stat" do
it "should exists" do
code, data = Qiniu::Storage.stat(@bucket, @key)
puts data.inspect
code.should == 200
end
end
context ".delete" do
it "should works" do
code, data = Qiniu::Storage.delete(@bucket, @key)
puts data.inspect
code.should == 200
end
end
context ".upload_with_token_2" do
it "should works" do
upopts = {:scope => @bucket, :expires_in => 3600, :endUser => "why404@gmail.com"}
uptoken = Qiniu.generate_upload_token(upopts)
code, data, raw_headers = Qiniu::Storage.upload_with_token_2(
uptoken,
__FILE__,
@key
)
code.should == 200
puts data.inspect
puts raw_headers.inspect
end
end # .upload_with_token_2
context ".stat" do
it "should exists" do
code, data = Qiniu::Storage.stat(@bucket, @key)
puts data.inspect
code.should == 200
end
end
context ".delete" do
it "should works" do
code, data = Qiniu::Storage.delete(@bucket, @key)
puts data.inspect
code.should == 200
end
end
context ".upload_with_put_policy" do
it "should works" do
pp = Qiniu::Auth::PutPolicy.new(@bucket, @key)
pp.end_user = "why404@gmail.com"
puts 'put_policy=' + pp.to_json
code, data, raw_headers = Qiniu::Storage.upload_with_put_policy(
pp,
__FILE__,
@key + '-not-equal'
)
code.should_not == 200
puts data.inspect
puts raw_headers.inspect
code, data, raw_headers = Qiniu::Storage.upload_with_put_policy(
pp,
__FILE__,
@key
)
code.should == 200
puts data.inspect
puts raw_headers.inspect
end
end # .upload_with_put_policy
context ".stat" do
it "should exists" do
code, data = Qiniu::Storage.stat(@bucket, @key)
puts data.inspect
code.should == 200
end
end
context ".delete" do
it "should works" do
code, data = Qiniu::Storage.delete(@bucket, @key)
puts data.inspect
code.should == 200
end
end
### 测试断点续上传
context ".resumable_upload_with_token" do
it "should works" do
upopts = {:scope => @bucket, :expires_in => 3600, :customer => "why404@gmail.com"}
uptoken = Qiniu.generate_upload_token(upopts)
code, data, raw_headers = Qiniu::Storage.resumable_upload_with_token(
uptoken,
@localfile_5m,
@bucket,
@key_5m
)
(code/100).should == 2
puts data.inspect
puts raw_headers.inspect
puts "key_5m=#{@key_5m}"
end
end
context ".stat" do
it "should exists" do
code, data = Qiniu::Storage.stat(@bucket, @key_5m)
puts data.inspect
code.should == 200
end
end
context ".delete" do
it "should works" do
code, data = Qiniu::Storage.delete(@bucket, @key_5m)
puts data.inspect
code.should == 200
end
end
context ".resumable_upload_with_token2" do
it "should works" do
upopts = {:scope => @bucket, :expires_in => 3600, :customer => "why404@gmail.com"}
uptoken = Qiniu.generate_upload_token(upopts)
code, data, raw_headers = Qiniu::Storage.resumable_upload_with_token(
uptoken,
@localfile_4m,
@bucket,
@key_4m
)
(code/100).should == 2
puts data.inspect
puts raw_headers.inspect
puts "key_4m=#{@key_4m}"
end
end
context ".stat" do
it "should exists" do
code, data = Qiniu::Storage.stat(@bucket, @key_4m)
puts data.inspect
code.should == 200
end
end
context ".delete" do
it "should works" do
code, data = Qiniu::Storage.delete(@bucket, @key_4m)
puts data.inspect
code.should == 200
end
end
context ".resumable_upload_with_token3" do
it "should works" do
upopts = {:scope => @bucket, :expires_in => 3600, :customer => "why404@gmail.com"}
uptoken = Qiniu.generate_upload_token(upopts)
code, data, raw_headers = Qiniu::Storage.resumable_upload_with_token(
uptoken,
@localfile_8m,
@bucket,
@key_8m
)
(code/100).should == 2
puts data.inspect
puts raw_headers.inspect
puts "key_8m=#{@key_8m}"
end
end
context ".stat" do
it "should exists" do
code, data = Qiniu::Storage.stat(@bucket, @key_8m)
puts data.inspect
code.should == 200
end
end
context ".delete" do
it "should works" do
code, data = Qiniu::Storage.delete(@bucket, @key_8m)
puts data.inspect
code.should == 200
end
end
context ".resumable_upload_with_token4" do
it "should works" do
upopts = {:scope => @bucket, :expires_in => 3600, :customer => "why404@gmail.com"}
uptoken = Qiniu.generate_upload_token(upopts)
code, data, raw_headers = Qiniu::Storage.resumable_upload_with_token(
uptoken,
@localfile_1m,
@bucket,
@key_1m
)
(code/100).should == 2
puts data.inspect
puts raw_headers.inspect
puts "key_1m=#{@key_1m}"
end
end
context ".stat" do
it "should exists" do
code, data = Qiniu::Storage.stat(@bucket, @key_1m)
puts data.inspect
code.should == 200
end
end
context ".delete" do
it "should works" do
code, data = Qiniu::Storage.delete(@bucket, @key_1m)
puts data.inspect
code.should == 200
end
end
end
end # module Storage
end # module Qiniu
@@ -2,7 +2,7 @@
require 'spec_helper'
require 'fakeweb'
require 'qiniu/rs/utils'
require 'qiniu/utils'
module Qiniu
module RS
@@ -28,7 +28,7 @@ module Qiniu
FakeWeb.allow_net_connect = false
FakeWeb.register_uri(:get, "http://docs.qiniutek.com/", :body => {:abc => 123}.to_json)
res = Utils.send_request_with 'http://docs.qiniutek.com/', nil, :method => :get
res.should == [200, {"abc" => 123}]
res.should == [200, {"abc" => 123}, {}]
end
[400, 500].each do |code|
+10
View File
@@ -0,0 +1,10 @@
# -*- encoding: utf-8 -*-
require 'spec_helper'
require 'qiniu/version'
describe Qiniu::Version do
it "should has a VERSION" do
Qiniu::Version.to_s.should =~ /^\d+\.\d+\.\d+?$/
end
end
+10 -3
View File
@@ -1,12 +1,19 @@
# -*- encoding: utf-8 -*-
require 'bundler/setup'
require 'qiniu/rs'
require 'qiniu'
require 'rspec'
RSpec.configure do |config|
config.before :all do
Qiniu::RS.establish_connection! :client_id => "abcd0c7edcdf914228ed8aa7c6cee2f2bc6155e2",
:client_secret => "fc9ef8b171a74e197b17f85ba23799860ddf3b9c"
Qiniu.establish_connection! :access_key => ENV["QINIU_ACCESS_KEY"], :secret_key => ENV["QINIU_SECRET_KEY"]
end
end
def make_unique_bucket (bucket)
bucket + "-" + ENV["QINIU_ACCESS_KEY"][0, 8]
end # make_unique_bucket
def make_unique_key_in_bucket (key)
"#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}-" + key
end # make_unique_key_in_bucket