对象存储

  • 对象存储 > SDK 下载 > Go SDK

    Go SDK

    最近更新时间: 2024-11-21 17:23:27

    简介

    此 SDK 适用于 Go 1.10.0 及以上版本。使用此 SDK 构建您的网络应用程序,能让您以非常便捷的方式将数据安全地存储到七牛云上。无论您的网络应用是一个网站程序,还是包括从云端(服务端程序)到终端(手持设备应用)的架构服务和应用,通过七牛云及其 SDK,都能让您应用程序的终端用户高速上传和下载,同时也让您的服务端更加轻盈。

    Go SDK 属于七牛服务端 SDK 之一,主要有如下功能:

    1. 提供生成客户端上传所需的上传凭证的功能
    2. 提供文件从服务端直接上传七牛的功能
    3. 提供对七牛空间中文件进行管理的功能
    4. 提供对七牛空间中文件进行处理的功能
    5. 提供七牛 CDN 相关的刷新,预取,日志功能
    6. 提供七牛视频监控 QVS 的功能

    开源

    安装

    推荐使用 go mod,github.com/qiniu/go-sdk/v7,如 require github.com/qiniu/go-sdk/v7 v7.22.0

    鉴权

    Go SDK 的所有的功能,都需要合法的授权。授权凭证的签算需要七牛账号下的一对有效的Access KeySecret Key,这对密钥可以通过如下步骤获得:

    1. 点击注册 🔗开通七牛开发者帐号
    2. 如果已有账号,直接登录七牛开发者后台,点击这里 🔗查看 Access Key 和 Secret Key

    文件上传

    上传流程

    文件上传分为客户端上传(主要是指网页端和移动端等面向终端用户的场景)和服务端上传两种场景,具体可以参考文档业务流程

    服务端 SDK 在上传方面主要提供两种功能,一种是生成客户端上传所需要的上传凭证,另外一种是直接上传文件到云端。

    客户端上传凭证

    客户端(移动端或者 Web 端)上传文件的时候,需要从客户自己的业务服务器获取上传凭证,而这些上传凭证是通过服务端的 SDK 来生成的,然后通过客户自己的业务 API 分发给客户端使用。根据上传的业务需求不同,七牛云 Go SDK 支持丰富的上传凭证生成方式。

    简单上传的凭证

    最简单的上传凭证只需要 AccessKeySecretKeyBucket 就可以。

    import (
        "context"
        "time"
        "github.com/qiniu/go-sdk/v7/storagev2/credentials"
        "github.com/qiniu/go-sdk/v7/storagev2/uptoken"
    )
    
    accessKey := "your access key"
    secretKey := "your secret key"
    mac := credentials.NewCredentials(accessKey, secretKey)
    
    bucket := "your bucket name"
    putPolicy, err := uptoken.NewPutPolicy(bucket, time.Now().Add(1 * time.Hour))
    if err != nil {
        return err
    }
    upToken, err := uptoken.NewSigner(putPolicy, mac).GetUpToken(context.Background())
    if err != nil {
        return err
    }
    

    覆盖上传的凭证

    覆盖上传除了需要简单上传所需要的信息之外,还需要想进行覆盖的文件名称,这个文件名称同时可是客户端上传代码中指定的文件名,两者必须一致。

    import (
        "context"
        "time"
        "github.com/qiniu/go-sdk/v7/storagev2/credentials"
        "github.com/qiniu/go-sdk/v7/storagev2/uptoken"
    )
    
    accessKey := "your access key"
    secretKey := "your secret key"
    mac := credentials.NewCredentials(accessKey, secretKey)
    
    bucket := "your bucket name"
    // 需要覆盖的文件名
    keyToOverwrite := "qiniu.mp4"
    putPolicy, err := uptoken.NewPutPolicyWithKey(bucket, keyToOverwrite, time.Now().Add(1 * time.Hour))
    if err != nil {
        return err
    }
    upToken, err := uptoken.NewSigner(putPolicy, mac).GetUpToken(context.Background())
    if err != nil {
        return err
    }
    

    自定义上传回复的凭证

    默认情况下,文件上传到七牛之后,在没有设置returnBody或者回调相关的参数情况下,七牛返回给上传端的回复格式为hashkey,例如:

    { "hash": "Ftgm-CkWePC9fzMBTRNmPMhGBcSV", "key": "qiniu.jpg" }
    

    有时候我们希望能自定义这个返回的 JSON 格式的内容,可以通过设置returnBody参数来实现,在returnBody中,我们可以使用七牛支持的魔法变量自定义变量

    import (
        "context"
        "time"
        "github.com/qiniu/go-sdk/v7/storagev2/credentials"
        "github.com/qiniu/go-sdk/v7/storagev2/uptoken"
    )
    
    accessKey := "your access key"
    secretKey := "your secret key"
    mac := credentials.NewCredentials(accessKey, secretKey)
    
    bucket := "your bucket name"
    putPolicy, err := uptoken.NewPutPolicy(bucket, time.Now().Add(1 * time.Hour))
    if err != nil {
        return err
    }
    putPolicy.SetReturnBody(`{"key":"$(key)","hash":"$(etag)","fsize":$(fsize),"bucket":"$(bucket)","name":"$(x:name)"}`)
    upToken, err := uptoken.NewSigner(putPolicy, mac).GetUpToken(context.Background())
    if err != nil {
        return err
    }
    

    则文件上传到七牛之后,收到的回复内容格式如下:

    {
      "key": "github-x.png",
      "hash": "FqKXVdTvIx_mPjOYdjDyUSy_H1jr",
      "fsize": 6091,
      "bucket": "if-pbl",
      "name": "github logo"
    }
    

    对于上面的自定义返回值,我们需要自定义结构体来解析这个回复,例如下面提供了一个解析结果的方法:

    import (
        "context"
        "time"
        "github.com/qiniu/go-sdk/v7/storagev2/credentials"
        "github.com/qiniu/go-sdk/v7/storagev2/uptoken"
        "github.com/qiniu/go-sdk/v7/storagev2/uploader"
    )
    
    // 自定义返回值结构体
    type MyPutRet struct {
        Key    string
        Hash   string
        Fsize  int
        Bucket string
        Name   string
    }
    
    accessKey := "your access key"
    secretKey := "your secret key"
    mac := credentials.NewCredentials(accessKey, secretKey)
    
    localFile := "your local file path"
    bucket := "your bucket name"
    key := "your file save key"
    
    putPolicy, err := uptoken.NewPutPolicy(bucket, time.Now().Add(1 * time.Hour))
    if err != nil {
        return err
    }
    putPolicy.SetReturnBody(`{"key":"$(key)","hash":"$(etag)","fsize":$(fsize),"bucket":"$(bucket)","name":"$(x:name)"}`)
    
    ret := MyPutRet{}
    uploadManager := uploader.NewUploadManager(&uploader.UploadManagerOptions{})
    err = uploadManager.UploadFile(context.Background(), localFile, &uploader.ObjectOptions{
        UpToken: uptoken.NewSigner(putPolicy, mac),
        ObjectName: &key,
        CustomVars: map[string]string{
            "name": "github logo",
        },
    }, &ret)
    if err != nil {
        return err
    }
    fmt.Println(ret.Bucket, ret.Key, ret.Fsize, ret.Hash, ret.Name)
    

    带回调业务服务器的凭证

    上面生成的自定义上传回复的上传凭证适用于上传端(无论是客户端还是服务端)和七牛服务器之间进行直接交互的情况下。在客户端上传的场景之下,有时候客户端需要在文件上传到七牛之后,从业务服务器获取相关的信息,这个时候就要用到七牛的上传回调及相关回调参数的设置。

    import (
        "context"
        "time"
        "github.com/qiniu/go-sdk/v7/storagev2/credentials"
        "github.com/qiniu/go-sdk/v7/storagev2/uptoken"
    )
    
    accessKey := "your access key"
    secretKey := "your secret key"
    mac := credentials.NewCredentials(accessKey, secretKey)
    
    bucket := "your bucket name"
    putPolicy, err := uptoken.NewPutPolicy(bucket, time.Now().Add(1 * time.Hour))
    if err != nil {
        return err
    }
    putPolicy.SetCallbackUrl("http://api.example.com/qiniu/upload/callback").SetCallbackBody(`{"key":"$(key)","hash":"$(etag)","fsize":$(fsize),"bucket":"$(bucket)","name":"$(x:name)"}`).SetCallbackBodyType("application/json")
    upToken, err := uptoken.NewSigner(putPolicy, mac).GetUpToken(context.Background())
    if err != nil {
        return err
    }
    

    在使用了上传回调的情况下,客户端收到的回复就是业务服务器响应七牛的 JSON 格式内容,业务服务器收到回调之后必须响应 JSON 格式的回复給七牛,这个回复会被七牛传递给客户端。
    例如上面的 CallbackBody 的设置会在文件上传到七牛之后,触发七牛回调如下内容給业务服务器:

    {
      "key": "github-x.png",
      "hash": "FqKXVdTvIx_mPjOYdjDyUSy_H1jr",
      "fsize": 6091,
      "bucket": "if-pbl",
      "name": "github logo"
    }
    

    通常情况下,我们建议使用application/json格式来设置callbackBody,保持数据格式的统一性。实际情况下,callbackBody也支持application/x-www-form-urlencoded格式来组织内容,这个主要看业务服务器在接收到callbackBody的内容时如何解析。例如:

    import (
        "context"
        "time"
        "github.com/qiniu/go-sdk/v7/storagev2/credentials"
        "github.com/qiniu/go-sdk/v7/storagev2/uptoken"
    )
    
    accessKey := "your access key"
    secretKey := "your secret key"
    mac := credentials.NewCredentials(accessKey, secretKey)
    
    bucket := "your bucket name"
    putPolicy, err := uptoken.NewPutPolicy(bucket, time.Now().Add(1 * time.Hour))
    if err != nil {
        return err
    }
    putPolicy.SetCallbackUrl("http://api.example.com/qiniu/upload/callback").SetCallbackBody(`key=$(key)&hash=$(etag)&bucket=$(bucket)&fsize=$(fsize)&name=$(x:name)`)
    upToken, err := uptoken.NewSigner(putPolicy, mac).GetUpToken(context.Background())
    if err != nil {
        return err
    }
    

    带数据处理的凭证

    七牛支持在文件上传到七牛之后,立即对其进行多种指令的数据处理,这个只需要在生成的上传凭证中指定相关的处理参数即可。

    import (
        "context"
        "time"
        "github.com/qiniu/go-sdk/v7/storagev2/credentials"
        "github.com/qiniu/go-sdk/v7/storagev2/uptoken"
    )
    
    accessKey := "your access key"
    secretKey := "your secret key"
    mac := credentials.NewCredentials(accessKey, secretKey)
    
    bucket := "your bucket name"
    putPolicy, err := uptoken.NewPutPolicy(bucket, time.Now().Add(1 * time.Hour))
    if err != nil {
        return err
    }
    
    saveMp4Entry := base64.URLEncoding.EncodeToString([]byte(bucket + ":avthumb_test_target.mp4"))
    saveJpgEntry := base64.URLEncoding.EncodeToString([]byte(bucket + ":vframe_test_target.jpg"))
    //数据处理指令,支持多个指令
    avthumbMp4Fop := "avthumb/mp4|saveas/" + saveMp4Entry
    vframeJpgFop := "vframe/jpg/offset/1|saveas/" + saveJpgEntry
    
    //连接多个操作指令
    persistentOps := strings.Join([]string{avthumbMp4Fop, vframeJpgFop}, ";")
    pipeline := "test"
    persistentType := int64(0)
    putPolicy.SetPersistentOps(persistentOps).SetPersistentNotifyUrl("http://api.example.com/qiniu/pfop/notify").SetPersistentPipeline(pipeline).SetPersistentType(persistentType)
    upToken, err := uptoken.NewSigner(putPolicy, mac).GetUpToken(context.Background())
    if err != nil {
        return err
    }
    

    也可以支持使用工作流模版替代数据处理指令。工作流模板是预先编排好的一系列媒体处理流程(如转码、截图、视频拼接等各类处理),登录 对象存储控制台 进行创建,详情参考工作流模板操作指南persistentWorkflowTemplateID 对应工作流模板列表的名称字段

    import (
        "context"
        "time"
        "github.com/qiniu/go-sdk/v7/storagev2/credentials"
        "github.com/qiniu/go-sdk/v7/storagev2/uptoken"
    )
    
    accessKey := "your access key"
    secretKey := "your secret key"
    mac := credentials.NewCredentials(accessKey, secretKey)
    
    bucket := "your bucket name"
    putPolicy, err := uptoken.NewPutPolicy(bucket, time.Now().Add(1 * time.Hour))
    if err != nil {
        return err
    }
    
    workflowTemplateID := "tempname"
    pipeline := "test"
    persistentType := int64(0)
    putPolicy.SetPersistentWorkflowTemplateID(workflowTemplateID).SetPersistentNotifyUrl("http://api.example.com/qiniu/pfop/notify").SetPersistentPipeline(pipeline).SetPersistentType(persistentType)
    upToken, err := uptoken.NewSigner(putPolicy, mac).GetUpToken(context.Background())
    if err != nil {
        return err
    }
    

    带自定义参数的凭证

    存储支持客户端上传文件的时候定义一些自定义参数,这些参数可以在returnBodycallbackBody里面和七牛内置支持的魔法变量(即系统变量)通过相同的方式来引用。这些自定义的参数名称必须以x:开头。例如客户端上传的时候指定了自定义的参数x:namex:age分别是stringint类型。那么可以通过下面的方式引用:

    putPolicy.SetReturnBody(`{"key":"$(key)","hash":"$(etag)","fsize":$(fsize),"bucket":"$(bucket)","name":"$(x:name)","age":$(x:age)}`)
    

    或者

    putPolicy.SetCallbackBody(`{"key":"$(key)","hash":"$(etag)","fsize":$(fsize),"bucket":"$(bucket)","name":"$(x:name)","age":$(x:age)}`)
    

    综合上传凭证

    上面的生成上传凭证的方法,都是通过设置上传策略 🔗相关的参数来支持的,这些参数可以通过不同的组合方式来满足不同的业务需求,可以灵活地组织你所需要的上传凭证。

    服务端直传

    服务端直传是指客户利用七牛服务端 SDK 从服务端直接上传文件到七牛云,交互的双方一般都在机房里面,所以服务端可以自己生成上传凭证,然后利用 SDK 中的上传逻辑进行上传,最后从七牛云获取上传的结果,这个过程中由于双方都是业务服务器,所以很少利用到上传回调的功能,而是直接自定义returnBody来获取自定义的回复内容。

    构建配置类

    存储支持空间创建在不同的机房,在使用七牛的 Go SDK 中UploadManager上传文件之前,可以构建一个上传用的UploadManagerOptions对象,在该对象中,可以指定空间对应的Regions(如不指定 Regions 则会使用自动判断区域)以及其他的一些影响上传的参数。

    import (
        "github.com/qiniu/go-sdk/v7/storagev2/http_client"
        "github.com/qiniu/go-sdk/v7/storagev2/region"
        "github.com/qiniu/go-sdk/v7/storagev2/uploader"
    )
    
    options := uploader.UploadManagerOptions{
        Options: http_client.Options{
            Regions: region.GetRegionByID("z0", true),
        },
    }
    uploadManager := uploader.NewUploadManager(&options)
    

    其中关于Region ID和机房的关系如下:

    机房 Region ID
    华东 z0
    华东浙江 2 区 cn-east-2
    华北 z1
    华南 z2
    北美 na0
    新加坡 as0
    亚太-河内 ap-southeast-2
    亚太-胡志明 ap-southeast-3

    文件上传

    最简单的就是上传本地文件,直接指定文件的完整路径即可上传。

    import (
        "context"
        "github.com/qiniu/go-sdk/v7/storagev2/credentials"
        "github.com/qiniu/go-sdk/v7/storagev2/http_client"
        "github.com/qiniu/go-sdk/v7/storagev2/uploader"
    )
    
    accessKey := "your access key"
    secretKey := "your secret key"
    mac := credentials.NewCredentials(accessKey, secretKey)
    
    localFile := "/Users/jemy/Documents/github.png"
    bucket := "if-pbl"
    key := "github-x.png"
    
    uploadManager := uploader.NewUploadManager(&uploader.UploadManagerOptions{
        Options: http_client.Options{
            Credentials: mac,
        },
    })
    err := uploadManager.UploadFile(context.Background(), localFile, &uploader.ObjectOptions{
        BucketName: bucket,
        ObjectName: &key,
        CustomVars: map[string]string{
            "name": "github logo",
        },
    }, nil)
    if err != nil {
        return err
    }
    

    数据流上传

    可以支持将 io.Reader 读取到的数据上传到空间中。

    import (
        "bytes"
        "context"
        "github.com/qiniu/go-sdk/v7/storagev2/credentials"
        "github.com/qiniu/go-sdk/v7/storagev2/http_client"
        "github.com/qiniu/go-sdk/v7/storagev2/uploader"
    )
    
    accessKey := "your access key"
    secretKey := "your secret key"
    mac := credentials.NewCredentials(accessKey, secretKey)
    
    bucket := "if-pbl"
    key := "github-x.png"
    
    uploadManager := uploader.NewUploadManager(&uploader.UploadManagerOptions{
        Options: http_client.Options{
            Credentials: mac,
        },
    })
    err := uploadManager.UploadReader(context.Background(), reader, &uploader.ObjectOptions{
        BucketName: bucket,
        ObjectName: &key,
        CustomVars: map[string]string{
            "name": "github logo",
        },
    }, nil)
    if err != nil {
        return err
    }
    

    目录上传

    可以支持直接将目录上传到空间中。

    import (
        "context"
        "github.com/qiniu/go-sdk/v7/storagev2/credentials"
        "github.com/qiniu/go-sdk/v7/storagev2/http_client"
        "github.com/qiniu/go-sdk/v7/storagev2/uploader"
    )
    
    accessKey := "your access key"
    secretKey := "your secret key"
    mac := credentials.NewCredentials(accessKey, secretKey)
    
    localDir := "/Users/jemy/Documents/"
    bucket := "if-pbl"
    keyPrefix := "Documents/"
    
    uploadManager := uploader.NewUploadManager(&uploader.UploadManagerOptions{
        Options: http_client.Options{
            Credentials: mac,
        },
    })
    err := uploadManager.UploadDirectory(context.Background(), localDir, &uploader.DirectoryOptions{
        BucketName: bucket,
        UpdateObjectName: func(key string) string {
            return keyPrefix + key
        },
        ObjectConcurrency:     16,   // 对象上传并发度
    })
    if err != nil {
        return err
    }
    

    文件分片上传

    对于大的文件,Go SDK 自动使用分片上传的方式来上传文件,分片上传 v2通过将一个文件切割为块,然后通过上传块的方式来进行文件的上传。Go SDK 中默认块大小为 4MB,支持根据条件设置块大小(要求除最后一块外,其他块大于等于 1MB,小于等于 1G),块与块之间可以并发上传,以提高上传效率。分片上传不等于断点续传,但是分片上传可以支持断点续传。

    import (
        "context"
        "github.com/qiniu/go-sdk/v7/storagev2/credentials"
        "github.com/qiniu/go-sdk/v7/storagev2/http_client"
        "github.com/qiniu/go-sdk/v7/storagev2/uploader"
    )
    
    accessKey := "your access key"
    secretKey := "your secret key"
    mac := credentials.NewCredentials(accessKey, secretKey)
    
    localFile := "/Users/jemy/Documents/github.mp4"
    bucket := "if-pbl"
    key := "github-x.mp4"
    
    uploadManager := uploader.NewUploadManager(&uploader.UploadManagerOptions{
        Options: http_client.Options{
            Credentials: mac,
        },
        PartSize: 16 * 1024 * 1024, // 分片大小调整为 16MB,默认为 4MB
        Concurrency: 4, // 分片上传并发度调整为 4,默认为 1
    })
    err := uploadManager.UploadFile(context.Background(), localFile, &uploader.ObjectOptions{
        BucketName: bucket,
        ObjectName: &key,
        CustomVars: map[string]string{
            "name": "github logo",
        },
    }, nil)
    if err != nil {
        return err
    }
    

    文件断点续传

    断点续传实现逻辑是将每个块上传完毕的进度信息保存到本地的文件中持久化,如果本次上传被中断,下次可以从这个进度文件中读取每个块上传的状态,然后继续上传完毕没有完成的块,最后完成文件的拼接。需要注意,只有在块上传完毕之后,才向本地的进度文件写入进度内容。另外,每个上传任务初始化之后有效期最长是 7 天,过期会触发 701 的错误,可以检查进度信息中的时间参数来确认是否过期。

    断点续传是基于分片上传来实现的,基本原理就是用一个文本文件记录下上传的进度,如果上传中断,下次再从这个文件读取进度,继续上传未完成的分块。用户只需要传入 ResumableRecorder 参数即可实现断点续传,其他逻辑内部封装,实例如下:

    import (
        "context"
        "github.com/qiniu/go-sdk/v7/storagev2/credentials"
        "github.com/qiniu/go-sdk/v7/storagev2/http_client"
        "github.com/qiniu/go-sdk/v7/storagev2/uploader"
        resumablerecorder "github.com/qiniu/go-sdk/v7/storagev2/uploader/resumable_recorder"
    )
    
    accessKey := "your access key"
    secretKey := "your secret key"
    mac := credentials.NewCredentials(accessKey, secretKey)
    
    localFile := "/Users/jemy/Documents/github.png"
    bucket := "if-pbl"
    key := "github-x.png"
    
    uploadManager := uploader.NewUploadManager(&uploader.UploadManagerOptions{
        Options: http_client.Options{
            Credentials: mac,
        },
        ResumableRecorder: resumablerecorder.NewJsonFileSystemResumableRecorder("/data"),
    })
    err := uploadManager.UploadFile(context.Background(), localFile, &uploader.ObjectOptions{
        BucketName: bucket,
        ObjectName: &key,
        CustomVars: map[string]string{
            "name": "github logo",
        },
    }, nil)
    if err != nil {
        return err
    }
    

    解析自定义回复内容

    有些情况下,七牛返回给上传端的内容不是默认的hashkey形式,这种情况下,可能出现在自定义returnBody或者自定义了callbackBody的情况下,前者一般是服务端直传的场景,而后者则是接受上传回调的场景,这两种场景之下,都涉及到需要将自定义的回复进行内容解析,一般建议在交互过程中,都采用JSON的方式,这样处理起来方法比较一致,而且JSON的方法最通用。如果需要用到自定义回复,可以类似上面讲解returnBody的时候给的例子,自定义一个结构体用于回复的解析,例如:

    // 自定义返回值结构体
    type MyPutRet struct {
        Key    string
        Hash   string
        Fsize  int
        Bucket string
        Name   string
    }
    

    业务服务器验证存储服务回调

    在上传策略里面设置了上传回调相关参数的时候,七牛在文件上传到服务器之后,会主动地向callbackUrl发送 POST 请求的回调,回调的内容为callbackBody模版所定义的内容,如果这个模版里面引用了魔法变量或者自定义变量,那么这些变量会被自动填充对应的值,然后在发送给业务服务器。

    业务服务器在收到来自七牛的回调请求的时候,可以根据请求头部的Authorization字段来进行验证,查看该请求是否是来自七牛的未经篡改的请求。

    Go SDK 提供了一个方法 credentials.VerifyCallback 用于验证回调的请求:

    // VerifyCallback 验证上传回调请求是否来自存储服务
    func VerifyCallback(mac *credentials.Credentials, req *http.Request) (bool, error) {
        return mac.VerifyCallback(req)
    }
    

    加速域名

    加速上传

    import (
        "context"
        "github.com/qiniu/go-sdk/v7/storagev2/credentials"
        "github.com/qiniu/go-sdk/v7/storagev2/http_client"
        "github.com/qiniu/go-sdk/v7/storagev2/uploader"
    )
    
    accessKey := "your access key"
    secretKey := "your secret key"
    mac := credentials.NewCredentials(accessKey, secretKey)
    
    localFile := "/Users/jemy/Documents/github.png"
    bucket := "if-pbl"
    key := "github-x.png"
    
    uploadManager := uploader.NewUploadManager(&uploader.UploadManagerOptions{
        Options: http_client.Options{
            Credentials: mac,
            AccelerateUploading: true,
        },
    })
    err := uploadManager.UploadFile(context.Background(), localFile, &uploader.ObjectOptions{
        BucketName: bucket,
        ObjectName: &key,
        CustomVars: map[string]string{
            "name": "github logo",
        },
    }, nil)
    if err != nil {
        return err
    }
    

    下载文件

    对于下载的情况,首先考虑下载 URL 如何生成。这里介绍三种下载 URL 的生成方式:

    通过指定的自定义域名从公开空间下载

    因为是公开空间,所以无需设置鉴权参数。

    import (
        "context"
        "github.com/qiniu/go-sdk/v7/storagev2/downloader"
    )
    
    domain := "download.domain.com"
    urlsProvider := downloader.NewStaticDomainBasedURLsProvider([]string{domain})
    

    这里的 urlsProvider 之后可以用于下载文件。

    通过指定的自定义域名从私有空间下载

    私有空间必须用鉴权参数签名后才能下载。

    import (
        "context"
        "time"
        "github.com/qiniu/go-sdk/v7/storagev2/credentials"
        "github.com/qiniu/go-sdk/v7/storagev2/downloader"
    )
    
    accessKey := "your access key"
    secretKey := "your secret key"
    mac := credentials.NewCredentials(accessKey, secretKey)
    
    domain := "download.domain.com"
    urlsProvider := downloader.SignURLsProvider(downloader.NewStaticDomainBasedURLsProvider([]string{domain}), downloader.NewCredentialsSigner(mac), &downloader.SignOptions{
        TTL: 1 * time.Hour, // 有效期
    })
    

    通过源站下载

    如果没有自定义域名可用,就可以选择源站下载,源站下载必须签名且有效期最长五分钟。

    import (
        "context"
        "time"
        "github.com/qiniu/go-sdk/v7/storagev2/credentials"
        "github.com/qiniu/go-sdk/v7/storagev2/downloader"
    )
    
    accessKey := "your access key"
    secretKey := "your secret key"
    mac := credentials.NewCredentials(accessKey, secretKey)
    
    urlsProvider := downloader.SignURLsProvider(downloader.NewDefaultSrcURLsProvider(mac.AccessKey, nil), downloader.NewCredentialsSigner(mac), nil)
    

    下载到文件

    localFile := "/Users/jemy/Documents/github.png"
    bucket := "if-pbl"
    key := "github-x.png"
    
    downloadManager := downloader.NewDownloadManager(&downloader.DownloadManagerOptions{})
    downloaded, err := downloadManager.DownloadToFile(context.Background(), key, localPath, &downloader.ObjectOptions{
        GenerateOptions:            downloader.GenerateOptions{BucketName: bucket},
        DownloadURLsProvider:       urlsProvider,
    })
    if err != nil {
        return err
    }
    

    下载到数据流

    bucket := "if-pbl"
    key := "github-x.png"
    
    downloadManager := downloader.NewDownloadManager(&downloader.DownloadManagerOptions{})
    n, err := downloadManager.DownloadToWriter(context.Background(), key, writer, &downloader.ObjectOptions{
        GenerateOptions:      downloader.GenerateOptions{BucketName: bucket},
        DownloadURLsProvider: urlsProvider,
    })
    if err != nil {
        return err
    }
    

    目录下载

    localDir := "/Users/jemy/Documents/"
    bucket := "if-pbl"
    keyPrefix := "Documents/"
    
    downloadManager := downloader.NewDownloadManager(&downloader.DownloadManagerOptions{
        Options: http_client.Options{Credentials: mac},
    })
    err := downloadManager.DownloadDirectory(context.Background(), localDir, &downloader.DirectoryOptions{
        BucketName:           bucket,
        ObjectPrefix:         keyPrefix,
        ObjectConcurrency:    16, // 对象下载并发度
        DownloadURLsProvider: urlsProvider,
    })
    if err != nil {
        return err
    }
    

    资源管理

    资源管理包括的主要功能有:

    资源管理相关的操作首先要构建Bucket对象:

    import (
        "github.com/qiniu/go-sdk/v7/storagev2/credentials"
        "github.com/qiniu/go-sdk/v7/storagev2/http_client"
        "github.com/qiniu/go-sdk/v7/storagev2/objects"
    )
    
    accessKey := "your access key"
    secretKey := "your secret key"
    mac := credentials.NewCredentials(accessKey, secretKey)
    objectsManager := objects.NewObjectsManager(&objects.ObjectsManagerOptions{
        Options: http_client.Options{Credentials: creds},
    })
    bucketName := "if-pbl"
    bucket := objectsManager.Bucket(bucketName)
    

    获取文件信息

    key := "github.png"
    
    objectInfo, err := bucket.Object(key).Stat().Call(context.Background())
    if err != nil {
        return err
    }
    

    修改文件 MimeType

    key := "github.png"
    newMime := "image/x-png"
    
    err := bucket.Object(key).SetMetadata(newMime).Call(context.Background())
    if err != nil {
        return err
    }
    

    修改文件存储类型

    key := "github.png"
    storageClass := objects.IAStorageClass
    
    err := bucket.Object(key).SetStorageClass(storageClass).Call(context.Background())
    if err != nil {
        return err
    }
    

    修改文件 MetaData

    key := "github.png"
    err := bucket.Object(key).SetMetadata("").
        Metadata(map[string]string{"x-qn-meta-c": "x-qn-meta-cc-value"}). // 如果 x-qn-meta-c 存在,则将值修改为 x-qn-meta-cc-value,如果不存在则增加 x-qn-meta-c 键值对
        Call(context.Background())
    if err != nil {
        return err
    }
    

    移动或重命名文件

    移动操作本身支持移动文件到相同,不同空间中,在移动的同时也可以支持文件重命名。唯一的限制条件是,移动的源空间和目标空间必须在同一个机房。

    源空间 目标空间 源文件名 目标文件名 描述
    BucketA BucketA KeyA KeyB 相当于同空间文件重命名
    BucketA BucketB KeyA KeyA 移动文件到 BucketB,文件名一致
    BucketA BucketB KeyA KeyB 移动文件到 BucketB,文件名变成 KeyB

    move操作支持强制覆盖选项,即如果目标文件已存在,可以设置强制覆盖选项force来覆盖那个文件的内容。

    key := "github.png"
    //目标空间可以和源空间相同,但是不能为跨机房的空间
    destBucket := srcBucket
    //目标文件名可以和源文件名相同,也可以不同
    destKey := "github-new.png"
    err := bucket.Object(key).MoveTo(destBucket, destKey).Call(context.Background())
    if err != nil {
        return err
    }
    

    复制文件副本

    文件的复制和文件移动其实操作一样,主要的区别是移动后源文件不存在了,而复制的结果是源文件还存在,只是多了一个新的文件副本。

    key := "github.png"
    //目标空间可以和源空间相同,但是不能为跨机房的空间
    destBucket := srcBucket
    //目标文件名可以和源文件名相同,也可以不同
    destKey := "github-new.png"
    err := bucket.Object(key).CopyTo(destBucket, destKey).Call(context.Background())
    if err != nil {
        return err
    }
    

    删除空间中的文件

    key := "github.png"
    err := bucket.Object(key).Delete().Call(context.Background())
    if err != nil {
        return err
    }
    

    设置或更新文件的生存时间

    可以给已经存在于空间中的文件设置文件生存时间,或者更新已设置了生存时间但尚未被删除的文件的新的生存时间。

    key := "github.png"
    days := 7
    err := bucket.Object(key).SetLifeCycle().DeleteAfterDays(days).Call(context.Background())
    if err != nil {
        return err
    }
    

    获取指定前缀的文件列表

    获取指定文件前缀的列表用来从空间列举文件。

    keyPrefix := "qshell/"
    
    iter := bucket.List(context.Background(), &objects.ListObjectsOptions{Prefix: keyPrefix})
    defer iter.Close()
    
    var objectInfo objects.ObjectDetails
    for iter.Next(&objectInfo) {
        fmt.Printf("%s\n", objectInfo.Name)
    }
    if err := iter.Error(); err != nil {
        return err
    }
    

    资源管理批量操作

    批量获取文件信息

    onResponse := func(od *objects.ObjectDetails) {
        fmt.Printf("%s: %d\n", od.Name, od.Size)
    }
    onError := func(err error) {
        fmt.Printf("%s\n", err)
    }
    err := objectsManager.Batch(context.Background(), []objects.Operation{
        bucket.Object("github1.png").Stat().OnResponse(onResponse).OnError(onError),
        bucket.Object("github2.png").Stat().OnResponse(onResponse).OnError(onError),
        bucket.Object("github3.png").Stat().OnResponse(onResponse).OnError(onError),
        bucket.Object("github4.png").Stat().OnResponse(onResponse).OnError(onError),
        bucket.Object("github5.png").Stat().OnResponse(onResponse).OnError(onError),
    }, nil)
    if err != nil {
        return err
    }
    

    批量修改文件类型

    onError := func(err error) {
        fmt.Printf("%s\n", err)
    }
    err := objectsManager.Batch(context.Background(), []objects.Operation{
        bucket.Object("github1.png").SetMetadata("image/x-png").OnError(onError),
        bucket.Object("github2.png").SetMetadata("image/x-png").OnError(onError),
        bucket.Object("github3.png").SetMetadata("image/x-png").OnError(onError),
        bucket.Object("github4.png").SetMetadata("image/x-png").OnError(onError),
        bucket.Object("github5.png").SetMetadata("image/x-png").OnError(onError),
    }, nil)
    if err != nil {
        return err
    }
    

    批量删除文件

    onError := func(err error) {
        fmt.Printf("%s\n", err)
    }
    err := objectsManager.Batch(context.Background(), []objects.Operation{
        bucket.Object("github1.png").Delete().OnError(onError),
        bucket.Object("github2.png").Delete().OnError(onError),
        bucket.Object("github3.png").Delete().OnError(onError),
        bucket.Object("github4.png").Delete().OnError(onError),
        bucket.Object("github5.png").Delete().OnError(onError),
    }, nil)
    if err != nil {
        return err
    }
    

    批量复制文件

    destBucket := "if-pbl-2"
    onError := func(err error) {
        fmt.Printf("%s\n", err)
    }
    err := objectsManager.Batch(context.Background(), []objects.Operation{
        bucket.Object("github1.png").CopyTo(destBucket, "github1.png").OnError(onError),
        bucket.Object("github2.png").CopyTo(destBucket, "github1.png").OnError(onError),
        bucket.Object("github3.png").CopyTo(destBucket, "github1.png").OnError(onError),
        bucket.Object("github4.png").CopyTo(destBucket, "github1.png").OnError(onError),
        bucket.Object("github5.png").CopyTo(destBucket, "github1.png").OnError(onError),
    }, nil)
    if err != nil {
        return err
    }
    

    批量移动或重命名文件

    destBucket := "if-pbl-2"
    onError := func(err error) {
        fmt.Printf("%s\n", err)
    }
    err := objectsManager.Batch(context.Background(), []objects.Operation{
        bucket.Object("github1.png").MoveTo(destBucket, "github1.png").OnError(onError),
        bucket.Object("github2.png").MoveTo(destBucket, "github1.png").OnError(onError),
        bucket.Object("github3.png").MoveTo(destBucket, "github1.png").OnError(onError),
        bucket.Object("github4.png").MoveTo(destBucket, "github1.png").OnError(onError),
        bucket.Object("github5.png").MoveTo(destBucket, "github1.png").OnError(onError),
    }, nil)
    if err != nil {
        return err
    }
    

    批量更新文件的有效期

    onError := func(err error) {
        fmt.Printf("%s\n", err)
    }
    err := objectsManager.Batch(context.Background(), []objects.Operation{
        bucket.Object("github1.png").SetLifeCycle().DeleteAfterDays(7).OnError(onError),
        bucket.Object("github2.png").SetLifeCycle().DeleteAfterDays(8).OnError(onError),
        bucket.Object("github3.png").SetLifeCycle().DeleteAfterDays(9).OnError(onError),
        bucket.Object("github4.png").SetLifeCycle().DeleteAfterDays(10).OnError(onError),
        bucket.Object("github5.png").SetLifeCycle().DeleteAfterDays(11).OnError(onError),
    }, nil)
    if err != nil {
        return err
    }
    

    批量更新文件存储类型

    storageClass := objects.IAStorageClass
    onError := func(err error) {
        fmt.Printf("%s\n", err)
    }
    err := objectsManager.Batch(context.Background(), []objects.Operation{
        bucket.Object("github1.png").SetStorageClass(storageClass).OnError(onError),
        bucket.Object("github2.png").SetStorageClass(storageClass).OnError(onError),
        bucket.Object("github3.png").SetStorageClass(storageClass).OnError(onError),
        bucket.Object("github4.png").SetStorageClass(storageClass).OnError(onError),
        bucket.Object("github5.png").SetStorageClass(storageClass).OnError(onError),
    }, nil)
    if err != nil {
        return err
    }
    

    持久化数据处理

    发送数据处理请求(数据处理指令)

    对于已经保存到七牛空间的文件,可以通过发送持久化的数据处理指令来进行处理,这些指令支持七牛官方提供的指令,也包括客户自己开发的自定义数据处理的指令。数据处理的结果还可以通过七牛主动通知的方式告知业务服务器。

    使用数据处理,首先需要构建一个OperationManager对象:

    mac := auth.New(accessKey, secretKey)
    cfg := storage.Config{
    	UseHTTPS: true,
    }
    
    // 指定空间所在的区域,如果不指定将自动探测
    // 如果没有特殊需求,默认不需要指定
    //cfg.Region=&storage.ZoneHuabei
    operationManager := storage.NewOperationManager(mac, &cfg)
    

    进行视频的处理示例:

    bucket := "if-pbl"
    key := "qiniu.mp4"
    mac := auth.New(accessKey, secretKey)
    
    saveBucket := bucket
    
    // 处理指令集合
    fopAvthumb := fmt.Sprintf("avthumb/mp4/s/480x320/vb/500k|saveas/%s",
    	storage.EncodedEntry(saveBucket, "pfop_test_qiniu.mp4"))
    fopVframe := fmt.Sprintf("vframe/jpg/offset/10|saveas/%s",
    	storage.EncodedEntry(saveBucket, "pfop_test_qiniu.jpg"))
    fopVsample := fmt.Sprintf("vsample/jpg/interval/20/pattern/%s",
    	base64.URLEncoding.EncodeToString([]byte("pfop_test_$(count).jpg")))
    
    fopBatch := []string{fopAvthumb, fopVframe, fopVsample}
    fops := strings.Join(fopBatch, ";")
    
    // 强制重新执行数据处理任务
    force := int64(1)
    // 数据处理指令全部完成之后,通知该地址
    notifyURL := "http://api.example.com/pfop/callback"
    // 数据处理的私有队列,必须指定以保障处理速度
    pipeline := "jemy"
    // 任务类型:0: 普通任务 1: 闲时任务(一旦指定闲时任务,就不能指定 pipeline)
    persistentType := int64(0)
    request := storage.PfopRequest{
        BucketName:         bucket,
        ObjectName:         key,
        Fops:               fops,
        NotifyUrl:          notifyURL,
        Force:              force,
        Type:               persistentType,
        Pipeline:           pipeline,
    }
    persistentId, err := operationManager.PfopV2(context.Background(), &request)
    if err != nil {
    	fmt.Println(err)
    	return
    }
    
    fmt.Println(persistentId)
    

    发送数据处理请求(工作流模版)

    也可以支持使用工作流模版替代数据处理指令。工作流模板是预先编排好的一系列媒体处理流程(如转码、截图、视频拼接等各类处理),登录 对象存储控制台 进行创建,详情参考工作流模板操作指南WorkflowTemplateID 对应工作流模板列表的名称字段

    bucket := "if-pbl"
    key := "qiniu.mp4"
    mac := auth.New(accessKey, secretKey)
    
    // 工作流模版
    workflowTemplateID := "tempname"
    // 强制重新执行数据处理任务
    force := int64(1)
    // 数据处理指令全部完成之后,通知该地址
    notifyURL := "http://api.example.com/pfop/callback"
    // 数据处理的私有队列,必须指定以保障处理速度
    pipeline := "jemy"
    // 任务类型:0: 普通任务 1: 闲时任务(一旦指定闲时任务,就不能指定 pipeline)
    persistentType := int64(0)
    request := storage.PfopRequest{
        BucketName:         bucket,
        ObjectName:         key,
        WorkflowTemplateID: workflowTemplateID,
        NotifyUrl:          notifyURL,
        Force:              force,
        Type:               persistentType,
        Pipeline:           pipeline,
    }
    persistentId, err := operationManager.PfopV2(context.Background(), &request)
    if err != nil {
    	fmt.Println(err)
    	return
    }
    
    fmt.Println(persistentId)
    

    查询数据处理请求状态

    由于数据处理是异步处理,可以根据发送处理请求时返回的 persistentId 去查询任务的处理进度,如果在上传策略里面设置了persistentNotifyUrl或者发送处理请求时指定notifyURL的情况下,直接业务服务器等待处理结果通知即可,如果需要主动查询,可以采用如下代码中的:

    persistentId := "z0.597f28b445a2650c994bb208"
    ret, err := operationManager.Prefop(persistentId)
    if err != nil {
    	fmt.Println(err)
    	return
    }
    fmt.Println(ret.String())
    

    CDN 相关功能

    在使用 CDN 相关功能之前,需要构建CdnManager对象:

    mac := auth.New(accessKey, secretKey)
    cdnManager := cdn.NewCdnManager(mac)
    

    文件刷新

    //刷新链接,单次请求链接不可以超过100个,如果超过,请分批发送请求
    urlsToRefresh := []string{
    	"http://if-pbl.qiniudn.com/qiniu.png",
    	"http://if-pbl.qiniudn.com/github.png",
    }
    
    ret, err := cdnManager.RefreshUrls(urlsToRefresh)
    if err != nil {
    	fmt.Println(err)
    	return
    }
    fmt.Println(ret.Code)
    fmt.Println(ret.RequestID)
    

    目录刷新

    // 刷新目录,刷新目录需要联系七牛技术支持开通权限
    // 单次请求链接不可以超过10个,如果超过,请分批发送请求
    dirsToRefresh := []string{
    	"http://if-pbl.qiniudn.com/images/",
    	"http://if-pbl.qiniudn.com/static/",
    }
    
    ret, err := cdnManager.RefreshDirs(dirsToRefresh)
    if err != nil {
    	fmt.Println(err)
    	return
    }
    fmt.Println(ret.Code)
    fmt.Println(ret.RequestID)
    fmt.Println(ret.Error)
    

    文件预取

    // 预取链接,单次请求链接不可以超过100个,如果超过,请分批发送请求
    urlsToPrefetch := []string{
    	"http://if-pbl.qiniudn.com/qiniu.png",
    	"http://if-pbl.qiniudn.com/github.png",
    }
    
    ret, err := cdnManager.PrefetchUrls(urlsToPrefetch)
    if err != nil {
    	fmt.Println(err)
    	return
    }
    fmt.Println(ret.Code)
    fmt.Println(ret.RequestID)
    

    获取域名流量

    domains := []string{
    	"if-pbl.qiniudn.com",
    	"qdisk.qiniudn.com",
    }
    startDate := "2017-07-30"
    endDate := "2017-07-31"
    granularity := "day"
    
    data, err := cdnManager.GetFluxData(startDate, endDate, granularity, domains)
    if err != nil {
    	fmt.Println(err)
    	return
    }
    fmt.Printf("%v\n", data)
    

    获取域名带宽

    domains := []string{
    	"if-pbl.qiniudn.com",
    	"qdisk.qiniudn.com",
    }
    startDate := "2017-07-30"
    endDate := "2017-07-31"
    granularity := "day"
    
    data, err := cdnManager.GetBandwidthData(startDate, endDate, granularity, domains)
    if err != nil {
    	fmt.Println(err)
    	return
    }
    fmt.Printf("%v\n", data)
    

    获取日志下载链接

    domains := []string{
    	"if-pbl.qiniudn.com",
    	"qdisk.qiniudn.com",
    }
    
    day := "2017-07-30"
    
    ret, err := cdnManager.GetCdnLogList(day, domains)
    if err != nil {
    	fmt.Println(err)
    	return
    }
    
    domainLogs := ret.Data
    for domain, logs := range domainLogs {
    	fmt.Println(domain)
    	for _, item := range logs {
    		fmt.Println(item.Name, item.URL, item.Size, item.ModifiedTime)
    	}
    }
    

    构建时间戳防盗链访问链接

    具体算法可以参考:时间戳防盗链

    urlStr := "http://img.abc.com/test.jpg"
    cryptKey := "abc123"
    deadline := time.Now().Add(time.Second * 3600).Unix()
    accessUrl, err := cdn.CreateTimestampAntileechURL(urlStr, cryptKey, deadline)
    if err != nil {
    	fmt.Println(err)
    	return
    }
    fmt.Println(accessUrl)
    

    API 参考

    常见问题

    • Go SDK 的 err 保留了请求的错误信息,回复信息和头部信息,遇到问题时,可以都打印出来提交给我们排查问题。
    if err != nil {
    	if v, ok := err.(*rpc.ErrorInfo); ok {
    		fmt.Println(v.Code, v.RequestID, v.Err)
    	} else if v, ok := err.(*storage.ErrorInfo); ok {
    		fmt.Println(v.Code, v.Reqid, v.Err)
    	} else {
    		fmt.Println(err)
    	}
    }
    
    • API 的使用,可以参考我们为大家精心准备的使用实例

    相关资源

    如果您有任何关于我们文档或产品的建议和想法,欢迎您通过以下方式与我们互动讨论:

    • 技术论坛 - 在这里您可以和其他开发者愉快的讨论如何更好的使用七牛云服务
    • 提交工单 - 如果您的问题不适合在论坛讨论或希望及时解决,您也可以提交一个工单,我们的技术支持人员会第一时间回复您
    • 博客 - 这里会持续更新发布市场活动和技术分享文章
    • 微博
    • 常见问题

    贡献代码

    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 © 2020 qiniu.com

    基于 MIT 协议发布:

    以上内容是否对您有帮助?
  • Qvm free helper
    Close