update vendor

This commit is contained in:
deepzz0
2017-07-11 23:50:01 +08:00
parent e1ec5cd08a
commit c18d9c0bef
107 changed files with 8347 additions and 126 deletions

435
vendor/github.com/qiniu/api.v7/kodo/bucket.go generated vendored Normal file
View File

@@ -0,0 +1,435 @@
package kodo
import (
. "context"
"encoding/base64"
"fmt"
"io"
"net/url"
"strconv"
"github.com/qiniu/api.v7/api"
"github.com/qiniu/x/log.v7"
)
// ----------------------------------------------------------
// Batch 批量操作。
//
func (p *Client) Batch(ctx Context, ret interface{}, op []string) (err error) {
return p.CallWithForm(ctx, ret, "POST", p.RSHost+"/batch", map[string][]string{"op": op})
}
// ----------------------------------------------------------
type Bucket struct {
api.BucketInfo
Conn *Client
Name string
}
// Buckets 获取所有地区的所有空间(bucket)
//
// shared 是否获取所有授权获得空间true为包含授权空间
//
func (p *Client) Buckets(ctx Context, shared bool) (buckets []string, err error) {
if shared {
err = p.Call(ctx, &buckets, "POST", p.RSHost+"/buckets?shared=trye")
} else {
err = p.Call(ctx, &buckets, "POST", p.RSHost+"/buckets")
}
return
}
// Bucket 取七牛空间bucket的对象实例。
//
// name 是创建该七牛空间bucket时采用的名称。
//
func (p *Client) Bucket(name string) Bucket {
b, err := p.BucketWithSafe(name)
if err != nil {
log.Errorf("Bucket(%s) failed: %+v", name, err)
}
return b
}
// BucketWithSafe 确认空间存在并获取七牛空间bucket的对象实例。
func (p *Client) BucketWithSafe(name string) (Bucket, error) {
var info api.BucketInfo
if len(p.UpHosts) == 0 {
var err error
info, err = p.apiCli.GetBucketInfo(p.mac.AccessKey, name)
if err != nil {
return Bucket{}, err
}
} else {
info.IoHost = p.IoHost
info.UpHosts = p.UpHosts
}
return Bucket{info, p, name}, nil
}
// Entry 资源元信息
type Entry struct {
Hash string `json:"hash"`
Fsize int64 `json:"fsize"`
PutTime int64 `json:"putTime"`
MimeType string `json:"mimeType"`
Type int `json:"type"`
EndUser string `json:"endUser"`
}
// Stat 取文件属性。
//
// ctx 是请求的上下文。
// key 是要访问的文件的访问路径。
//
func (p Bucket) Stat(ctx Context, key string) (entry Entry, err error) {
err = p.Conn.Call(ctx, &entry, "POST", p.Conn.RSHost+URIStat(p.Name, key))
return
}
// Delete 删除一个文件。
//
// ctx 是请求的上下文。
// key 是要删除的文件的访问路径。
//
func (p Bucket) Delete(ctx Context, key string) (err error) {
return p.Conn.Call(ctx, nil, "POST", p.Conn.RSHost+URIDelete(p.Name, key))
}
// Move 移动一个文件。
//
// ctx 是请求的上下文。
// keySrc 是要移动的文件的旧路径。
// keyDest 是要移动的文件的新路径。
//
func (p Bucket) Move(ctx Context, keySrc, keyDest string) (err error) {
return p.Conn.Call(ctx, nil, "POST", p.Conn.RSHost+URIMove(p.Name, keySrc, p.Name, keyDest))
}
// MoveEx 跨空间bucket移动一个文件。
//
// ctx 是请求的上下文。
// keySrc 是要移动的文件的旧路径。
// bucketDest 是文件的目标空间。
// keyDest 是要移动的文件的新路径。
//
func (p Bucket) MoveEx(ctx Context, keySrc, bucketDest, keyDest string) (err error) {
return p.Conn.Call(ctx, nil, "POST", p.Conn.RSHost+URIMove(p.Name, keySrc, bucketDest, keyDest))
}
// Copy 复制一个文件。
//
// ctx 是请求的上下文。
// keySrc 是要复制的文件的源路径。
// keyDest 是要复制的文件的目标路径。
//
func (p Bucket) Copy(ctx Context, keySrc, keyDest string) (err error) {
return p.Conn.Call(ctx, nil, "POST", p.Conn.RSHost+URICopy(p.Name, keySrc, p.Name, keyDest))
}
// ChangeMime 修改文件的MIME类型。
//
// ctx 是请求的上下文。
// key 是要修改的文件的访问路径。
// mime 是要设置的新MIME类型。
//
func (p Bucket) ChangeMime(ctx Context, key, mime string) (err error) {
return p.Conn.Call(ctx, nil, "POST", p.Conn.RSHost+URIChangeMime(p.Name, key, mime))
}
// ChangeType 修改文件的存储类型。
//
// ctx 是请求的上下文。
// key 是要修改的文件的访问路径。
// fileType 是要设置的新存储类型。0 表示标准存储1 表示低频存储。
//
func (p Bucket) ChangeType(ctx Context, key string, fileType int) (err error) {
return p.Conn.Call(ctx, nil, "POST", p.Conn.RSHost+URIChangeType(p.Name, key, fileType))
}
// Fetch 从网上抓取一个资源并存储到七牛空间bucket中。
//
// ctx 是请求的上下文。
// key 是要存储的文件的访问路径。如果文件已经存在则覆盖。
// url 是要抓取的资源的URL。
//
func (p Bucket) Fetch(ctx Context, key string, url string) (err error) {
return p.Conn.Call(ctx, nil, "POST", p.IoHost+uriFetch(p.Name, key, url))
}
// DeleteAfterDays 更新文件生命周期
//
// ctx 是请求的上下文。
// key 是要更新的文件的访问路径。
// deleteAfterDays 设置为0表示取消 lifecycle
//
func (p Bucket) DeleteAfterDays(ctx Context, key string, days int) (err error) {
return p.Conn.Call(ctx, nil, "POST", p.Conn.RSHost+URIDeleteAfterDays(p.Name, key, days))
}
// Image 设置镜像源
//
// srcSiteURL 镜像源的访问域名。必须设置为形如 `http://source.com/` 或 `http://114.114.114.114/` 的字符串
// host 回源时使用的 Host 头部值
//
// 镜像源地址支持两种格式:
// 格式 1`http(s)://绑定域名/源站资源相对路径`
// 格式 2`http(s)://绑定 IP/源站资源相对路径`
//
func (p Bucket) Image(ctx Context, srcSiteURL, host string) (err error) {
return p.Conn.Call(ctx, nil, "POST", "http://pu.qbox.me:10200"+URIImage(p.Name, srcSiteURL, host))
}
// UnImage 取消镜像源
//
func (p Bucket) UnImage(ctx Context) (err error) {
return p.Conn.Call(ctx, nil, "POST", "http://pu.qbox.me:10200"+URIUnImage(p.Name))
}
// Prefetch 镜像资源更新
//
// key 被抓取资源名称
//
func (p Bucket) Prefetch(ctx Context, key string) (err error) {
return p.Conn.Call(ctx, nil, "POST", p.Conn.IoHost+URIPrefetch(p.Name, key))
}
// PfopResult pfop返回信息
type PfopResult struct {
PersistentID string `json:"persistentId,omitempty"`
}
// FopRet 持久化云处理结果
type FopRet struct {
ID string `json:"id"`
Code int `json:"code"`
Desc string `json:"desc"`
InputBucket string `json:"inputBucket,omitempty"`
InputKey string `json:"inputKey,omitempty"`
Pipeline string `json:"pipeline,omitempty"`
Reqid string `json:"reqid,omitempty"`
Items []FopResult
}
// FopResult 云处理操作列表,包含每个云处理操作的状态信息
type FopResult struct {
Cmd string `json:"cmd"`
Code int `json:"code"`
Desc string `json:"desc"`
Error string `json:"error,omitempty"`
Hash string `json:"hash,omitempty"`
Key string `json:"key,omitempty"`
Keys []string `json:"keys,omitempty"`
}
// Pfop 持久化数据处理
//
// bucket 资源空间
// key 源资源名
// fops 云处理操作列表,用`;``分隔,如:`avthumb/flv;saveas/cWJ1Y2tldDpxa2V5`是将上传的视频文件转码成flv格式后存储为 qbucket:qkey ,其中 cWJ1Y2tldDpxa2V5 是 qbucket:qkey 的URL安全的Base64编码结果。
// notifyURL 处理结果通知接收 URL七牛将会向你设置的 URL 发起 Content-Type: application/json 的 POST 请求。
// pipeline 为空则表示使用公用队列,处理速度比较慢。建议指定私有队列,转码的时候使用独立的计算资源。
// force 强制执行数据处理。当服务端发现 fops 指定的数据处理结果已经存在,那就认为已经处理成功,避免重复处理浪费资源。本字段设为 `true`,则可强制执行数据处理并覆盖原结果。
//
func (p *Client) Pfop(ctx Context, bucket, key, fops, notifyURL, pipeline string, force bool) (persistentID string, err error) {
pfopParams := map[string][]string{
"bucket": []string{bucket},
"key": []string{key},
"fops": []string{fops},
}
if notifyURL != "" {
pfopParams["notifyURL"] = []string{notifyURL}
}
if pipeline != "" {
pfopParams["pipeline"] = []string{pipeline}
}
if force {
pfopParams["force"] = []string{"1"}
}
var ret PfopResult
err = p.CallWithForm(ctx, &ret, "POST", "http://api.qiniu.com/pfop/", pfopParams)
if err != nil {
return
}
persistentID = ret.PersistentID
return
}
// Prefop 持久化处理状态查询
func (p *Client) Prefop(ctx Context, persistentID string) (ret FopRet, err error) {
err = p.Call(ctx, &ret, "GET", "http://api.qiniu.com/status/get/prefop?id="+persistentID)
return
}
// ----------------------------------------------------------
// ListItem List借口返回结果
type ListItem struct {
Key string `json:"key"`
Hash string `json:"hash"`
Fsize int64 `json:"fsize"`
PutTime int64 `json:"putTime"`
MimeType string `json:"mimeType"`
EndUser string `json:"endUser"`
}
// List 首次请求,请将 marker 设置为 ""。
// 无论 err 值如何,均应该先看 entries 是否有内容。
// 如果后续没有更多数据err 返回 EOFmarkerOut 返回 ""(但不通过该特征来判断是否结束)。
//
func (p Bucket) List(
ctx Context, prefix, delimiter, marker string, limit int) (entries []ListItem, commonPrefixes []string, markerOut string, err error) {
listUrl := p.makeListURL(prefix, delimiter, marker, limit)
var listRet struct {
Marker string `json:"marker"`
Items []ListItem `json:"items"`
Prefixes []string `json:"commonPrefixes"`
}
err = p.Conn.Call(ctx, &listRet, "POST", listUrl)
if err != nil {
return
}
if listRet.Marker == "" {
return listRet.Items, listRet.Prefixes, "", io.EOF
}
return listRet.Items, listRet.Prefixes, listRet.Marker, nil
}
func (p Bucket) makeListURL(prefix, delimiter, marker string, limit int) string {
query := make(url.Values)
query.Add("bucket", p.Name)
if prefix != "" {
query.Add("prefix", prefix)
}
if delimiter != "" {
query.Add("delimiter", delimiter)
}
if marker != "" {
query.Add("marker", marker)
}
if limit > 0 {
query.Add("limit", strconv.FormatInt(int64(limit), 10))
}
return p.Conn.RSFHost + "/list?" + query.Encode()
}
// ----------------------------------------------------------
type BatchStatItemRet struct {
Data Entry `json:"data"`
Error string `json:"error"`
Code int `json:"code"`
}
// BatchStat 批量取文件属性
func (p Bucket) BatchStat(ctx Context, keys ...string) (ret []BatchStatItemRet, err error) {
b := make([]string, len(keys))
for i, key := range keys {
b[i] = URIStat(p.Name, key)
}
err = p.Conn.Batch(ctx, &ret, b)
return
}
type BatchItemRet struct {
Error string `json:"error"`
Code int `json:"code"`
}
// BatchDelete 批量删除
func (p Bucket) BatchDelete(ctx Context, keys ...string) (ret []BatchItemRet, err error) {
b := make([]string, len(keys))
for i, key := range keys {
b[i] = URIDelete(p.Name, key)
}
err = p.Conn.Batch(ctx, &ret, b)
return
}
type KeyPair struct {
Src string
Dest string
}
// BatchMove 批量移动文件
func (p Bucket) BatchMove(ctx Context, entries ...KeyPair) (ret []BatchItemRet, err error) {
b := make([]string, len(entries))
for i, e := range entries {
b[i] = URIMove(p.Name, e.Src, p.Name, e.Dest)
}
err = p.Conn.Batch(ctx, &ret, b)
return
}
// BatchCopy 批量复制文件
func (p Bucket) BatchCopy(ctx Context, entries ...KeyPair) (ret []BatchItemRet, err error) {
b := make([]string, len(entries))
for i, e := range entries {
b[i] = URICopy(p.Name, e.Src, p.Name, e.Dest)
}
err = p.Conn.Batch(ctx, &ret, b)
return
}
// ----------------------------------------------------------
func encodeURI(uri string) string {
return base64.URLEncoding.EncodeToString([]byte(uri))
}
func uriFetch(bucket, key, url string) string {
return "/fetch/" + encodeURI(url) + "/to/" + encodeURI(bucket+":"+key)
}
func URIDelete(bucket, key string) string {
return "/delete/" + encodeURI(bucket+":"+key)
}
func URIStat(bucket, key string) string {
return "/stat/" + encodeURI(bucket+":"+key)
}
func URICopy(bucketSrc, keySrc, bucketDest, keyDest string) string {
return "/copy/" + encodeURI(bucketSrc+":"+keySrc) + "/" + encodeURI(bucketDest+":"+keyDest)
}
func URIMove(bucketSrc, keySrc, bucketDest, keyDest string) string {
return "/move/" + encodeURI(bucketSrc+":"+keySrc) + "/" + encodeURI(bucketDest+":"+keyDest)
}
func URIChangeMime(bucket, key, mime string) string {
return "/chgm/" + encodeURI(bucket+":"+key) + "/mime/" + encodeURI(mime)
}
func URIChangeType(bucket, key string, fileType int) string {
return "/chtype/" + encodeURI(bucket+":"+key) + "/type/" + strconv.Itoa(fileType)
}
func URIDeleteAfterDays(bucket, key string, days int) string {
return fmt.Sprintf("/deleteAfterDays/%s/%d", encodeURI(bucket+":"+key), days)
}
func URIImage(bucket, srcSiteURL, host string) string {
return fmt.Sprintf("/image/%s/from/%s/host/%s", bucket, encodeURI(srcSiteURL), encodeURI(host))
}
func URIUnImage(bucket string) string {
return fmt.Sprintf("/unimage/%s", bucket)
}
func URIPrefetch(bucket, key string) string {
return fmt.Sprintf("/prefetch/%s", encodeURI(bucket+":"+key))
}
// ----------------------------------------------------------

167
vendor/github.com/qiniu/api.v7/kodo/bucket_test.go generated vendored Normal file
View File

@@ -0,0 +1,167 @@
package kodo
import (
"context"
"math/rand"
"strconv"
"testing"
"time"
)
var (
batchTestKey = "abatch"
batchTestNewKey1 = "abatch/newkey1"
batchTestNewKey2 = "abatch/newkey2"
)
func init() {
if skipTest() {
return
}
rand.Seed(time.Now().UnixNano())
batchTestKey += strconv.Itoa(rand.Int())
batchTestNewKey1 += strconv.Itoa(rand.Int())
batchTestNewKey2 += strconv.Itoa(rand.Int())
// 删除 可能存在的 key
bucket.BatchDelete(nil, batchTestKey, batchTestNewKey1, batchTestNewKey2)
}
func TestAll(t *testing.T) {
if skipTest() {
return
}
//上传一个文件用用于测试
err := upFile("bucket_test.go", batchTestKey)
if err != nil {
t.Fatal(err)
}
defer bucket.Delete(nil, batchTestKey)
testBatchStat(t)
testBatchCopy(t)
testBatchMove(t)
testBatchDelete(t)
testBatch(t)
testClient_MakeUptokenBucket(t)
testDeleteAfterDays(t)
}
func testBatchStat(t *testing.T) {
rets, err := bucket.BatchStat(nil, batchTestKey, batchTestKey, batchTestKey)
if err != nil {
t.Fatal("bucket.BatchStat failed:", err)
}
if len(rets) != 3 {
t.Fatal("BatchStat failed: len(rets) = ", 3)
}
stat, err := bucket.Stat(nil, batchTestKey)
if err != nil {
t.Fatal("bucket.Stat failed:", err)
}
if rets[0].Data != stat || rets[1].Data != stat || rets[2].Data != stat {
t.Fatal("BatchStat failed : returns err")
}
}
func testBatchMove(t *testing.T) {
stat0, err := bucket.Stat(nil, batchTestKey)
if err != nil {
t.Fatal("BathMove get stat failed:", err)
}
_, err = bucket.BatchMove(nil, KeyPair{batchTestKey, batchTestNewKey1}, KeyPair{batchTestNewKey1, batchTestNewKey2})
if err != nil {
t.Fatal("bucket.BatchMove failed:", err)
}
defer bucket.Move(nil, batchTestNewKey2, batchTestKey)
stat1, err := bucket.Stat(nil, batchTestNewKey2)
if err != nil {
t.Fatal("BathMove get stat failed:", err)
}
if stat0.Hash != stat1.Hash {
t.Fatal("BatchMove failed : Move err", stat0, stat1)
}
}
func testBatchCopy(t *testing.T) {
_, err := bucket.BatchCopy(nil, KeyPair{batchTestKey, batchTestNewKey1}, KeyPair{batchTestKey, batchTestNewKey2})
if err != nil {
t.Fatal(err)
}
defer bucket.Delete(nil, batchTestNewKey1)
defer bucket.Delete(nil, batchTestNewKey2)
stat0, _ := bucket.Stat(nil, batchTestKey)
stat1, _ := bucket.Stat(nil, batchTestNewKey1)
stat2, _ := bucket.Stat(nil, batchTestNewKey2)
if stat0.Hash != stat1.Hash || stat0.Hash != stat2.Hash {
t.Fatal("BatchCopy failed : Copy err")
}
}
func testBatchDelete(t *testing.T) {
bucket.Copy(nil, batchTestKey, batchTestNewKey1)
bucket.Copy(nil, batchTestKey, batchTestNewKey2)
_, err := bucket.BatchDelete(nil, batchTestNewKey1, batchTestNewKey2)
if err != nil {
t.Fatal(err)
}
_, err1 := bucket.Stat(nil, batchTestNewKey1)
_, err2 := bucket.Stat(nil, batchTestNewKey2)
//这里 err1 != nil否则文件没被成功删除
if err1 == nil || err2 == nil {
t.Fatal("BatchDelete failed : File do not delete")
}
}
func testBatch(t *testing.T) {
ops := []string{
URICopy(bucketName, batchTestKey, bucketName, batchTestNewKey1),
URIDelete(bucketName, batchTestKey),
URIMove(bucketName, batchTestNewKey1, bucketName, batchTestKey),
}
var rets []BatchItemRet
err := client.Batch(nil, &rets, ops)
if err != nil {
t.Fatal(err)
}
if len(rets) != 3 {
t.Fatal("len(rets) != 3")
}
}
func testDeleteAfterDays(t *testing.T) {
ctx := context.Background()
err := bucket.DeleteAfterDays(ctx, batchTestNewKey1, 5)
if err == nil {
t.Fatal("Expect an error")
}
bucket.Copy(ctx, batchTestKey, batchTestNewKey1)
err = bucket.DeleteAfterDays(ctx, batchTestNewKey1, 5)
if err != nil {
t.Fatal(err)
}
}

52
vendor/github.com/qiniu/api.v7/kodo/doc.go generated vendored Normal file
View File

@@ -0,0 +1,52 @@
/*
包 github.com/qiniu/api.v7/kodo 提供了在您的业务服务器(服务端)调用七牛云存储服务的能力
首先,我们要配置下 AccessKey/SecretKey这可以在七牛 Portal 中查到:
kodo.SetMac("your-access-key", "your-secret-key")
然后我们创建一个 Client 对象:
zone := kodo.ZoneZ0 // 您空间(Bucket)所在的区域
c := kodo.New(zone, nil) // 用默认配置创建 Client
有了 Client你就可以操作您的空间(Bucket)了,比如我们要上传一个文件:
import "golang.org/x/net/context"
bucket := c.Bucket("your-bucket-name")
ctx := context.Background()
...
localFile := "/your/local/image/file.jpg"
err := bucket.PutFile(ctx, nil, "foo/bar.jpg", localFile, nil)
if err != nil {
... // 上传文件失败处理
return
}
// 上传文件成功
// 这时登录七牛Portal在 your-bucket-name 空间就可以看到一个 foo/bar.jpg 的文件了
当然,除了上传文件,各种空间(Bucket)相关的操作都可以有,最常见自然是增删改查了:
entry, err := bucket.Stat(ctx, "foo/bar.jpg") // 看看空间中是否存在某个文件,其属性是什么
bucket.Delete(ctx, "foo/bar.jpg") // 删除空间中的某个文件
bucket.ChangeMime(ctx, "foo/bar.jpg", "image/jpeg") // 修改某个文件的 MIME 属性
bucket.Move(ctx, "foo/bar.jpg", "new-name.jpg") // 移动文件
bucket.Copy(ctx, "foo/bar.jpg", "new-copy-file.jpg") // 复制文件
等等... 请问怎么下载文件?如果是公开文件,我们只需要:
import "net/http"
domain := "domain-of-your-bucket.com" // 您的空间绑定的域名这个可以在七牛的Portal中查到
baseUrl := kodo.MakeBaseUrl(domain, "foo/bar.jpg") // 得到下载 url
resp, err := http.Get(baseUrl)
...
但是对于私有空间,事情要复杂一些,访问上面的 baseUrl 会被拒绝。我们需要多做一步:
privateUrl := c.MakePrivateUrl(baseUrl, nil) // 用默认的下载策略去生成私有下载的 url
resp, err := http.Get(privateUrl)
...
*/
package kodo

154
vendor/github.com/qiniu/api.v7/kodo/main.go generated vendored Normal file
View File

@@ -0,0 +1,154 @@
package kodo
import (
"net/http"
"github.com/qiniu/api.v7/api"
"github.com/qiniu/api.v7/auth/qbox"
"github.com/qiniu/api.v7/conf"
"github.com/qiniu/x/rpc.v7"
)
// ----------------------------------------------------------
type zoneConfig struct {
IoHost string
UpHosts []string
}
const (
// ZoneZ0 华东机房
ZoneZ0 = iota
// ZoneZ1 华北机房
ZoneZ1
// ZoneZ2 华南机房
ZoneZ2
// ZoneNa0 北美机房
ZoneNa0
)
var zones = []zoneConfig{
// z0 华东机房:
{
IoHost: "http://iovip.qbox.me",
UpHosts: []string{
"http://up.qiniu.com",
"http://upload.qiniu.com",
"-H up.qiniu.com http://183.136.139.16",
},
},
// z1 华北机房:
{
IoHost: "http://iovip-z1.qbox.me",
UpHosts: []string{
"http://up-z1.qiniu.com",
"http://upload-z1.qiniu.com",
"-H up-z1.qiniu.com http://106.38.227.27",
},
},
// z2 华南机房:
{
IoHost: "http://iovip-z2.qbox.me",
UpHosts: []string{
"http://up-z2.qiniu.com",
"http://upload-z2.qiniu.com",
},
},
// na0 北美机房:
{
IoHost: "http://iovip-na0.qbox.me",
UpHosts: []string{
"http://up-na0.qiniu.com",
"http://upload-na0.qiniu.com",
},
},
}
const (
defaultRsHost = "http://rs.qbox.me"
defaultRsfHost = "http://rsf.qbox.me"
)
// ----------------------------------------------------------
type Config struct {
AccessKey string
SecretKey string
RSHost string
RSFHost string
APIHost string
Scheme string
IoHost string
UpHosts []string
Transport http.RoundTripper
}
// ----------------------------------------------------------
type Client struct {
rpc.Client
mac *qbox.Mac
Config
apiCli *api.Client
}
func New(zone int, cfg *Config) (p *Client) {
p = new(Client)
if cfg != nil {
p.Config = *cfg
}
p.mac = qbox.NewMac(p.AccessKey, p.SecretKey)
p.Client = rpc.Client{qbox.NewClient(p.mac, p.Transport)}
if p.RSHost == "" {
p.RSHost = defaultRsHost
}
if p.RSFHost == "" {
p.RSFHost = defaultRsfHost
}
if p.Scheme != "https" {
p.Scheme = "http"
}
if p.APIHost == "" {
p.APIHost = api.DefaultApiHost
}
p.apiCli = api.NewClient(p.APIHost, p.Scheme)
if zone < 0 || zone >= len(zones) {
return
}
if len(p.UpHosts) == 0 {
p.UpHosts = zones[zone].UpHosts
}
if p.IoHost == "" {
p.IoHost = zones[zone].IoHost
}
return
}
func NewWithoutZone(cfg *Config) (p *Client) {
return New(-1, cfg)
}
// ----------------------------------------------------------
// 设置全局默认的 ACCESS_KEY, SECRET_KEY 变量。
//
func SetMac(accessKey, secretKey string) {
conf.ACCESS_KEY, conf.SECRET_KEY = accessKey, secretKey
}
// ----------------------------------------------------------
// 设置使用这个SDK的应用程序名。userApp 必须满足 [A-Za-z0-9_\ \-\.]*
//
func SetAppName(userApp string) error {
return conf.SetAppName(userApp)
}
// ----------------------------------------------------------

151
vendor/github.com/qiniu/api.v7/kodo/main_test.go generated vendored Normal file
View File

@@ -0,0 +1,151 @@
package kodo
import (
"fmt"
"math/rand"
"os"
"strconv"
"testing"
"time"
)
var (
key = "aa"
keyFetch = "afetch"
newkey1 = "bbbb"
newkey2 = "cccc"
fetchURL = "http://www-static.u.qiniucdn.com/public/v1645/img/css-sprite.png"
bucketName string
domain string
client *Client
bucket = newBucket()
QINIU_KODO_TEST string
)
func init() {
if skipTest() {
return
}
rand.Seed(time.Now().UnixNano())
key += strconv.Itoa(rand.Int())
keyFetch += strconv.Itoa(rand.Int())
newkey1 += strconv.Itoa(rand.Int())
newkey2 += strconv.Itoa(rand.Int())
bucket.BatchDelete(nil, key, keyFetch, newkey1, newkey2)
}
func newBucket() (bucket Bucket) {
QINIU_KODO_TEST = os.Getenv("QINIU_KODO_TEST")
if skipTest() {
println("[INFO] QINIU_KODO_TEST: skipping to test github.com/qiniu/api.v7")
return
}
ak := os.Getenv("QINIU_ACCESS_KEY")
sk := os.Getenv("QINIU_SECRET_KEY")
if ak == "" || sk == "" {
panic("require ACCESS_KEY & SECRET_KEY")
}
SetMac(ak, sk)
bucketName = os.Getenv("QINIU_TEST_BUCKET")
domain = os.Getenv("QINIU_TEST_DOMAIN")
if bucketName == "" || domain == "" {
panic("require test env")
}
client = NewWithoutZone(nil)
return client.Bucket(bucketName)
}
func skipTest() bool {
return QINIU_KODO_TEST == ""
}
func upFile(localFile, key string) error {
return bucket.PutFile(nil, nil, key, localFile, nil)
}
func TestFetch(t *testing.T) {
if skipTest() {
return
}
err := bucket.Fetch(nil, keyFetch, fetchURL)
if err != nil {
t.Fatal("bucket.Fetch failed:", err)
}
entry, err := bucket.Stat(nil, keyFetch)
if err != nil || entry.MimeType != "image/png" {
t.Fatal("bucket.Fetch: Stat failed -", err, "entry:", entry)
}
fmt.Println(entry)
}
func TestEntry(t *testing.T) {
if skipTest() {
return
}
//上传一个文件用用于测试
err := upFile("doc.go", key)
if err != nil {
t.Fatal(err)
}
defer bucket.Delete(nil, key)
einfo, err := bucket.Stat(nil, key)
if err != nil {
t.Fatal(err)
}
mime := "text/plain"
err = bucket.ChangeMime(nil, key, mime)
if err != nil {
t.Fatal(err)
}
einfo, err = bucket.Stat(nil, key)
if err != nil {
t.Fatal(err)
}
if einfo.MimeType != mime {
t.Fatal("mime type did not change")
}
err = bucket.Copy(nil, key, newkey1)
if err != nil {
t.Fatal(err)
}
enewinfo, err := bucket.Stat(nil, newkey1)
if err != nil {
t.Fatal(err)
}
if einfo.Hash != enewinfo.Hash {
t.Fatal("invalid entryinfo:", einfo, enewinfo)
}
err = bucket.Move(nil, newkey1, newkey2)
if err != nil {
t.Fatal(err)
}
enewinfo2, err := bucket.Stat(nil, newkey2)
if err != nil {
t.Fatal(err)
}
if enewinfo.Hash != enewinfo2.Hash {
t.Fatal("invalid entryinfo:", enewinfo, enewinfo2)
}
err = bucket.Delete(nil, newkey2)
if err != nil {
t.Fatal(err)
}
}

119
vendor/github.com/qiniu/api.v7/kodo/token.go generated vendored Normal file
View File

@@ -0,0 +1,119 @@
package kodo
import (
"encoding/json"
"fmt"
"strconv"
"strings"
"time"
"github.com/qiniu/api.v7/api"
"github.com/qiniu/api.v7/auth/qbox"
"github.com/qiniu/x/url.v7"
)
// ----------------------------------------------------------
// 根据空间(Bucket)的域名,以及文件的 key获得 baseUrl。
// 如果空间是 public 的,那么通过 baseUrl 可以直接下载文件内容。
// 如果空间是 private 的,那么需要对 baseUrl 进行私有签名得到一个临时有效的 privateUrl 进行下载。
//
func MakeBaseUrl(domain, key string) (baseUrl string) {
return "http://" + domain + "/" + url.Escape(key)
}
// ----------------------------------------------------------
type GetPolicy struct {
Expires uint32
}
func (p *Client) MakePrivateUrl(baseUrl string, policy *GetPolicy) (privateUrl string) {
var expires int64
if policy == nil || policy.Expires == 0 {
expires = 3600
} else {
expires = int64(policy.Expires)
}
deadline := time.Now().Unix() + expires
if strings.Contains(baseUrl, "?") {
baseUrl += "&e="
} else {
baseUrl += "?e="
}
baseUrl += strconv.FormatInt(deadline, 10)
token := qbox.Sign(p.mac, []byte(baseUrl))
return baseUrl + "&token=" + token
}
// --------------------------------------------------------------------------------
type PutPolicy struct {
Scope string `json:"scope"`
Expires uint32 `json:"deadline"` // 截止时间(以秒为单位)
InsertOnly uint16 `json:"insertOnly,omitempty"` // 若非0, 即使Scope为 Bucket:Key 的形式也是insert only
DetectMime uint8 `json:"detectMime,omitempty"` // 若非0, 则服务端根据内容自动确定 MimeType
CallbackFetchKey uint8 `json:"callbackFetchKey,omitempty"`
FsizeLimit int64 `json:"fsizeLimit,omitempty"`
MimeLimit string `json:"mimeLimit,omitempty"`
SaveKey string `json:"saveKey,omitempty"`
CallbackUrl string `json:"callbackUrl,omitempty"`
CallbackHost string `json:"callbackHost,omitempty"`
CallbackBody string `json:"callbackBody,omitempty"`
CallbackBodyType string `json:"callbackBodyType,omitempty"`
ReturnUrl string `json:"returnUrl,omitempty"`
ReturnBody string `json:"returnBody,omitempty"`
PersistentOps string `json:"persistentOps,omitempty"`
PersistentNotifyUrl string `json:"persistentNotifyUrl,omitempty"`
PersistentPipeline string `json:"persistentPipeline,omitempty"`
AsyncOps string `json:"asyncOps,omitempty"`
EndUser string `json:"endUser,omitempty"`
Checksum string `json:"checksum,omitempty"` // 格式:<HashName>:<HexHashValue>,目前支持 MD5/SHA1。
UpHosts []string `json:"uphosts,omitempty"`
DeleteAfterDays int `json:"deleteAfterDays,omitempty"`
FileType int `json:"fileType,omitempty"`
}
func (p *Client) MakeUptoken(policy *PutPolicy) string {
token, err := p.MakeUptokenWithSafe(policy)
if err != nil {
fmt.Errorf("makeuptoken failed: policy: %+v, error: %+v", policy, err)
}
return token
}
func (p *Client) MakeUptokenWithSafe(policy *PutPolicy) (token string, err error) {
var rr = *policy
if len(rr.UpHosts) == 0 {
bucketName := getBucketNameFromPutPolicy(policy)
bucketInfo, err1 := p.GetBucketInfo(bucketName)
if err1 != nil {
err = err1
return
}
rr.UpHosts = bucketInfo.UpHosts
}
if rr.Expires == 0 {
rr.Expires = 3600
}
rr.Expires += uint32(time.Now().Unix())
b, _ := json.Marshal(&rr)
token = qbox.SignWithData(p.mac, b)
return
}
func getBucketNameFromPutPolicy(policy *PutPolicy) (bucketName string) {
scope := policy.Scope
bucketName = strings.Split(scope, ":")[0]
return
}
func (p *Client) GetBucketInfo(bucketName string) (bucketInfo api.BucketInfo, err error) {
return p.apiCli.GetBucketInfo(p.mac.AccessKey, bucketName)
}
// ----------------------------------------------------------

70
vendor/github.com/qiniu/api.v7/kodo/token_test.go generated vendored Normal file
View File

@@ -0,0 +1,70 @@
package kodo
import (
"crypto/sha1"
"encoding/base64"
"io"
"net/http"
"testing"
)
func init() {
if skipTest() {
return
}
// 删除 可能存在的 key
bucket.BatchDelete(nil, key)
}
func TestGetPrivateUrl(t *testing.T) {
if skipTest() {
return
}
// 上传一个文件用用于测试
err := upFile("token.go", key)
if err != nil {
t.Fatal(err)
}
defer bucket.Delete(nil, key)
baseUrl := MakeBaseUrl(domain, key)
privateUrl := client.MakePrivateUrl(baseUrl, nil)
resp, err := http.Get(privateUrl)
if err != nil {
t.Fatal("http.Get failed:", err)
}
defer resp.Body.Close()
h := sha1.New()
io.Copy(h, resp.Body)
etagExpected := base64.URLEncoding.EncodeToString(h.Sum([]byte{'\x16'}))
etag := resp.Header.Get("Etag")
if etag[1:len(etag)-1] != etagExpected {
t.Fatal("http.Get etag failed:", etag, etagExpected)
}
}
func testClient_MakeUptokenBucket(t *testing.T) {
c := New(0, nil)
token := c.MakeUptoken(&PutPolicy{
Scope: "gosdk",
Expires: 3600,
})
if token == "" {
t.Fatal("nil token")
}
token, err := c.MakeUptokenWithSafe(&PutPolicy{
Scope: "NotExistBucket",
Expires: 3600,
})
if err == nil {
t.Fatal("make up token fail")
}
}

179
vendor/github.com/qiniu/api.v7/kodo/upload.go generated vendored Normal file
View File

@@ -0,0 +1,179 @@
package kodo
import (
. "context"
"io"
"net/http"
"github.com/qiniu/api.v7/kodocli"
"github.com/qiniu/x/rpc.v7"
)
type PutExtra kodocli.PutExtra
type RputExtra kodocli.RputExtra
type PutRet kodocli.PutRet
// ----------------------------------------------------------
func (p Bucket) makeUptoken(key string) string {
policy := &PutPolicy{
Scope: p.Name + ":" + key,
Expires: 3600,
UpHosts: p.UpHosts,
}
return p.Conn.MakeUptoken(policy)
}
func (p Bucket) makeUptokenWithoutKey() string {
policy := &PutPolicy{
Scope: p.Name,
Expires: 3600,
UpHosts: p.UpHosts,
}
return p.Conn.MakeUptoken(policy)
}
func (p Bucket) makeUploader() kodocli.Uploader {
c := &http.Client{Transport: p.Conn.Transport}
return kodocli.Uploader{Conn: rpc.Client{c}, UpHosts: p.UpHosts, ApiCli: p.Conn.apiCli}
}
// ----------------------------------------------------------
// Put 上传一个文件。
//
// ctx 是请求的上下文。
// ret 是上传成功后返回的数据。返回的是 PutRet 结构。可选,可以传 nil 表示不感兴趣。
// key 是要上传的文件访问路径。比如:"foo/bar.jpg"。注意我们建议 key 不要以 '/' 开头。另外key 为空字符串是合法的。
// data 是文件内容的访问接口io.Reader
// fsize 是要上传的文件大小。
// extra 是上传的一些可选项。详细见 PutExtra 结构的描述。
//
func (p Bucket) Put(
ctx Context, ret interface{}, key string, data io.Reader, size int64, extra *PutExtra) error {
uploader := p.makeUploader()
uptoken := p.makeUptoken(key)
return uploader.Put(ctx, ret, uptoken, key, data, size, (*kodocli.PutExtra)(extra))
}
// PutWithoutKey 上传一个文件。自动以文件的 hash 作为文件的访问路径key
//
// ctx 是请求的上下文。
// ret 是上传成功后返回的数据。返回的是 PutRet 结构。可选,可以传 nil 表示不感兴趣。
// data 是文件内容的访问接口io.Reader
// fsize 是要上传的文件大小。
// extra 是上传的一些可选项。详细见 PutExtra 结构的描述。
//
func (p Bucket) PutWithoutKey(
ctx Context, ret interface{}, data io.Reader, size int64, extra *PutExtra) error {
uploader := p.makeUploader()
uptoken := p.makeUptokenWithoutKey()
return uploader.PutWithoutKey(ctx, ret, uptoken, data, size, (*kodocli.PutExtra)(extra))
}
// PutFile 上传一个文件。
// 和 Put 不同的只是一个通过提供文件路径来访问文件内容,一个通过 io.Reader 来访问。
//
// ctx 是请求的上下文。
// ret 是上传成功后返回的数据。返回的是 PutRet 结构。可选,可以传 nil 表示不感兴趣。
// localFile 是要上传的文件的本地路径。
// extra 是上传的一些可选项。详细见 PutExtra 结构的描述。
//
func (p Bucket) PutFile(
ctx Context, ret interface{}, key, localFile string, extra *PutExtra) (err error) {
uploader := p.makeUploader()
uptoken := p.makeUptoken(key)
return uploader.PutFile(ctx, ret, uptoken, key, localFile, (*kodocli.PutExtra)(extra))
}
// PutFileWithoutKey 上传一个文件。自动以文件的 hash 作为文件的访问路径key
// 和 PutWithoutKey 不同的只是一个通过提供文件路径来访问文件内容,一个通过 io.Reader 来访问。
//
// ctx 是请求的上下文。
// ret 是上传成功后返回的数据。返回的是 PutRet 结构。可选,可以传 nil 表示不感兴趣。
// localFile 是要上传的文件的本地路径。
// extra 是上传的一些可选项。详细见 PutExtra 结构的描述。
//
func (p Bucket) PutFileWithoutKey(
ctx Context, ret interface{}, localFile string, extra *PutExtra) (err error) {
uploader := p.makeUploader()
uptoken := p.makeUptokenWithoutKey()
return uploader.PutFileWithoutKey(ctx, ret, uptoken, localFile, (*kodocli.PutExtra)(extra))
}
// ----------------------------------------------------------
// Rput 上传一个文件,支持断点续传和分块上传。
//
// ctx 是请求的上下文。
// ret 是上传成功后返回的数据。如果 uptoken 中没有设置 CallbackUrl 或 ReturnBody那么返回的数据结构是 PutRet 结构。
// key 是要上传的文件访问路径。比如:"foo/bar.jpg"。注意我们建议 key 不要以 '/' 开头。另外key 为空字符串是合法的。
// data 是文件内容的访问接口。考虑到需要支持分块上传和断点续传,要的是 io.ReaderAt 接口,而不是 io.Reader。
// fsize 是要上传的文件大小。
// extra 是上传的一些可选项。详细见 RputExtra 结构的描述。
//
func (p Bucket) Rput(
ctx Context, ret interface{}, key string, data io.ReaderAt, size int64, extra *RputExtra) error {
uploader := p.makeUploader()
uptoken := p.makeUptoken(key)
return uploader.Rput(ctx, ret, uptoken, key, data, size, (*kodocli.RputExtra)(extra))
}
// RputWithoutKey 上传一个文件,支持断点续传和分块上传。自动以文件的 hash 作为文件的访问路径key
//
// ctx 是请求的上下文。
// ret 是上传成功后返回的数据。如果 uptoken 中没有设置 CallbackUrl 或 ReturnBody那么返回的数据结构是 PutRet 结构。
// data 是文件内容的访问接口。考虑到需要支持分块上传和断点续传,要的是 io.ReaderAt 接口,而不是 io.Reader。
// fsize 是要上传的文件大小。
// extra 是上传的一些可选项。详细见 RputExtra 结构的描述。
//
func (p Bucket) RputWithoutKey(
ctx Context, ret interface{}, data io.ReaderAt, size int64, extra *RputExtra) error {
uploader := p.makeUploader()
uptoken := p.makeUptokenWithoutKey()
return uploader.RputWithoutKey(ctx, ret, uptoken, data, size, (*kodocli.RputExtra)(extra))
}
// RputFile 上传一个文件,支持断点续传和分块上传。
// 和 Rput 不同的只是一个通过提供文件路径来访问文件内容,一个通过 io.ReaderAt 来访问。
//
// ctx 是请求的上下文。
// ret 是上传成功后返回的数据。如果 uptoken 中没有设置 CallbackUrl 或 ReturnBody那么返回的数据结构是 PutRet 结构。
// key 是要上传的文件访问路径。比如:"foo/bar.jpg"。注意我们建议 key 不要以 '/' 开头。另外key 为空字符串是合法的。
// localFile 是要上传的文件的本地路径。
// extra 是上传的一些可选项。详细见 RputExtra 结构的描述。
//
func (p Bucket) RputFile(
ctx Context, ret interface{}, key, localFile string, extra *RputExtra) (err error) {
uploader := p.makeUploader()
uptoken := p.makeUptoken(key)
return uploader.RputFile(ctx, ret, uptoken, key, localFile, (*kodocli.RputExtra)(extra))
}
// RputFileWithoutKey 上传一个文件,支持断点续传和分块上传。自动以文件的 hash 作为文件的访问路径key
// 和 RputWithoutKey 不同的只是一个通过提供文件路径来访问文件内容,一个通过 io.ReaderAt 来访问。
//
// ctx 是请求的上下文。
// ret 是上传成功后返回的数据。如果 uptoken 中没有设置 CallbackUrl 或 ReturnBody那么返回的数据结构是 PutRet 结构。
// localFile 是要上传的文件的本地路径。
// extra 是上传的一些可选项。详细见 RputExtra 结构的描述。
//
func (p Bucket) RputFileWithoutKey(
ctx Context, ret interface{}, localFile string, extra *RputExtra) (err error) {
uploader := p.makeUploader()
uptoken := p.makeUptokenWithoutKey()
return uploader.RputFileWithoutKey(ctx, ret, uptoken, localFile, (*kodocli.RputExtra)(extra))
}
// ----------------------------------------------------------