Browse files

Merge pull request #42 from qiniu/develop

Release v6.0.0
  • Loading branch information...
2 parents debae40 + 01e77b6 commit 9eeca1251be5c7dbeb9e6db37f35620485368460 @xushiwei xushiwei committed Jun 26, 2013
View
43 .gitignore
@@ -1,4 +1,45 @@
.DS_Store
*.swp
*.pyc
-*.jpg
+
+*.py[cod]
+
+
+##
+## from https://github.com/github/gitignore/blob/master/Python.gitignore
+##
+
+# C extensions
+*.so
+
+# Packages
+*.egg
+*.egg-info
+dist
+build
+eggs
+parts
+bin
+var
+sdist
+develop-eggs
+.installed.cfg
+lib
+lib64
+__pycache__
+
+# Installer logs
+pip-log.txt
+
+# Unit test / coverage reports
+.coverage
+.tox
+nosetests.xml
+
+# Translations
+*.mo
+
+# Mr Developer
+.mr.developer.cfg
+.project
+.pydevproject
View
14 .travis.yml
@@ -0,0 +1,14 @@
+language: python
+python:
+ - "2.6"
+ - "2.7"
+before_script:
+ - export QINIU_ACCESS_KEY="X0XpjFmLMTJpHB_ESHjeolCtipk-1U3Ok7LVTdoN"
+ - export QINIU_SECRET_KEY="wenlwkU1AYwNBf7Q9cCoG4VT_GYyrHE9AS_R2u81"
+ - export QINIU_PIC_KEY="hello_jpg"
+ - export QINIU_NOEXIST_PIC_KEY="no_exist"
+ - export QINIU_BUCKET_NAME="pysdk"
+ - export QINIU_DOMAIN="pysdk.qiniudn.com"
+script:
+ - python setup.py nosetests
+ - python demo.py
View
7 CHANGELOG.md
@@ -0,0 +1,7 @@
+## CHANGE LOG
+
+### v6.0.0
+
+2013-06-26 issue [#42](https://github.com/qiniu/python-sdk/pull/42)
+
+- 遵循 [sdkspec v6.0.1](https://github.com/qiniu/sdkspec/tree/v6.0.1)
View
62 README.md
@@ -1,36 +1,26 @@
-# Qiniu Resource (Cloud) Storage SDK for Python
-
-# 关于
-
-此 SDK 适用于 Python 2.x 版本。基于 [七牛云存储官方API](http://docs.qiniutek.com/v2/api/) 构建。使用此 SDK 构建您的网络应用程序,能让您以非常便捷地方式将数据安全地存储到七牛云存储上。无论您的网络应用是一个网站程序,还是包括从云端(服务端程序)到终端(手持设备应用)的架构的服务或应用,通过七牛云存储及其 SDK,都能让您应用程序的终端用户高速上传和下载,同时也让您的服务端更加轻盈。
-
-
-## 安装
-
-请到 [ https://github.com/qiniu/python-sdk/tags]( https://github.com/qiniu/python-sdk/tags) 下载最新版本的SDK源代码,然后将该SDK压缩包解压后放置到您相应的工程目录中。
-
-## 依赖
-
-- [httplib2](http://code.google.com/p/httplib2/)
-
-## 使用
-
-参考文献:[七牛云存储 Python SDK使用指南](http://docs.qiniutek.com/v2/sdk/python/)
-
-
-## 贡献代码
-
-1. Fork
-2. 创建您的特性分支 (`git checkout -b my-new-feature`)
-3. 提交您的改动 (`git commit -am 'Added some feature'`)
-4. 将您的修改记录提交到远程 `git` 仓库 (`git push origin my-new-feature`)
-5. 然后到 github 网站的该 `git` 远程仓库的 `my-new-feature` 分支下发起 Pull Request
-
-
-## 许可证
-
-Copyright (c) 2012 qiniutek.com
-
-基于 MIT 协议发布:
-
-[www.opensource.org/licenses/MIT](http://www.opensource.org/licenses/MIT)
+Qiniu Resource Storage SDK for Python
+===
+
+[![Build Status](https://api.travis-ci.org/qiniu/python-sdk.png?branch=develop)](https://travis-ci.org/qiniu/python-sdk)
+
+[![Qiniu Logo](http://qiniutek.com/images/logo-2.png)](http://qiniu.com/)
+
+## 使用
+
+参考文档:[七牛云存储 Python SDK 使用指南](https://github.com/qiniu/python-sdk/blob/develop/docs/README.md)
+
+## 贡献代码
+
+1. Fork
+2. 创建您的特性分支 (`git checkout -b my-new-feature`)
+3. 提交您的改动 (`git commit -am 'Added some feature'`)
+4. 将您的修改记录提交到远程 `git` 仓库 (`git push origin my-new-feature`)
+5. 然后到 github 网站的该 `git` 远程仓库的 `my-new-feature` 分支下发起 Pull Request
+
+## 许可证
+
+Copyright (c) 2013 qiniu.com
+
+基于 MIT 协议发布:
+
+* [www.opensource.org/licenses/MIT](http://www.opensource.org/licenses/MIT)
View
347 demo.py
@@ -0,0 +1,347 @@
+# -*- coding: utf-8 -*-
+import os
+import sys
+
+# @gist import_io
+import qiniu.io
+# @endgist
+import qiniu.conf
+# @gist import_rs
+import qiniu.rs
+# @endgist
+# @gist import_fop
+import qiniu.fop
+# @endgist
+# @gist import_resumable_io
+import qiniu.resumable_io as rio
+# @endgist
+
+import qiniu.rsf
+
+bucket_name = None
+uptoken = None
+key = None
+key2 = None
+key3 = None
+domain = None
+
+# ----------------------------------------------------------
+
+def setup(access_key, secret_key, bucketname, bucket_domain):
+ global bucket_name, uptoken, key, key2, domain, key3
+ qiniu.conf.ACCESS_KEY = access_key
+ qiniu.conf.SECRET_KEY = secret_key
+ bucket_name = bucketname
+ domain = bucket_domain
+ # @gist uptoken
+ policy = qiniu.rs.PutPolicy(bucket_name)
+ uptoken = policy.token()
+ # @endgist
+ key = "python-demo-put-file"
+ key2 = "python-demo-put-file-2"
+ key3 = "python-demo-put-file-3"
+
+def _setup():
+ ''' 根据环境变量配置信息 '''
+ access_key = getenv("QINIU_ACCESS_KEY")
+ if access_key is None:
+ exit("请配置环境变量 QINIU_ACCESS_KEY")
+ secret_key = getenv("QINIU_SECRET_KEY")
+ bucket_name = getenv("QINIU_BUCKET_NAME")
+ domain = getenv("QINIU_DOMAIN")
+ setup(access_key, secret_key, bucket_name, domain)
+
+def getenv(name):
+ env = os.getenv(name)
+ if env is None:
+ sys.stderr.write("请配置环境变量 %s\n" % name)
+ exit(1)
+ return env
+
+def error(obj):
+ sys.stderr.write('error: %s ' % obj)
+
+def get_demo_list():
+ return [put_file, put_binary,
+ resumable_put, resumable_put_file,
+ stat, copy, move, delete, batch,
+ image_info, image_exif, image_view,
+ list_prefix,
+ ]
+
+def run_demos(demos):
+ for i, demo in enumerate(demos):
+ print '%s.%s ' % (i+1, demo.__doc__),
+ demo()
+ print
+
+# ----------------------------------------------------------
+def make_private_url(domain, key):
+ ''' 生成私有下载链接 '''
+ # @gist dntoken
+ base_url = qiniu.rs.make_base_url(domain, key)
+ policy = qiniu.rs.GetPolicy()
+ private_url = policy.make_request(base_url)
+ # @endgist
+ return private_url
+
+def put_file():
+ ''' 演示上传文件的过程 '''
+ # 尝试删除
+ qiniu.rs.Client().delete(bucket_name, key)
+
+ # @gist put_file
+ localfile = "%s" % __file__
+ extra = qiniu.io.PutExtra(bucket_name)
+
+ ret, err = qiniu.io.put_file(uptoken, key, localfile, extra)
+ if err is not None:
+ error(err)
+ return
+ # @endgist
+
+
+def put_binary():
+ ''' 上传二进制数据 '''
+ # 尝试删除
+ qiniu.rs.Client().delete(bucket_name, key)
+
+ # @gist put
+ extra = qiniu.io.PutExtra(bucket_name)
+ extra.mime_type = "text/plain"
+
+ ret, err = qiniu.io.put(uptoken, key, "hello!", extra)
+ if err is not None:
+ error(err)
+ return
+ # @endgist
+
+def resumable_put():
+ ''' 断点续上传 '''
+ # 尝试删除
+ qiniu.rs.Client().delete(bucket_name, key)
+
+ # @gist resumable_put
+ class ResumableUpload(object):
+ position = 0
+ def __init__(self, string_data):
+ self.data = string_data
+
+ def read(self, length):
+ data = self.data[self.position: self.position+length]
+ self.position += length
+ return data
+
+ a = "resumable upload string"
+ extra = rio.PutExtra(bucket_name)
+ extra.mime_type = "text/plain"
+ ret, err = rio.put(uptoken, key, ResumableUpload(a), len(a), extra)
+ if err is not None:
+ error(err)
+ return
+ print ret,
+ # @endgist
+
+
+def resumable_put_file():
+ ''' 断点续上传文件 '''
+ # 尝试删除
+ qiniu.rs.Client().delete(bucket_name, key)
+
+ # @gist resumable_put_file
+ localfile = "%s" % __file__
+ extra = rio.PutExtra(bucket_name)
+
+ ret, err = rio.put_file(uptoken, key, localfile, extra)
+ if err is not None:
+ error(err)
+ return
+ print ret,
+ # @endgist
+
+
+def stat():
+ ''' 查看上传文件的内容 '''
+ # @gist stat
+ ret, err = qiniu.rs.Client().stat(bucket_name, key)
+ if err is not None:
+ error(err)
+ return
+ print ret,
+ # @endgist
+
+def copy():
+ ''' 复制文件 '''
+ # 初始化
+ qiniu.rs.Client().delete(bucket_name, key2)
+
+ # @gist copy
+ ret, err = qiniu.rs.Client().copy(bucket_name, key, bucket_name, key2)
+ if err is not None:
+ error(err)
+ return
+ # @endgist
+
+ stat, err = qiniu.rs.Client().stat(bucket_name, key2)
+ if err is not None:
+ error(err)
+ return
+ print 'new file:', stat,
+
+def move():
+ ''' 移动文件 '''
+ # 初始化
+ qiniu.rs.Client().delete(bucket_name, key3)
+
+ # @gist move
+ ret, err = qiniu.rs.Client().move(bucket_name, key2, bucket_name, key3)
+ if err is not None:
+ error(err)
+ return
+ # @endgist
+
+ # 查看文件是否移动成功
+ ret, err = qiniu.rs.Client().stat(bucket_name, key3)
+ if err is not None:
+ error(err)
+ return
+
+ # 查看文件是否被删除
+ ret, err = qiniu.rs.Client().stat(bucket_name, key2)
+ if err is None:
+ error("删除失败")
+ return
+
+def delete():
+ ''' 删除文件 '''
+ # @gist delete
+ ret, err = qiniu.rs.Client().delete(bucket_name, key3)
+ if err is not None:
+ error(err)
+ return
+ # @endgist
+
+ ret, err = qiniu.rs.Client().stat(bucket_name, key3)
+ if err is None:
+ error("删除失败")
+ return
+
+def image_info():
+ ''' 上传图片, 并且查看他的信息 '''
+ # 初始化
+ qiniu.rs.Client().delete(bucket_name, key2)
+
+ extra = qiniu.io.PutExtra(bucket_name)
+ extra.mime_type = "image/png"
+ localfile = 'qiniu/test/photo_test.jpeg'
+ ret, err = qiniu.io.put_file(uptoken, key2, localfile, extra)
+ if err is not None:
+ error(err)
+ return
+
+ # @gist image_info
+ # 生成base_url
+ url = qiniu.rs.make_base_url(domain, key2)
+
+ # 生成fop_url
+ image_info = qiniu.fop.ImageInfo()
+ url = image_info.make_request(url)
+
+ # 对其签名,生成private_url。如果是公有bucket此步可以省略
+ policy = qiniu.rs.GetPolicy()
+ url = policy.make_request(url)
+
+ print '可以在浏览器浏览: %s' % url
+ # @endgist
+
+def image_exif():
+ ''' 查看图片的exif信息 '''
+ # @gist exif
+ # 生成base_url
+ url = qiniu.rs.make_base_url(domain, key2)
+
+ # 生成fop_url
+ image_exif = qiniu.fop.Exif()
+ url = image_exif.make_request(url)
+
+ # 对其签名,生成private_url。如果是公有bucket此步可以省略
+ policy = qiniu.rs.GetPolicy()
+ url = policy.make_request(url)
+
+ print '可以在浏览器浏览: %s' % url
+ # @endgist
+
+def image_view():
+ ''' 对图片进行预览处理 '''
+ # @gist image_view
+ iv = qiniu.fop.ImageView()
+ iv.width = 100
+
+ # 生成base_url
+ url = qiniu.rs.make_base_url(domain, key2)
+ # 生成fop_url
+ url = iv.make_request(url)
+ # 对其签名,生成private_url。如果是公有bucket此步可以省略
+ policy = qiniu.rs.GetPolicy()
+ url = policy.make_request(url)
+ print '可以在浏览器浏览: %s' % url
+ # @endgist
+
+def batch():
+ ''' 文件处理的批量操作 '''
+ # @gist batch_path
+ path_1 = qiniu.rs.EntryPath(bucket_name, key)
+ path_2 = qiniu.rs.EntryPath(bucket_name, key2)
+ path_3 = qiniu.rs.EntryPath(bucket_name, key3)
+ # @endgist
+
+ # 查看状态
+ # @gist batch_stat
+ rets, err = qiniu.rs.Client().batch_stat([path_1, path_2, path_3])
+ if err is not None:
+ error(err)
+ return
+ # @endgist
+ if not [ret['code'] for ret in rets] == [200, 612, 612]:
+ error("批量获取状态与预期不同")
+ return
+
+ # 复制
+ # @gist batch_copy
+ pair_1 = qiniu.rs.EntryPathPair(path_1, path_3)
+ rets, err = qiniu.rs.Client().batch_copy([pair_1])
+ if not rets[0]['code'] == 200:
+ error("复制失败")
+ return
+ # @endgist
+
+ qiniu.rs.Client().batch_delete([path_2])
+ # @gist batch_move
+ pair_2 = qiniu.rs.EntryPathPair(path_3, path_2)
+ rets, err = qiniu.rs.Client().batch_move([pair_2])
+ if not rets[0]['code'] == 200:
+ error("移动失败")
+ return
+ # @endgist
+
+ # 删除残留文件
+ # @gist batch_delete
+ rets, err = qiniu.rs.Client().batch_delete([path_1, path_2])
+ if not [ret['code'] for ret in rets] == [200, 200]:
+ error("删除失败")
+ return
+ # @endgist
+
+def list_prefix():
+ ''' 列出文件操作 '''
+ rets, err = qiniu.rsf.Client().list_prefix(bucket_name, prefix="test", limit=3)
+ if err is not None:
+ error(err)
+ return
+ print rets
+
+if __name__ == "__main__":
+ _setup()
+
+ demos = get_demo_list()
+ run_demos(demos)
View
318 docs/README.gist.md
@@ -0,0 +1,318 @@
+Qiniu Resource (Cloud) Storage SDK for Python
+===
+
+# Python 2.x SDK 使用指南
+
+此 Python SDK 适用于2.x版本,基于 [七牛云存储官方API](http://docs.qiniutek.com/v3/api/) 构建。使用此 SDK 构建您的网络应用程序,能让您以非常便捷地方式将数据安全地存储到七牛云存储上。无论您的网络应用是一个网站程序,还是包括从云端(服务端程序)到终端(手持设备应用)的架构的服务或应用,通过七牛云存储及其 SDK,都能让您应用程序的终端用户高速上传和下载,同时也让您的服务端更加轻盈。
+
+目录
+----
+- [1. 安装](#install)
+- [2. 初始化](#setup)
+ - [2.1 配置密钥](#setup-key)
+- [3. 资源管理接口](#rs-api)
+ - [3.1 查看单个文件属性信息](#rs-stat)
+ - [3.2 复制单个文件](#rs-copy)
+ - [3.3 移动单个文件](#rs-move)
+ - [3.4 删除单个文件](#rs-delete)
+ - [3.5 批量操作](#batch)
+ - [3.5.1 批量获取文件属性信息](#batch-stat)
+ - [3.5.2 批量复制文件](#batch-copy)
+ - [3.5.3 批量移动文件](#batch-move)
+ - [3.5.4 批量删除文件](#batch-delete)
+- [4. 上传下载接口](#get-and-put-api)
+ - [4.1 上传授权](#token)
+ - [4.1.1 生成uptoken](#make-uptoken)
+ - [4.2 文件上传](#upload)
+ - [4.2.1 普通上传](#io-upload)
+ - [4.2.2 断点续上传](#resumable-io-upload)
+ - [4.3 文件下载](#io-download)
+ - [4.3.1 公有资源下载](#public-download)
+ - [4.3.2 私有资源下载](#private-download)
+- [5. 数据处理接口](#fop-api)
+ - [5.1 图像](#fop-image)
+ - [5.1.1 查看图像属性](#fop-image-info)
+ - [5.1.2 查看图片EXIF信息](#fop-exif)
+ - [5.1.3 生成图片预览](#fop-image-view)
+- [6. 贡献代码](#contribution)
+- [7. 许可证](#license)
+
+----
+
+<a name=install></a>
+## 1. 安装
+
+直接安装:
+
+ pip install qiniu
+ #或
+ easy_install qiniu
+
+源码安装:
+
+ git clone http://github.com/qiniu/python-sdk
+ cd python-sdk && python setup.py install
+
+<a name=setup-key></a>
+### 2.1 配置密钥
+
+要接入七牛云存储,您需要拥有一对有效的 Access Key 和 Secret Key 用来进行签名认证。可以通过如下步骤获得:
+
+1. [开通七牛开发者帐号](https://dev.qiniutek.com/signup)
+2. [登录七牛开发者自助平台,查看 Access Key 和 Secret Key](https://dev.qiniutek.com/account/keys) 。
+
+在获取到 Access Key 和 Secret Key 之后,您可以在您的程序中调用如下两行代码进行初始化对接, 要确保`ACCESS_KEY``SECRET_KEY` 在调用所有七牛API服务之前均已赋值:
+
+```{python}
+@gist(gist/conf.py#config)
+```
+
+<a name=rs-api></a>
+## 3. 资源管理接口
+
+<a name=rs-stat></a>
+### 3.1 查看单个文件属性信息
+```{python}
+@gist(gist/conf.py#config)
+
+@gist(../demo.py#import_rs)
+
+@gist(../demo.py#stat)
+```
+
+<a name=rs-copy></a>
+### 3.2 复制单个文件
+```{python}
+@gist(gist/conf.py#config)
+
+@gist(../demo.py#import_rs)
+
+@gist(../demo.py#copy)
+```
+
+<a name=rs-move></a>
+### 3.3 移动单个文件
+```{python}
+@gist(gist/conf.py#config)
+
+@gist(../demo.py#import_rs)
+
+@gist(../demo.py#move)
+```
+
+<a name=rs-delete></a>
+### 3.4 删除单个文件
+```{python}
+@gist(gist/conf.py#config)
+
+@gist(../demo.py#import_rs)
+
+@gist(../demo.py#move)
+```
+
+<a name=batch></a>
+### 3.5 批量操作
+当您需要一次性进行多个操作时, 可以使用批量操作.
+<a name=batch-stat></a>
+#### 3.5.1 批量获取文件属性信息
+```{python}
+@gist(gist/conf.py#config)
+
+@gist(../demo.py#import_rs)
+
+@gist(../demo.py#batch_path)
+
+@gist(../demo.py#batch_stat)
+```
+
+<a name=batch-copy></a>
+#### 3.5.2 批量复制文件
+```{python}
+@gist(gist/conf.py#config)
+
+@gist(../demo.py#import_rs)
+
+@gist(../demo.py#batch_path)
+
+@gist(../demo.py#batch_copy)
+```
+
+<a name=batch-move></a>
+#### 3.5.3 批量移动文件
+```{python}
+@gist(gist/conf.py#config)
+
+@gist(../demo.py#import_rs)
+
+@gist(../demo.py#batch_path)
+
+@gist(../demo.py#batch_move)
+```
+
+<a name=batch-delete></a>
+#### 3.5.4 批量删除文件
+```{python}
+@gist(gist/conf.py#config)
+
+@gist(../demo.py#import_rs)
+
+@gist(../demo.py#batch_path)
+
+@gist(../demo.py#batch_delete)
+```
+
+<a name=get-and-put-api></a>
+## 4. 上传下载接口
+
+<a name=token></a>
+### 4.1 上传下载授权
+<a name=make-uptoken></a>
+#### 4.1.1 上传授权uptoken
+uptoken是一个字符串,作为http协议Header的一部分(Authorization字段)发送到我们七牛的服务端,表示这个http请求是经过认证的。
+```{python}
+@gist(gist/conf.py#config)
+
+@gist(../demo.py#import_rs)
+
+@gist(../demo.py#uptoken)
+```
+
+<a name=upload></a>
+### 4.2 文件上传
+**注意**:如果您只是想要上传已存在您电脑本地或者是服务器上的文件到七牛云存储,可以直接使用七牛提供的 [qrsync](/v3/tools/qrsync/) 上传工具。
+文件上传有两种方式,一种是以普通方式直传文件,简称普通上传,另一种方式是断点续上传,断点续上传在网络条件很一般的情况下也能有出色的上传速度,而且对大文件的传输非常友好。
+
+<a name=io-upload></a>
+### 4.2.1 普通上传
+普通上传的接口在 `qiniu.io` 里,如下:
+
+直接上传二进制流
+```{python}
+@gist(gist/conf.py#config)
+
+@gist(../demo.py#import_io)
+
+@gist(../demo.py#put)
+```
+
+上传本地文件
+
+```{python}
+@gist(gist/conf.py#config)
+
+@gist(../demo.py#import_io)
+
+@gist(../demo.py#put_file)
+```
+
+<a name=resumable-io-upload></a>
+### 4.2.2 断点续上传
+上传二进制流
+```{python}
+@gist(gist/conf.py#config)
+
+@gist(../demo.py#import_resumable_io)
+
+@gist(../demo.py#resumable_put)
+```
+
+上传本地文件
+```{python}
+@gist(gist/conf.py#config)
+
+@gist(../demo.py#import_resumable_io)
+
+@gist(../demo.py#resumable_put_file)
+```
+
+<a name=io-download></a>
+### 4.3 文件下载
+七牛云存储上的资源下载分为 公有资源下载 和 私有资源下载 。
+
+私有(private)是 Bucket(空间)的一个属性,一个私有 Bucket 中的资源为私有资源,私有资源不可匿名下载。
+
+新创建的空间(Bucket)缺省为私有,也可以将某个 Bucket 设为公有,公有 Bucket 中的资源为公有资源,公有资源可以匿名下载。
+
+<a name=public-download></a>
+#### 4.3.1 公有资源下载
+如果在给bucket绑定了域名的话,可以通过以下地址访问。
+
+ [GET] http://<domain>/<key>
+
+其中<domain>可以到[七牛云存储开发者自助网站](https://portal.qiniu.com/)绑定, 域名可以使用自己一级域名的或者是由七牛提供的二级域名(`<bucket>.qiniudn.com`)。注意,尖括号不是必需,代表替换项。
+
+<a name=private-download></a>
+#### 4.3.2 私有资源下载
+私有资源必须通过临时下载授权凭证(downloadToken)下载,如下:
+
+ [GET] http://<domain>/<key>?e=<deadline>&token=<downloadToken>
+
+注意,尖括号不是必需,代表替换项。
+私有下载链接可以使用 SDK 提供的如下方法生成:
+
+```{python}
+@gist(gist/conf.py#config)
+
+@gist(../demo.py#import_rs)
+
+@gist(../demo.py#dntoken)
+```
+
+<a name=fop-api></a>
+## 5. 数据处理接口
+七牛支持在云端对图像, 视频, 音频等富媒体进行个性化处理
+
+<a name=fop-image></a>
+### 5.1 图像
+<a name=fop-image-info></a>
+### 5.1.1 查看图像属性
+```{python}
+@gist(gist/conf.py#config)
+
+@gist(../demo.py#import_fop)
+@gist(../demo.py#import_rs)
+
+@gist(../demo.py#image_info)
+```
+
+<a name=fop-exif></a>
+### 5.1.2 查看图片EXIF信息
+```{python}
+@gist(gist/conf.py#config)
+
+@gist(../demo.py#import_fop)
+@gist(../demo.py#import_rs)
+
+@gist(../demo.py#exif)
+```
+
+
+<a name=fop-image-view></a>
+### 5.1.3 生成图片预览
+```{python}
+@gist(gist/conf.py#config)
+
+@gist(../demo.py#import_fop)
+@gist(../demo.py#import_rs)
+
+@gist(../demo.py#image_view)
+```
+
+<a name=contribution></a>
+## 6. 贡献代码
+
+1. Fork
+2. 创建您的特性分支 (`git checkout -b my-new-feature`)
+3. 提交您的改动 (`git commit -am 'Added some feature'`)
+4. 将您的修改记录提交到远程 `git` 仓库 (`git push origin my-new-feature`)
+5. 然后到 github 网站的该 `git` 远程仓库的 `my-new-feature` 分支下发起 Pull Request
+
+<a name=license></a>
+## 7. 许可证
+
+Copyright (c) 2013 qiniu.com
+
+基于 MIT 协议发布:
+
+* [www.opensource.org/licenses/MIT](http://www.opensource.org/licenses/MIT)
+
View
649 docs/README.md
@@ -1,374 +1,479 @@
----
-title: Python 2.x SDK | 七牛云存储
----
+Qiniu Resource (Cloud) Storage SDK for Python
+===
# Python 2.x SDK 使用指南
-
-此 SDK 适用于 Python 2.x 版本
-
-SDK 下载地址:[https://github.com/qiniu/python-sdk/tags](https://github.com/qiniu/python-sdk/tags)
-
-SDK 使用依赖Python第三方HTTP CLient -- <http://code.google.com/p/httplib2/>
-
-**应用接入**
-
-- [获取Access Key 和 Secret Key](#acc-appkey)
-- [签名认证](#acc-auth)
-
-**云存储接口**
-
-- [新建存储空间(Bucket)](#rs-Mkbucket)
-- [上传文件](#rs-PutFile)
- - [获取用于上传文件的临时授权凭证](#token)
- - [服务端上传文件](#putfile)
- - [客户端直传文件](#enputfile)
- - [网页直传文件](#web-upload-fie)
-- [初始化空间(Bucket)对象](#rs-NewService)
-- [获取已上传文件信息](#rs-Stat)
-- [下载文件](#rs-Get)
-- [发布公开资源](#rs-Publish)
-- [取消资源发布](#rs-Unpublish)
-- [删除已上传的文件](#rs-Delete)
-- [资源表管理](#rs-buckets)
- - [列出所有资源表](#rs-Buckets)
- - [删除整张资源表](#rs-Drop)
-- [资源表批量操作接口](#rs-Batch)
- - [批量下载](#rs-BatchGet)
- - [批量删除](#rs-BatchDelete)
-
-**图像处理接口**
-
-TODO
-
-
-
-## 应用接入
-
-<a name="acc-appkey"></a>
-
-### 1. 获取Access Key 和 Secret Key
+此 Python SDK 适用于2.x版本,基于 [七牛云存储官方API](http://docs.qiniutek.com/v3/api/) 构建。使用此 SDK 构建您的网络应用程序,能让您以非常便捷地方式将数据安全地存储到七牛云存储上。无论您的网络应用是一个网站程序,还是包括从云端(服务端程序)到终端(手持设备应用)的架构的服务或应用,通过七牛云存储及其 SDK,都能让您应用程序的终端用户高速上传和下载,同时也让您的服务端更加轻盈。
+
+目录
+----
+- [1. 安装](#install)
+- [2. 初始化](#setup)
+ - [2.1 配置密钥](#setup-key)
+- [3. 资源管理接口](#rs-api)
+ - [3.1 查看单个文件属性信息](#rs-stat)
+ - [3.2 复制单个文件](#rs-copy)
+ - [3.3 移动单个文件](#rs-move)
+ - [3.4 删除单个文件](#rs-delete)
+ - [3.5 批量操作](#batch)
+ - [3.5.1 批量获取文件属性信息](#batch-stat)
+ - [3.5.2 批量复制文件](#batch-copy)
+ - [3.5.3 批量移动文件](#batch-move)
+ - [3.5.4 批量删除文件](#batch-delete)
+- [4. 上传下载接口](#get-and-put-api)
+ - [4.1 上传授权](#token)
+ - [4.1.1 生成uptoken](#make-uptoken)
+ - [4.2 文件上传](#upload)
+ - [4.2.1 普通上传](#io-upload)
+ - [4.2.2 断点续上传](#resumable-io-upload)
+ - [4.3 文件下载](#io-download)
+ - [4.3.1 公有资源下载](#public-download)
+ - [4.3.2 私有资源下载](#private-download)
+- [5. 数据处理接口](#fop-api)
+ - [5.1 图像](#fop-image)
+ - [5.1.1 查看图像属性](#fop-image-info)
+ - [5.1.2 查看图片EXIF信息](#fop-exif)
+ - [5.1.3 生成图片预览](#fop-image-view)
+- [6. 贡献代码](#contribution)
+- [7. 许可证](#license)
+
+----
+
+<a name=install></a>
+## 1. 安装
+
+直接安装:
+
+ pip install qiniu
+ #或
+ easy_install qiniu
+
+源码安装:
+
+ git clone http://github.com/qiniu/python-sdk
+ cd python-sdk && python setup.py install
+
+<a name=setup-key></a>
+### 2.1 配置密钥
要接入七牛云存储,您需要拥有一对有效的 Access Key 和 Secret Key 用来进行签名认证。可以通过如下步骤获得:
1. [开通七牛开发者帐号](https://dev.qiniutek.com/signup)
-2. [登录七牛开发者自助平台,查看 Access Key 和 Secret Key](https://dev.qiniutek.com/account/keys)
-
-<a name="acc-login"></a>
-
-### 2. 签名认证
-
-首先,到 [https://github.com/qiniu/python-sdk/tags](https://github.com/qiniu/python-sdk/tags) 下载SDK源码。
-
-然后,您可以解压 SDK 包后将其放入您项目工程相应的目录中。在引入 SDK 里边的文件后,您需要修改下配置项:
-
- import config
-
- config.ACCESS_KEY = '<Please apply your access key>'
- config.SECRET_KEY = '<Dont send your secret key to anyone>'
-
-在完成 Access Key 和 Secret Key 配置后,您就可以正常使用该 SDK 提供的功能了,这些功能接下来会一一介绍。
-
-## 云存储接口
-
-<a name="rs-Mkbucket"></a>
-
-### 1. 新建存储空间(Bucket)
-
-新建存储空间(Bucket)的意义在于,您可以将所有上传的资源分布式加密存储在七牛云存储服务端后还能保持相应的完整映射索引。
-
-可以通过 SDK 提供的 `Mkbucket` 函数创建一个 Bucket 。
-
- resp = rs.Mkbucket(BucketName)
-
-**参数**
-
-**BucketName**
-: 必填,字符串(String)类型,空间名称,不能含有特殊字符。 
-
-<a name="rs-PutFile"></a>
-
-### 2. 上传文件
-
-<a name="token"></a>
-
-#### 2.1 获取用于上传文件的临时授权凭证
-
-要上传一个文件,首先需要调用 SDK 提供的 generate_token 函数来获取一个经过授权用于临时匿名上传的 uploadtoken——经过数字签名的一组数据信息,该 uploadtoken 作为文件上传流中 multipart/form-data 的一部分进行传输。
-
-生成uptoken如下:
-
- import uptoken
- tokenObj = uptoken.UploadToken(scope, expires_in, callback_url, callback_bodytype, customer)
- uploadtoken = tokenObj.generate_token()
-
-UploadToken 初始化各参数含义如下:
-
-**scope**
-: 必须,字符串类型(String),设定文件要上传到的目标 bucket。
-
-**expires_in**
-: 可选,数字类型,用于设置上传 URL 的有效期,单位:秒,缺省为 3600 秒,即 1 小时后该上传链接不再有效(但该上传URL在其生成之后的59分59秒都是可用的)。
-
-**callback_url**
-: 可选,字符串类型(String),用于设置文件上传成功后,七牛云存储服务端要回调客户方的业务服务器地址。
-
-**callback_bodytype**
-: 可选,字符串类型(String),用于设置文件上传成功后,七牛云存储服务端向客户方的业务服务器发送回调请求的 Content-Type。
-
-**customer**
-: 可选,字符串类型(String),客户方终端用户(End User)的ID,该字段可以用来标示一个文件的属主,这在一些特殊场景下(比如给终端用户上传的图片打上名字水印)非常有用。
-
-
-<a name="putfile"></a>
-
-#### 2.2 服务端上传文件
-
-PutFile() 方法可在客户方的业务服务器上直接往七牛云存储上传文件。该函数规格如下:
-
- import rscli
- resp = rscli.UploadFile(bucket, key, mimeType, localFile, customMeta, callbackParams, uploadToken)
-
-PutFile() 参数含义如下:
-
- bucket # 要上传到的目标 bucket 名称
- key # 设置文件唯一标识
- mimeType # 资源类型,文件的 MIME TYPE,比如 jpg 图片可以是 'image/jpg'
- localFile # 本地文件路径,最好是完整的绝对路径
- customMeta # 自定义描述信息
- callbackParams # 回调参数,格式: "k1=v1&k2=v2&k3=v3..."
- uploadToken # 此次上传的授权凭证
-
-<a name="enputfile"></a>
-
-#### 2.3 客户端直传文件
-
-客户端上传流程和服务端上传类似,差别在于:客户端直传文件所需的 `upload_token` 可以选择在客户方的业务服务器端生成,也可以选择在客户方的客户端程序里边生成。选择前者,可以和客户方的业务揉合得更紧密和安全些,比如防伪造请求。
-
-简单来讲,客户端上传流程也分为两步:
+2. [登录七牛开发者自助平台,查看 Access Key 和 Secret Key](https://dev.qiniutek.com/account/keys) 。
-1. 生成 `uploadToken`([用于上传文件的临时授权凭证](#generate-upload-token))
-2. 将该 `uploadToken` 作为文件上传流 `multipart/form-data` 中的一部分实现上传操作
+在获取到 Access Key 和 Secret Key 之后,您可以在您的程序中调用如下两行代码进行初始化对接, 要确保`ACCESS_KEY``SECRET_KEY` 在调用所有七牛API服务之前均已赋值:
-如果您的网络程序是从云端(服务端程序)到终端(手持设备应用)的架构模型,且终端用户有使用您移动端App上传文件(比如照片或视频)的需求,可以把您服务器得到的此 `upload_token` 返回给手持设备端的App,然后您的移动 App 可以使用 [七牛云存储 Objective-SDK (iOS)](http://docs.qiniutek.com/v3/sdk/objc/) 或 [七牛云存储 Android-SDK](http://docs.qiniutek.com/v3/sdk/android/) 的相关上传函数或参照 [七牛云存储API之文件上传](http://docs.qiniutek.com/v3/api/io/#upload) 直传文件。这样,您的终端用户即可把数据(比如图片或视频)直接上传到七牛云存储服务器上无须经由您的服务端中转,而且在上传之前,七牛云存储做了智能加速,终端用户上传数据始终是离他物理距离最近的存储节点。当终端用户上传成功后,七牛云存储服务端会向您指定的 `callback_url` 发送回调数据。如果 `callback_url` 所在的服务处理完毕后输出 `JSON` 格式的数据,七牛云存储服务端会将该回调请求所得的响应信息原封不动地返回给终端应用程序。
+```{python}
+import qiniu.config
-<a name="web-upload-fie"></a>
+qiniu.config.ACCESS_KEY = "<YOUR_APP_ACCESS_KEY>"
+qiniu.config.SECRET_KEY = "<YOUR_APP_SECRET_KEY>"
+```
-#### 2.3.1 网页直传文件
+<a name=rs-api></a>
+## 3. 资源管理接口
-网页上传文件,需要满足如下 HTML Form 规格:
+<a name=rs-stat></a>
+### 3.1 查看单个文件属性信息
+```{python}
+import qiniu.config
- <form method="post" enctype="multipart/form-data" action="http://up.qbox.me/upload">
- <input type="hidden" name="action" value="/rs-put/{urlsafe_b64encode({bucket}:{key})}" />
- <input type="hidden" name="params" value="bucket={bucket}&key={key}&k1=v1&k2=v2&k3=v3&..." />
- <input type="hidden" name="auth" value="{uploadToken}" />
- <input type="file" name="file" />
- <input type="hidden" name="return_url" value="http://DOMAIN/PATH?QUERY_STRING" />
- <input type="submit" value="Upload File" />
- </form>
+qiniu.config.ACCESS_KEY = "<YOUR_APP_ACCESS_KEY>"
+qiniu.config.SECRET_KEY = "<YOUR_APP_SECRET_KEY>"
-如上表单结构,其中 `return_url` 字段非必须。倘若有入 `return_url` 字段,七牛云存储会在文件上传成功后执行301跳转,跳转的 URL 即 `return_url` 指定的文本值。七牛云存储执行 301 跳转不影响回调请求的进行,若生成 `uploadToken` 的过程中有指定 `callback_url` 参数,那么回调同样会执行,回调请求POST发送的参数即上述 HTML Form 结构中 `params` 字段指定的值。
+import qiniu.rs
-以上 HTML Form 结构只满足单个文件上传,大多数时候我们在网页中会用到批量上传,比如使用 `SWFUpload``jQuery-Ajax-File-Upload` 批量上传组件。使用这些批量上传组件,只需在文件上传前的虚拟Form中动态插入如上 HTML Form 结构中相应的字段即可,`return_url` 除外。关于网页批直传文件的更多细节,您可以向我们的技术支持工程师获得更详细的帮助。
+ret, err = qiniu.rs.Client().stat(bucket_name, key)
+if err is not None:
+ error(err)
+ return
+print ret,
+```
-<a name="rs-NewService"></a>
+<a name=rs-copy></a>
+### 3.2 复制单个文件
+```{python}
+import qiniu.config
-### 3. 初始化空间(Bucket)对象
+qiniu.config.ACCESS_KEY = "<YOUR_APP_ACCESS_KEY>"
+qiniu.config.SECRET_KEY = "<YOUR_APP_SECRET_KEY>"
-初始化空间(Bucket)对象后,后续可以在该空间对象的基础上对该空间进行各种操作。 
+import qiniu.rs
- client = digestoauth.Client()
- bucket = 'bucket_name'
- rs = qboxrs.Service(client, bucket)
+ret, err = qiniu.rs.Client().copy(bucket_name, key, bucket_name, key2)
+if err is not None:
+ error(err)
+ return
+```
+<a name=rs-move></a>
+### 3.3 移动单个文件
+```{python}
+import qiniu.config
-<a name="rs-Stat"></a>
+qiniu.config.ACCESS_KEY = "<YOUR_APP_ACCESS_KEY>"
+qiniu.config.SECRET_KEY = "<YOUR_APP_SECRET_KEY>"
-### 4. 获取已上传文件信息
+import qiniu.rs
-您可以调用资源表对象的 Stat() 方法并传入一个 Key(类似ID)来获取指定文件的相关信息。
+ret, err = qiniu.rs.Client().move(bucket_name, key2, bucket_name, key3)
+if err is not None:
+ error(err)
+ return
+```
- resp = rs.Stat(key)
+<a name=rs-delete></a>
+### 3.4 删除单个文件
+```{python}
+import qiniu.config
+qiniu.config.ACCESS_KEY = "<YOUR_APP_ACCESS_KEY>"
+qiniu.config.SECRET_KEY = "<YOUR_APP_SECRET_KEY>"
-如果请求成功,得到的 statRet 数组将会包含如下几个字段:
+import qiniu.rs
+
+ret, err = qiniu.rs.Client().move(bucket_name, key2, bucket_name, key3)
+if err is not None:
+ error(err)
+ return
+```
- hash: <FileETag>
- fsize: <FileSize>
- putTime: <PutTime>
- mimeType: <MimeType>
+<a name=batch></a>
+### 3.5 批量操作
+当您需要一次性进行多个操作时, 可以使用批量操作.
+<a name=batch-stat></a>
+#### 3.5.1 批量获取文件属性信息
+```{python}
+import qiniu.config
+qiniu.config.ACCESS_KEY = "<YOUR_APP_ACCESS_KEY>"
+qiniu.config.SECRET_KEY = "<YOUR_APP_SECRET_KEY>"
-<a name="rs-Get"></a>
+import qiniu.rs
+
+path_1 = qiniu.rs.EntryPath(bucket_name, key)
+path_2 = qiniu.rs.EntryPath(bucket_name, key2)
+path_3 = qiniu.rs.EntryPath(bucket_name, key3)
+
+rets, err = qiniu.rs.Client().batch_stat([path_1, path_2, path_3])
+if err is not None:
+ error(err)
+ return
+```
-### 5. 下载文件
+<a name=batch-copy></a>
+#### 3.5.2 批量复制文件
+```{python}
+import qiniu.config
-要下载一个文件,首先需要取得下载授权,所谓下载授权,就是取得一个临时合法有效的下载链接,只需调用资源表对象的 Get() 方法并传入相应的 文件ID 和下载要保存的文件名 作为参数即可。示例代码如下:
+qiniu.config.ACCESS_KEY = "<YOUR_APP_ACCESS_KEY>"
+qiniu.config.SECRET_KEY = "<YOUR_APP_SECRET_KEY>"
+
+import qiniu.rs
+
+path_1 = qiniu.rs.EntryPath(bucket_name, key)
+path_2 = qiniu.rs.EntryPath(bucket_name, key2)
+path_3 = qiniu.rs.EntryPath(bucket_name, key3)
- resp = rs.Get(key, saveAsFriendlyName)
+pair_1 = qiniu.rs.EntryPathPair(path_1, path_3)
+rets, err = qiniu.rs.Client().batch_copy([pair_1])
+if not rets[0]['code'] == 200:
+ error("复制失败")
+ return
+```
+<a name=batch-move></a>
+#### 3.5.3 批量移动文件
+```{python}
+import qiniu.config
-注意,这并不会直接将文件下载并命名为一个 example.jpg 的文件。当请求执行成功,Get() 方法返回的 getRet 变量将会包含如下字段:
+qiniu.config.ACCESS_KEY = "<YOUR_APP_ACCESS_KEY>"
+qiniu.config.SECRET_KEY = "<YOUR_APP_SECRET_KEY>"
- url: <DownloadURL> # 获取文件内容的实际下载地址
- hash: <FileETag>
- fsize: <FileSize>
- mimeType: <MimeType>
- expires:<Seconds> #下载url的实际生命周期,精确到秒
+import qiniu.rs
+path_1 = qiniu.rs.EntryPath(bucket_name, key)
+path_2 = qiniu.rs.EntryPath(bucket_name, key2)
+path_3 = qiniu.rs.EntryPath(bucket_name, key3)
-这里所说的断点续传指断点续下载,所谓断点续下载,就是已经下载的部分不用下载,只下载基于某个“游标”之后的那部分文件内容。相对于资源表对象的 Get() 方法,调用断点续下载方法 GetIfNotModified() 需额外再传入一个 $baseVersion 的参数作为下载的内容起点。示例代码如下:
+pair_2 = qiniu.rs.EntryPathPair(path_3, path_2)
+rets, err = qiniu.rs.Client().batch_move([pair_2])
+if not rets[0]['code'] == 200:
+ error("移动失败")
+ return
+```
- resp = rs.GetIfNotModified(key, saveAsFriendlyName, resp['hash'])
+<a name=batch-delete></a>
+#### 3.5.4 批量删除文件
+```{python}
+import qiniu.config
+
+qiniu.config.ACCESS_KEY = "<YOUR_APP_ACCESS_KEY>"
+qiniu.config.SECRET_KEY = "<YOUR_APP_SECRET_KEY>"
-GetIfNotModified() 方法返回的结果包含的字段同 Get() 方法一致。
+import qiniu.rs
+
+path_1 = qiniu.rs.EntryPath(bucket_name, key)
+path_2 = qiniu.rs.EntryPath(bucket_name, key2)
+path_3 = qiniu.rs.EntryPath(bucket_name, key3)
+
+rets, err = qiniu.rs.Client().batch_delete([path_1, path_2])
+if not [ret['code'] for ret in rets] == [200, 200]:
+ error("删除失败")
+ return
+```
+
+<a name=get-and-put-api></a>
+## 4. 上传下载接口
+
+<a name=token></a>
+### 4.1 上传下载授权
+<a name=make-uptoken></a>
+#### 4.1.1 上传授权uptoken
+uptoken是一个字符串,作为http协议Header的一部分(Authorization字段)发送到我们七牛的服务端,表示这个http请求是经过认证的。
+```{python}
+import qiniu.config
+
+qiniu.config.ACCESS_KEY = "<YOUR_APP_ACCESS_KEY>"
+qiniu.config.SECRET_KEY = "<YOUR_APP_SECRET_KEY>"
+
+import qiniu.rs
+
+policy = qiniu.rs.PutPolicy(bucket_name)
+uptoken = policy.token()
+```
+
+<a name=upload></a>
+### 4.2 文件上传
+**注意**:如果您只是想要上传已存在您电脑本地或者是服务器上的文件到七牛云存储,可以直接使用七牛提供的 [qrsync](/v3/tools/qrsync/) 上传工具。
+文件上传有两种方式,一种是以普通方式直传文件,简称普通上传,另一种方式是断点续上传,断点续上传在网络条件很一般的情况下也能有出色的上传速度,而且对大文件的传输非常友好。
-<a name="rs-Publish"></a>
+<a name=io-upload></a>
+### 4.2.1 普通上传
+普通上传的接口在 `qiniu.io` 里,如下:
+
+直接上传二进制流
+```{python}
+import qiniu.config
-### 6. 发布公开资源
+qiniu.config.ACCESS_KEY = "<YOUR_APP_ACCESS_KEY>"
+qiniu.config.SECRET_KEY = "<YOUR_APP_SECRET_KEY>"
-使用七牛云存储提供的资源发布功能,您可以将一个资源表里边的所有文件以静态链接可访问的方式公开发布到您自己的域名下。
-要公开发布一个资源表里边的所有文件,只需调用改资源表对象的 Publish() 方法并传入 域名 作为参数即可。如下示例:
+import qiniu.io
- resp = rs.Publish(YOUR_DOMAIN)
+extra = qiniu.io.PutExtra(bucket_name)
+extra.mime_type = "text/plain"
-注意:需要到您的域名管理中心将 `YOUR_DOMAIN` CNAME 到 iovip.qbox.me
+ret, err = qiniu.io.put(uptoken, key, "hello!", extra)
+if err is not None:
+ error(err)
+ return
+```
-如果还没有您自己的域名,可将 YOUR_DOMAIN 改成 `<bucketName>.dn.qbox.me` 供临时测试使用。
+上传本地文件
-<a name="rs-Unpublish"></a>
+```{python}
+import qiniu.config
-### 7. 取消资源发布
+qiniu.config.ACCESS_KEY = "<YOUR_APP_ACCESS_KEY>"
+qiniu.config.SECRET_KEY = "<YOUR_APP_SECRET_KEY>"
-调用资源表对象的 Unpublish() 方法可取消该资源表内所有文件的静态外链。
+import qiniu.io
- resp = rs.Unpublish(YOUR_DOMAIN)
+localfile = "%s" % __file__
+extra = qiniu.io.PutExtra(bucket_name)
-<a name="rs-Delete"></a>
+ret, err = qiniu.io.put_file(uptoken, key, localfile, extra)
+if err is not None:
+ error(err)
+ return
+```
-### 8. 删除已上传的文件
+<a name=resumable-io-upload></a>
+### 4.2.2 断点续上传
+上传二进制流
+```{python}
+import qiniu.config
-要删除指定的文件,只需调用资源表对象的 Delete() 方法并传入 文件ID(key)作为参数即可。如下示例代码:
+qiniu.config.ACCESS_KEY = "<YOUR_APP_ACCESS_KEY>"
+qiniu.config.SECRET_KEY = "<YOUR_APP_SECRET_KEY>"
- resp = rs.Delete(key)
+import qiniu.resumable_io as rio
-<a name="rs-buckets"></a>
+class ResumableUpload(object):
+ position = 0
+ def __init__(self, string_data):
+ self.data = string_data
+
+ def read(self, length):
+ data = self.data[self.position: self.position+length]
+ self.position += length
+ return data
-### 9. 资源表管理
+a = "resumable upload string"
+extra = rio.PutExtra(bucket_name)
+extra.mime_type = "text/plain"
+ret, err = rio.put(uptoken, key, ResumableUpload(a), len(a), extra)
+if err is not None:
+ error(err)
+ return
+print ret,
+```
-<a name="rs-Buckets"></a>
+上传本地文件
+```{python}
+import qiniu.config
-#### 9.1 列出所有资源表
+qiniu.config.ACCESS_KEY = "<YOUR_APP_ACCESS_KEY>"
+qiniu.config.SECRET_KEY = "<YOUR_APP_SECRET_KEY>"
-可以通过 SDK 提供的 `Buckets` 列出所有 bucket(资源表)。
+import qiniu.resumable_io as rio
- resp = rs.Buckets()
+localfile = "%s" % __file__
+extra = rio.PutExtra(bucket_name)
-<a name="rs-Drop"></a>
+ret, err = rio.put_file(uptoken, key, localfile, extra)
+if err is not None:
+ error(err)
+ return
+print ret,
+```
+<a name=io-download></a>
+### 4.3 文件下载
+七牛云存储上的资源下载分为 公有资源下载 和 私有资源下载 。
-#### 9.2 删除整张资源表
+私有(private)是 Bucket(空间)的一个属性,一个私有 Bucket 中的资源为私有资源,私有资源不可匿名下载。
-要删除整个资源表及该表里边的所有文件,可以调用资源表对象的 Drop() 方法。
-需慎重,这会删除整个表及其所有文件。
+新创建的空间(Bucket)缺省为私有,也可以将某个 Bucket 设为公有,公有 Bucket 中的资源为公有资源,公有资源可以匿名下载。
- resp = rs.Drop()
+<a name=public-download></a>
+#### 4.3.1 公有资源下载
+如果在给bucket绑定了域名的话,可以通过以下地址访问。
+ [GET] http://<domain>/<key>
-<a name="rs-Batch"></a>
+其中<domain>可以到[七牛云存储开发者自助网站](https://portal.qiniu.com/)绑定, 域名可以使用自己一级域名的或者是由七牛提供的二级域名(`<bucket>.qiniudn.com`)。注意,尖括号不是必需,代表替换项。
-### 10. 资源表批量操作接口
+<a name=private-download></a>
+#### 4.3.2 私有资源下载
+私有资源必须通过临时下载授权凭证(downloadToken)下载,如下:
-通过指定的操作行为名称,以及传入的一组 keys,可以达到批量处理的功能。
+ [GET] http://<domain>/<key>?e=<deadline>&token=<downloadToken>
- resp = rs.Batch(actionNameString, keysList)
+注意,尖括号不是必需,代表替换项。
+私有下载链接可以使用 SDK 提供的如下方法生成:
-**示例**
+```{python}
+import qiniu.config
-批量获取文件属性信息:
+qiniu.config.ACCESS_KEY = "<YOUR_APP_ACCESS_KEY>"
+qiniu.config.SECRET_KEY = "<YOUR_APP_SECRET_KEY>"
- resp = rs.Batch('stat', [key1, key2, key3, ..., keyN])
+import qiniu.rs
-批量获取下载链接:
+base_url = qiniu.rs.make_base_url(domain, key)
+policy = qiniu.rs.GetPolicy()
+private_url = policy.make_request(base_url)
+```
- resp = rs.Batch('get', [key1, key2, key3, ..., keyN])
+<a name=fop-api></a>
+## 5. 数据处理接口
+七牛支持在云端对图像, 视频, 音频等富媒体进行个性化处理
-批量删除文件:
+<a name=fop-image></a>
+### 5.1 图像
+<a name=fop-image-info></a>
+### 5.1.1 查看图像属性
+```{python}
+import qiniu.config
- resp = rs.Batch('delete', [key1, key2, key3, ..., keyN])
+qiniu.config.ACCESS_KEY = "<YOUR_APP_ACCESS_KEY>"
+qiniu.config.SECRET_KEY = "<YOUR_APP_SECRET_KEY>"
-**响应**
+import qiniu.fop
+import qiniu.rs
- 200 OK [
- <Result1>, <Result2>, ...
- ]
- 298 Partial OK [
- <Result1>, <Result2>, ...
- ]
- <Result> 是 {
- code: <HttpCode>,
- data: <Data> 或 error: <ErrorMessage>
- }
+# 生成base_url
+url = qiniu.rs.make_base_url(domain, key2)
-当只有部分 keys 执行成功时,返回 298(PartialOK)。
+# 生成fop_url
+image_info = qiniu.fop.ImageInfo()
+url = image_info.make_request(url)
+# 对其签名,生成private_url。如果是公有bucket此步可以省略
+policy = qiniu.rs.GetPolicy()
+url = policy.make_request(url)
-<a name="rs-BatchGet"></a>
+print '可以在浏览器浏览: %s' % url
+```
-#### 10.1 批量下载
+<a name=fop-exif></a>
+### 5.1.2 查看图片EXIF信息
+```{python}
+import qiniu.config
-使用资源表对象的 `BatchGet` 方法可以批量取得下载链接:
+qiniu.config.ACCESS_KEY = "<YOUR_APP_ACCESS_KEY>"
+qiniu.config.SECRET_KEY = "<YOUR_APP_SECRET_KEY>"
- resp = rs.BatchGet(keysList)
+import qiniu.fop
+import qiniu.rs
-**示例**
+# 生成base_url
+url = qiniu.rs.make_base_url(domain, key2)
-批量获取下载链接:
+# 生成fop_url
+image_exif = qiniu.fop.Exif()
+url = image_exif.make_request(url)
- resp = rs.BatchGet([key1, key2, key3, ..., keyN])
+# 对其签名,生成private_url。如果是公有bucket此步可以省略
+policy = qiniu.rs.GetPolicy()
+url = policy.make_request(url)
-**响应**
+print '可以在浏览器浏览: %s' % url
+```
- 200 OK [
- <Result1>, <Result2>, ...
- ]
- 298 Partial OK [
- <Result1>, <Result2>, ...
- ]
- <Result> 是 {
- code: <HttpCode>,
- data: <Data> 或 error: <ErrorMessage>
- }
-当只有部分 keys 执行成功时,返回 298(PartialOK)。
+<a name=fop-image-view></a>
+### 5.1.3 生成图片预览
+```{python}
+import qiniu.config
+qiniu.config.ACCESS_KEY = "<YOUR_APP_ACCESS_KEY>"
+qiniu.config.SECRET_KEY = "<YOUR_APP_SECRET_KEY>"
-<a name="rs-BatchDelete"></a>
+import qiniu.fop
+import qiniu.rs
-#### 10.2 批量删除
+iv = qiniu.fop.ImageView()
+iv.width = 100
-使用资源表对象的 `BatchDelete` 方法可以批量删除指定文件:
+# 生成base_url
+url = qiniu.rs.make_base_url(domain, key2)
+# 生成fop_url
+url = iv.make_request(url)
+# 对其签名,生成private_url。如果是公有bucket此步可以省略
+policy = qiniu.rs.GetPolicy()
+url = policy.make_request(url)
+print '可以在浏览器浏览: %s' % url
+```
- resp = rs.BatchDelete(keysList)
+<a name=contribution></a>
+## 6. 贡献代码
-**示例**
+1. Fork
+2. 创建您的特性分支 (`git checkout -b my-new-feature`)
+3. 提交您的改动 (`git commit -am 'Added some feature'`)
+4. 将您的修改记录提交到远程 `git` 仓库 (`git push origin my-new-feature`)
+5. 然后到 github 网站的该 `git` 远程仓库的 `my-new-feature` 分支下发起 Pull Request
-批量删除指定文件:
+<a name=license></a>
+## 7. 许可证
- resp = rs.BatchDelete([key1, key2, key3, ..., keyN])
+Copyright (c) 2013 qiniu.com
-**响应**
+基于 MIT 协议发布:
- 200 OK [
- <Result1>, <Result2>, ...
- ]
- 298 Partial OK [
- <Result1>, <Result2>, ...
- ]
- <Result> 是 {
- code: <HttpCode>,
- data: <Data> 或 error: <ErrorMessage>
- }
+* [www.opensource.org/licenses/MIT](http://www.opensource.org/licenses/MIT)
-当只有部分 keys 执行成功时,返回 298(PartialOK)。
View
6 docs/gist/conf.py
@@ -0,0 +1,6 @@
+# @gist config
+import qiniu.config
+
+qiniu.config.ACCESS_KEY = "<YOUR_APP_ACCESS_KEY>"
+qiniu.config.SECRET_KEY = "<YOUR_APP_SECRET_KEY>"
+# @endgist
View
128 qbox/MultipartPostHandler.py
@@ -1,128 +0,0 @@
-#!/usr/bin/python
-
-####
-# 02/2006 Will Holcomb <wholcomb@gmail.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-"""
-Usage:
- Enables the use of multipart/form-data for posting forms
-
-Inspirations:
- Upload files in python:
- http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/146306
- urllib2_file:
- Fabien Seisen: <fabien@seisen.org>
-
-Example:
- import MultipartPostHandler, urllib2, cookielib
-
- cookies = cookielib.CookieJar()
- opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookies),
- MultipartPostHandler.MultipartPostHandler)
- params = { "username" : "bob", "password" : "riviera",
- "file" : open("filename", "rb") }
- opener.open("http://wwww.bobsite.com/upload/", params)
-
-Further Example:
- The main function of this file is a sample which downloads a page and
- then uploads it to the W3C validator.
-"""
-
-import urllib
-import urllib2
-import mimetools, mimetypes
-import os, stat
-
-class Callable:
- def __init__(self, anycallable):
- self.__call__ = anycallable
-
-class MultipartPostHandler(urllib2.BaseHandler):
- handler_order = urllib2.HTTPHandler.handler_order - 10 # needs to run first
-
- def http_request(self, request):
- data = request.get_data()
- if data is not None and type(data) != str:
- v_files = []
- v_vars = []
- try:
- for(key, value) in data.items():
- if type(value) == file:
- v_files.append((key, value))
- else:
- v_vars.append((key, value))
- except TypeError:
- systype, value, traceback = sys.exc_info()
- raise TypeError, "not a valid non-string sequence or mapping object", traceback
-
- if len(v_files) == 0:
- data = urllib.urlencode(v_vars)
- else:
- boundary, data = self.multipart_encode(v_vars, v_files)
- contenttype = 'multipart/form-data; boundary=%s' % boundary
- if request.has_header('Content-type'):
- pass # print "Replacing %s with %s" % (request.get_header('content-type'), contenttype)
- request.add_unredirected_header('Content-type', contenttype)
-
- request.add_data(data)
-
- return request
-
- def multipart_encode(vars, files, boundary = None, buffer = None):
- if boundary is None:
- boundary = mimetools.choose_boundary()
- if buffer is None:
- buffer = ''
- for(key, value) in vars:
- buffer += '--%s\r\n' % boundary
- buffer += 'Content-Disposition: form-data; name="%s"' % key
- buffer += '\r\n\r\n' + value + '\r\n'
- for(key, fd) in files:
- file_size = os.fstat(fd.fileno())[stat.ST_SIZE]
- filename = fd.name.split('/')[-1]
- contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
- buffer += '--%s\r\n' % boundary
- buffer += 'Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename)
- buffer += 'Content-Type: %s\r\n' % contenttype
- # buffer += 'Content-Length: %s\r\n' % file_size
- fd.seek(0)
- buffer += '\r\n' + fd.read() + '\r\n'
- buffer += '--%s--\r\n\r\n' % boundary
- return boundary, buffer
- multipart_encode = Callable(multipart_encode)
-
- https_request = http_request
-
-def main():
- import tempfile, sys
-
- validatorURL = "http://validator.w3.org/check"
- opener = urllib2.build_opener(MultipartPostHandler)
-
- def validateFile(url):
- temp = tempfile.mkstemp(suffix=".html")
- os.write(temp[0], opener.open(url).read())
- params = { "ss" : "0", # show source
- "doctype" : "Inline",
- "uploaded_file" : open(temp[1], "rb") }
- print opener.open(validatorURL, params).read()
- os.remove(temp[1])
-
- if len(sys.argv[1:]) > 0:
- for arg in sys.argv[1:]:
- validateFile(arg)
- else:
- validateFile("http://www.google.com")
-
-if __name__=="__main__":
- main()
View
17 qbox/config.py
@@ -1,17 +0,0 @@
-# -*- encoding: utf-8 -*-
-
-ACCESS_KEY = '<Please apply your access key>'
-SECRET_KEY = '<Dont send your secret key to anyone>'
-
-REDIRECT_URI = '<RedirectURL>'
-AUTHORIZATION_ENDPOINT = '<AuthURL>'
-TOKEN_ENDPOINT = 'https://acc.qbox.me/oauth2/token'
-
-
-PUT_TIMEOUT = 300000 # 300s = 5m
-
-IO_HOST = 'http://iovip.qbox.me'
-UP_HOST = 'http://up.qbox.me'
-RS_HOST = 'http://rs.qbox.me:10100'
-EU_HOST = 'http://eu.qbox.me'
-PUB_HOST = 'http://pu.qbox.me:10200'
View
89 qbox/digestoauth.py
@@ -1,89 +0,0 @@
-# -*- encoding: utf-8 -*-
-
-"""A simple client library to work with Digest Oauth APIs."""
-
-__author__ = 'stevenle08@gmail.com (Steven Le); xushiwei@qbox.net'
-
-
-import config
-import httplib2
-import hmac
-import urllib
-from urlparse import urlparse
-from hashlib import sha1
-from base64 import urlsafe_b64encode
-
-try:
- import json
-except ImportError:
- import simplejson as json
-
-
-class Error(Exception):
- pass
-
-
-class Client(object):
-
- def Call(self, url):
- headers = {}
-
- digest = self.CheckSum(url)
- token = "%s:%s" % (config.ACCESS_KEY,digest)
- headers['Authorization'] = 'QBox %s' % (token)
- resp, content = httplib2.Http('').request(url, 'POST', '', headers=headers)
-
- code = resp['status']
- if code != '200':
- raise Error('DigestOauthRequest.Call failed. Error was: %s %s' % (code, content))
- if len(content) != 0:
- return json.loads(content)
- return True
-
- def CallNoRet(self, url):
- headers = {}
- digest = self.CheckSum(url)
- token = "%s:%s" % (config.ACCESS_KEY,digest)
- headers['Authorization'] = 'QBox %s' % (token)
- resp, content = httplib2.Http('').request(url, 'POST', '', headers=headers)
-
- code = resp['status']
- if code != '200':
- raise Error('DigestOauthRequest.Call failed. Error was: %s %s' % (code, content))
- return True
-
- def CheckSum(self, url, params=None):
- parsedurl = urlparse(url)
- query = parsedurl.query
- path = parsedurl.path
- data = path
- if query != "":
- data = ''.join([data,'?',query])
- data = ''.join([data,"\n"])
-
- if params != None:
- data = ''.join([data,params])
-
- hashed = hmac.new(config.SECRET_KEY,data,sha1)
- return urlsafe_b64encode(hashed.digest())
-
- def CallWithForm(self, url, params):
- headers = {}
-
- msg = urllib.urlencode(params)
-
- digest = self.CheckSum(url, msg)
- token = "%s:%s" % (config.ACCESS_KEY,digest)
-
- headers['Authorization'] = 'QBox %s' % (token)
- headers["Content-Type"] = "application/x-www-form-urlencoded"
-
- resp, content = httplib2.Http('').request(url, 'POST', msg, headers=headers)
-
- code = resp['status']
- if code != '200':
- raise Error('DigestOauthRequest.Call failed. Error was: %s %s' % (code, content))
-
- if len(content) != 0:
- return json.loads(content)
- return True
View
25 qbox/eu.py
@@ -1,25 +0,0 @@
-# -*- encoding: utf-8 -*-
-
-import config
-
-class Service:
- """
- * End-user Settings Service
- * 终端用户配置项服务
- """
-
- def __init__(self, conn):
- self.Conn = conn
-
- def GetWatermark(self, customer):
- url = config.EU_HOST+"/wmget"
- params = {}
- params["customer"] = customer
- return self.Conn.CallWithForm(url, params)
-
- def SetWatermark(self, customer, tpl):
- url = config.EU_HOST+"/wmset"
- tpl["customer"] = customer
- ret = self.Conn.CallWithForm(url, tpl)
- tpl.pop("customer")
- return ret
View
47 qbox/eu_demo.py
@@ -1,47 +0,0 @@
-#!/usr/bin/env python
-
-import config
-import digestoauth
-import rscli
-import rs as qboxrs
-import uptoken
-import eu
-
-config.ACCESS_KEY = '<Please apply your access key>'
-config.SECRET_KEY = '<Dont send your secret key to anyone>'
-
-bucket = 'test_photos'
-key = 'test.jpg'
-customer = 'end_user_id'
-demo_domain = 'test_photos1.dn.qbox.me'
-
-client = digestoauth.Client()
-rs = qboxrs.Service(client, bucket)
-
-rs.SetProtected(1)
-rs.SetSeparator("-")
-rs.SetStyle("gsmall", "imageView/0/w/64/h/64/watermark/0")
-rs.SetStyle("gmiddle", "imageView/0/w/256/h/256/watermark/1")
-rs.SetStyle("glarge", "imageView/0/w/512/h/512/wartermark/1")
-
-wm = eu.Service(client)
-template = {"text":"hello", "dx":1, "dy":19, "bucket":bucket}
-resp = wm.SetWatermark(customer, template)
-print '\n===> SetWatermark %s result:' % customer
-print resp
-
-tokenObj = uptoken.UploadToken(bucket, 3600, "", "", customer)
-uploadToken = tokenObj.generate_token()
-print "Upload Token is: %s" % uploadToken
-
-resp = rscli.UploadFile(bucket, key, 'image/jpg', key, '', '', uploadToken)
-print '\n===> UploadFile %s result:' % key
-print resp
-
-resp = rs.Publish(demo_domain)
-print '\n===> Publish Domain %s result:' % demo_domain
-print resp
-
-resp = rs.Unpublish(demo_domain)
-print '\n===> Unpublish Domain %s result:' % demo_domain
-print resp
View
45 qbox/fileop.py
@@ -1,45 +0,0 @@
-# -*- encoding: utf-8 -*-
-
-from base64 import urlsafe_b64encode
-
-def MakeStyleURL(url, templPngFile, params, quality = 85):
- """
- * func MakeStyleURL(url string, templPngFile string, params string, quality int) => (urlMakeStyle string)
- """
- return url + '/makeStyle/' + urlsafe_b64encode(templPngFile) + '/params/' + urlsafe_b64encode(params) + '/quality/' + quality
-
-def ImagePreviewURL(url, thumbType):
- """
- * func ImagePreviewURL(url string, thumbType int) => (urlImagePreview string)
- """
- return url + '/imagePreview/' + thumbType
-
-def ImageMogrURL(url, params):
- """
- * func ImagePreviewURL(url string, thumbType int) => (urlImagePreview string)
- """
- return url + '/imageMogr/' + params
-
-def ImageInfoURL(url):
- """
- * func ImageInfoURL(url string) => (urlImageInfo string)
- """
- return url + '/imageInfo'
-
-def Image90x90URL(url):
- url2 = url + '/imageMogr/auto-orient/thumbnail/!90x90r/gravity/center/crop/90x90'
- print url2
- return url2
-
-def mkImageMogrifyParams(opts):
- keys = ["thumbnail", "gravity", "crop", "quality", "rotate", "format"]
- params_string = ""
- for key in keys:
- if opts.has_key(key) and opts[key] != None:
- params_string += "/" + str(key) + "/" + str(opts[key])
- if opts.has_key("auto_orient") and opts["auto_orient"] == True:
- params_string += "/auto-orient"
- return "imageMogr" + params_string
-
-def ImageMogrifyPreviewURL(src_img_url, opts):
- return src_img_url + "?" + mkImageMogrifyParams(opts)
View
58 qbox/fileop_demo.py
@@ -1,58 +0,0 @@
-#!/usr/bin/env python
-
-import config
-import urllib
-import simpleoauth2
-import fileop
-import rs as qboxrs
-import digestoauth
-import uptoken
-import rscli
-
-config.ACCESS_KEY = '<Please apply your access key>'
-config.SECRET_KEY = '<Dont send your secret key to anyone>'
-
-bucket = 'test_photos'
-key = 'test.jpg'
-targetKey = 'cropped-' + key
-
-
-tokenObj = uptoken.UploadToken(bucket, 3600)
-uploadToken = tokenObj.generate_token()
-print "Upload Token is: %s" % uploadToken
-
-resp = rscli.UploadFile(bucket, key, 'image/jpg', key, '', '', uploadToken)
-print '\n===> UploadFile %s result:' % key
-print resp
-
-
-client = digestoauth.Client()
-rs = qboxrs.Service(client, bucket)
-
-resp = rs.Get(key, key)
-print '\n===> Get %s result:' % key
-print resp
-
-urlImageInfo = fileop.ImageInfoURL(resp['url'])
-print "\n===> ImageInfo of %s:" % key
-print urllib.urlopen(urlImageInfo).read()
-
-urlImageSource = resp['url']
-opts = {
- "thumbnail":"!120x120r",
- "gravity":"center",
- "crop":"!120x120a0a0",
- "quality":85,
- "rotate":45,
- "format":"jpg",
- "auto_orient":True
-}
-
-mogrifyPreviewURL = fileop.ImageMogrifyPreviewURL(urlImageSource, opts)
-print "\n===> ImageMogrifyPreviewURL result:"
-print mogrifyPreviewURL
-
-imgrs = qboxrs.Service(client, "test_thumbnails_bucket")
-resp = imgrs.ImageMogrifyAs(targetKey, urlImageSource, opts)
-print "\n===> ImageMogrifyAs %s result:" % targetKey
-print resp
View
32 qbox/put_demo.py
@@ -1,32 +0,0 @@
-#!/usr/bin/env python
-
-import config
-import urllib
-import simpleoauth2
-import rs as qboxrs
-import rscli
-import digestoauth
-import uptoken
-
-config.ACCESS_KEY = '<Please apply your access key>'
-config.SECRET_KEY = '<Dont send your secret key to anyone>'
-
-bucket = 'test_photos'
-key = 'test.jpg'
-customer = 'end_user_id'
-demo_domain = 'test_photos.dn.qbox.me'
-
-tokenObj = uptoken.UploadToken(bucket, 3600, "", "", customer)
-uploadToken = tokenObj.generate_token()
-print "Upload Token is: %s" % uploadToken
-
-resp = rscli.UploadFile(bucket, key, 'image/jpg', key, '', '', uploadToken)
-print '\n===> UploadFile %s result:' % key
-print resp
-
-client = digestoauth.Client()
-rs = qboxrs.Service(client, bucket)
-
-resp = rs.Stat(key)
-print '\n===> Stat %s result:' % key
-print resp
View
159 qbox/rs.py
@@ -1,159 +0,0 @@
-# -*- encoding: utf-8 -*-
-
-import config
-import fileop
-from base64 import urlsafe_b64encode
-
-class Service:
- """
- * Qiniu Resource Storage (Key-Value) Service
- * 资源存储(键值对)。基本特性为:每个账户可创建多个表,每个表包含多个键值对(Key-Value对),Key是任意的字符串,Value是一个文件。
- """
-
- def __init__(self, conn, bucket = ''):
- self.Conn = conn
- self.BucketName = bucket
-
- def PutAuth(self):
- """
- * func PutAuth() => PutAuthRet
- * 上传授权(生成一个短期有效的可匿名上传URL)
- """
- url = config.IO_HOST + '/put-auth/'
- return self.Conn.Call(url)
-
- def PutAuthEx(self, expires, cbUrl):
- """
- * func PutAuthEx(expires, callbackUrl) => PutAuthRet
- * 上传授权(生成一个短期有效的可匿名上传URL)
- """
- url = config.IO_HOST + '/put-auth/' + str(expires) + '/callback/' + urlsafe_b64encode(cbUrl)
- return self.Conn.Call(url)
-
- def Get(self, key, attName):
- """
- * func Get(key string, attName string) => GetRet
- * 下载授权(生成一个短期有效的可匿名下载URL)
- """
- entryURI = self.BucketName + ':' + key
- url = config.RS_HOST + '/get/' + urlsafe_b64encode(entryURI) + '/attName/' + urlsafe_b64encode(attName)
- return self.Conn.Call(url)
-
- def GetIfNotModified(self, key, attName, base):
- """
- * func GetIfNotModified(key string, attName string, base string) => GetRet
- * 下载授权(生成一个短期有效的可匿名下载URL),如果服务端文件没被人修改的话(用于断点续传)
- """
- entryURI = self.BucketName + ':' + key
- url = config.RS_HOST + '/get/' + urlsafe_b64encode(entryURI) + '/attName/' + urlsafe_b64encode(attName) + '/base/' + base
- return self.Conn.Call(url)
-
- def Stat(self, key):
- """
- * func Stat(key string) => Entry
- * 查看资源属性
- """
- entryURI = self.BucketName + ':' + key
- url = config.RS_HOST + '/stat/' + urlsafe_b64encode(entryURI)
- return self.Conn.Call(url)
-
- def Publish(self, domain):
- """
- * func Publish(domain string) => Bool
- * 将本 Table 的内容作为静态资源发布。静态资源的url为:http://domain/key
- """
- url = config.RS_HOST + '/publish/' + urlsafe_b64encode(domain) + '/from/' + self.BucketName
- return self.Conn.CallNoRet(url)
-
- def Unpublish(self, domain):
- """
- * func Unpublish(domain string) => Bool
- * 取消发布
- """
- url = config.RS_HOST + '/unpublish/' + urlsafe_b64encode(domain)
- return self.Conn.CallNoRet(url)
-
- def Delete(self, key):
- """
- * func Delete(key string) => Bool
- * 删除资源
- """
- entryURI = self.BucketName + ':' + key
- url = config.RS_HOST + '/delete/' + urlsafe_b64encode(entryURI)
- return self.Conn.CallNoRet(url)
-
- def Mkbucket(self, bucketname):
- """
- * func Mkbucket(bucketname string) => Bool
- * 创建一个资源表
- """
- url = config.RS_HOST + '/mkbucket/' + bucketname
- return self.Conn.Call(url)
-
- def Buckets(self):
- """
- * func Buckets() => list
- * 列出所有资源表
- """
- url = config.RS_HOST + '/buckets'
- return self.Conn.Call(url)
-
- def Drop(self, bucketname):
- """
- * func Drop(bucketname string) => Bool
- * 删除一个资源表(慎用!)
- """
- url = config.RS_HOST + '/drop/' + bucketname
- return self.Conn.CallNoRet(url)
-
- def SetProtected(self, protectedMode):
- """
- * func SetProtected(protectedMode string) => Bool
- * 对 Bucket 设置保护,使资源本身不能被直接访问(只能访问被授权的经处理过的资源,比如打水印的图片)
- """
- url = config.PUB_HOST + '/accessMode/' + self.BucketName + '/mode/' + str(protectedMode)
- return self.Conn.Call(url)
-
- def SetSeparator(self, sep):
- """
- * func SetSeparator(sep string) => Bool
- * 设置分隔符
- """
- url = config.PUB_HOST + '/separator/' + self.BucketName + '/sep/' + urlsafe_b64encode(sep)
- return self.Conn.Call(url)
-
- def SetStyle(self, name, style):
- """
- * func SetStyle(name string, style string) => Bool
- * 设置缩略图规格别名
- """
- url = config.PUB_HOST + '/style/' + self.BucketName + '/name/' + urlsafe_b64encode(name) + '/style/' + urlsafe_b64encode(style)
- return self.Conn.Call(url)
-
- def UnsetStyle(self, name):
- """
- * func UnsetStyle(name string) => Bool
- * 删除指定缩略图规格
- """
- url = config.PUB_HOST + '/unstyle/' + self.BucketName + '/name/' + urlsafe_b64encode(name)
- return self.Conn.Call(url)
-
- def SaveAs(self, key, source_url, opWithParams):
- """
- * func SaveAs(target_key, source_url, opWithParams string) => Bool
- * 调用相关接口进行云处理并持久化存储处理结构
- """
- entryURI = self.BucketName + ":" + key
- saveAsEntryURI = urlsafe_b64encode(entryURI)
- saveAsParam = "/save-as/" + saveAsEntryURI
- newurl = source_url + "?" + opWithParams + saveAsParam
- return self.Conn.Call(newurl)
-
- def ImageMogrifyAs(self, key, source_img_url, opts):
- """
- * func ImageMogrifyAs(target_key string, source_img_url string, opts map) => Bool
- * 基于指定URL的原图生成缩略图并以指定的key持久化存储该缩略图
- """
- mogrifyParams = fileop.mkImageMogrifyParams(opts)
- return self.SaveAs(key, source_img_url, mogrifyParams)
-
View
70 qbox/rs_demo.py
@@ -1,70 +0,0 @@
-#!/usr/bin/env python
-
-import config
-import urllib
-import digestoauth
-import rs as qboxrs
-import rscli
-import config
-import uptoken
-
-config.ACCESS_KEY = '<Please apply your access key>'
-config.SECRET_KEY = '<Dont send your secret key to anyone>'
-
-DEMO_DOMAIN = 'test_photos2.dn.qbox.me'
-
-bucket = 'test_photos'
-newbucket = "new_test_bucket"
-key = 'test.jpg'
-customer = 'end_user_id'
-
-tokenObj = uptoken.UploadToken(bucket, 3600, "", "", customer)
-uploadToken = tokenObj.generate_token()
-print "Upload Token is: %s" % uploadToken
-
-resp = rscli.UploadFile(bucket, key, 'image/jpg', key, '', '', uploadToken)
-print '\n===> UploadFile %s result:' % key
-print resp
-
-
-client = digestoauth.Client()
-rs = qboxrs.Service(client, bucket)
-
-resp = rs.Publish(DEMO_DOMAIN)
-print '\n===> Publish result:'
-print resp
-
-resp = rs.Stat(key)
-print '\n===> Stat %s result:' % key
-print resp
-
-resp = rs.Get(key, key)
-print '\n===> Get %s result:' % key
-print resp
-
-resp = rs.GetIfNotModified(key, key, resp['hash'])
-print '\n===> GetIfNotModified %s result:' % key
-print resp
-
-# print '\n===> Display %s contents:' % key
-# print urllib.urlopen(resp['url']).read()
-
-resp = rs.Delete(key)
-print '\n===> Delete %s result:' % key
-print resp
-
-resp = rs.Mkbucket(newbucket)
-print '\n===> Mkucket %s result:' % newbucket
-print resp
-
-resp = rs.Buckets()
-print '\n===> Buckets result:'
-print resp
-
-resp = rs.Drop(newbucket)
-print '\n===> Drop %s result:' % newbucket
-print resp
-
-resp = rs.Unpublish(DEMO_DOMAIN)
-print '\n===> Unpublish Domain %s result:' % DEMO_DOMAIN
-print resp
View
40 qbox/rscli.py
@@ -1,40 +0,0 @@
-# -*- encoding: utf-8 -*-
-
-import MultipartPostHandler
-import urllib
-import urllib2
-from base64 import urlsafe_b64encode
-import config
-
-def PutFile(url, bucket, key, mimeType, localFile, customMeta = '', callbackParams = ''):
- if mimeType == '':
- mimeType = 'application/octet-stream'
- entryURI = bucket + ':' + key
- action = '/rs-put/' + urlsafe_b64encode(entryURI) + '/mimeType/' + urlsafe_b64encode(mimeType)
- if customMeta != '':
- action += '/meta/' + urlsafe_b64encode(customMeta)
- params = {'file' : file(localFile, 'rb'), 'action': action}
- if callbackParams != '':
- if isinstance(callbackParams, dict):
- callbackParams = urllib.urlencode(callbackParams)
- params['params'] = callbackParams
- opener = urllib2.build_opener(MultipartPostHandler.MultipartPostHandler)
- return opener.open(url, params).read()
-
-def UploadFile(bucket, key, mimeType, localFile, customMeta = '', callbackParams = '', upToken = ''):
- if mimeType == '':
- mimeType = 'application/octet-stream'
- entryURI = bucket + ':' + key
- action = '/rs-put/' + urlsafe_b64encode(entryURI) + '/mimeType/' + urlsafe_b64encode(mimeType)
- if customMeta != '':
- action += '/meta/' + urlsafe_b64encode(customMeta)
- params = {'action': action, 'file' : file(localFile, 'rb')}
- if callbackParams != '':
- if isinstance(callbackParams, dict):
- callbackParams = urllib.urlencode(callbackParams)
- params['params'] = callbackParams
- if upToken != '':
- params['auth'] = upToken
- url = config.UP_HOST + "/upload"
- opener = urllib2.build_opener(MultipartPostHandler.MultipartPostHandler)
- return opener.open(url, params).read()
View
163 qbox/simpleoauth2.py
@@ -1,163 +0,0 @@
-# -*- encoding: utf-8 -*-
-
-"""A simple client library to work with OAuth 2.0 APIs."""
-
-__author__ = 'stevenle08@gmail.com (Steven Le); xushiwei@qbox.net'
-
-
-import config
-import urllib
-import httplib2
-try:
- import json
-except ImportError:
- import simplejson as json
-
-
-class Error(Exception):
- pass
-
-
-class Client(object):
- """The base class for OAuth 2.0 clients.
-
- Attributes:
- auth_url: The OAuth 2.0 endpoint to redirect users to to authorize access.
- token_url: The OAuth 2.0 endpoint to request and refresh tokens.
- client_id: The client ID issued by the OAuth 2.0 service.
- client_secret: The client secret issued by the OAuth 2.0 service.
- token: OAuth 2.0 token.
- """
-
- def __init__(self, auth_url=config.AUTHORIZATION_ENDPOINT, token_url=config.TOKEN_ENDPOINT, client_id="a75604760c4da4caaa456c0c5895c061c3065c5a", client_secret="75df554a39f58accb7eb293b550fa59618674b7d"):
- self.auth_url = auth_url
- self.token_url = token_url
- self.client_id = client_id
- self.client_secret = client_secret
-
- def CreateAuthUrl(self, scope, redirect_uri, state=None):
- """Creates a authorization URL.
-
- Args:
- scope: A scope or list of scopes identifying the service to be accessed.
- redirect_uri: The URL on your site that will handle OAuth responses after
- the user takes an action on the dialog.
- state: A string used to maintain state between the request and redirect.
- This value will be appended to the redirect_uri after the user takes
- an action on the OAuth dialog.
- Returns:
- A URL to that can be used to redirect users to authorize access to a
- service.
- """
- if hasattr(scope, '__iter__'):
- # Multiple scopes.
- scope = ' '.join(scope)
-
- params = {
- 'client_id': self.client_id,