golang 操作ceph object storage

ceph的object storage 提供了和amazon s3兼容的接口以供客户访问。

在ceph的官网上,可以看到它提供了多种语言的访问范本,例如python的(http://docs.ceph.com/docs/master/radosgw/s3/python/):

import boto
import boto.s3.connection
access_key = 'abc'
secret_key = 'cba'
conn = boto.connect_s3(
        aws_access_key_id = access_key,
        aws_secret_access_key = secret_key,
        host = '1.2.3.4', port=80,
        is_secure=False,
        calling_format = boto.s3.connection.OrdinaryCallingFormat(),
)
#bucket = conn.create_bucket('my-first-s3-bucket')
bucket = conn.get_bucket('my-first-s3-bucket')
print "{name}".format(
            name = bucket.name,
#            created = bucket.creation_date,
            )
for obj in bucket.get_all_keys():
    print(obj.key)
key = bucket.new_key('auto')
key.set_contents_from_filename('/tmp/test.yaml')

但是找不到golang版本的,研究了很久,才在网上找到golang版本的example,链接:https://my.oschina.net/u/578775/blog/863370

package main

import (
    "bufio"
    "bytes"
    "errors"
    "fmt"
    "github.com/astaxie/beego"
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/credentials"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/s3"
    "io"
    "os"
)

type CephMgmt struct {
    host        string `ceph host`
    DoName      string `ceph doname`
    bucket_id   string `bucket_id`
    PathStyle   bool   `ceph url style, true means you can use host directly,  false means bucket_id.doname will be used`
    AccessKey   string `aws s3 aceessKey`
    SecretKey   string `aws s3 secretKey`
    block_size int64  `block_size`
}

var Ceph CephMgmt

type MyProvider struct{}

func (m *MyProvider) Retrieve() (credentials.Value, error) {

    return credentials.Value{
        AccessKeyID:     Ceph.AccessKey, //"9YGYCFH1V3QUT4B9KZD1",
        SecretAccessKey: Ceph.SecretKey, //"9hdiwSjCfyrZPKGObH8Kctdur8PiBPJu3B4zGYaZ",
    }, nil
}
func (m *MyProvider) IsExpired() bool { return false }

func (this *CephMgmt) Init() error {
    // "http://s3.devopscloud.com"
    this.host = beego.AppConfig.String("ceph::host")
    if len(this.host) <= 0 {
        return errors.New("ceph conf host is nil")
    }
    this.bucket_id = beego.AppConfig.String("ceph::bucket_id")
    if len(this.bucket_id) <= 0 {
        return errors.New("ceph conf bucket_id is nil")
    }
    this.block_size, _ = beego.AppConfig.Int64("ceph::block_size")
    if this.block_size <= 5*1024*1024 {
        this.block_size = 5 * 1024 * 1024
    }
    return nil
}


func (this *CephMgmt) Init2(host string, bucket string, accesskey string, secretkey string) error {

    this.host = host
    this.bucket_id = bucket
    this.AccessKey = accesskey
    this.SecretKey = secretkey
    this.PathStyle = true
    this.block_size = 8 * 1024 * 1024

    return nil
}

func (this *CephMgmt) connect() (*s3.S3, error) {
    ///////////////创建连接
    sess := session.Must(session.NewSessionWithOptions(session.Options{
        Config: aws.Config{
            Region: aws.String("default"), //Required 目前尚未分区,填写default即可
            //EndpointResolver: endpoints.ResolverFunc(s3CustResolverFn),
            Endpoint:         &this.host,
            S3ForcePathStyle: &this.PathStyle,
            Credentials:      credentials.NewCredentials(&MyProvider{}),
        },
    }))
    // Create the S3 service client with the shared session. This will
    // automatically use the S3 custom endpoint configured in the custom
    // endpoint resolver wrapping the default endpoint resolver.
    return s3.New(sess), nil
}


func (this *CephMgmt) Download(src_name, dst_name string) error {
    s3Svc, _ := this.connect()
    // Operation calls will be made to the custom endpoint.
    resp, err := s3Svc.GetObject(&s3.GetObjectInput{
        Bucket: &this.bucket_id, //Required 可认为是cpid,也可认为是对于根目录而言的第一级目录.必须先创建,在存储。
        Key:    &src_name,       //Required 文件名,中间可以带着路径,格式如:{path}/{filename}
        //Range:  aws.String("bytes=0-499"),             //not must be Required 文件范围,如果没有则是全文件
    })
    if err != nil {
        // Print the error, cast err to awserr.Error to get the Code and
        // Message from an error.
        fmt.Println("hello , i met error")
        fmt.Println(err.Error())
        return err
    }

    fmt.Println("AcceptRanges :", *resp.AcceptRanges)
    fmt.Println("ContentLength:", *resp.ContentLength)
    //fmt.Println("ContentRange :", *resp.ContentRange)
    //fmt.Println("PartsCount   :", *resp.PartsCount)
    // Pretty-print the response data.
    //fmt.Println(resp)

    //数据都在resp.Body中,可以转储到文件,具体操作参考go的操作
    out, err := os.OpenFile(dst_name, os.O_CREATE|os.O_RDWR, 0666)
    if out == nil {
        fmt.Println("Open fail")
        return err
    }
    num, err := io.Copy(out, resp.Body)
    fmt.Printf("\n write %d err %v \n", num, err)
    return nil
}

func (this *CephMgmt) Upload(src_name, dst_name string) error {
    s3Svc, _ := this.connect()

    dst_full_name := fmt.Sprintf("/%s/%s", this.bucket_id, dst_name) //Notice: 如果要讲桶ID拼到全路径下,一定要在最前面加'/'

    //////////////////////////////////////// 存入 /////////////////////////////
    /////////////// 创建一个分片上传context
    param_init := &s3.CreateMultipartUploadInput{
        Bucket: aws.String(this.bucket_id), // Required {bucket}
        Key:    aws.String(dst_full_name),  // Required /{bucket}/{path}/{filename}
        /*
            Metadata: map[string]*string{
                "Key": aws.String("lasttime_if_need"), // Required 可以填充一些文件属性,ceph云存储不关心其中的内容
            },
        */
    }

    resp_init, err := s3Svc.CreateMultipartUpload(param_init)
    if err != nil {
        // Print the error, cast err to awserr.Error to get the Code and
        // Message from an error.
        fmt.Println("I am create upload.", err.Error())
        return err

    }
    // Pretty-print the response data.
    up_id := resp_init.UploadId
    fmt.Println(resp_init) // 需要保存该resp , resp.UploadId 是对象网关创建的标识

    ///////////////////////// 分片上传
    f, err := os.Open(src_name)
    if err != nil {
        return err
    }
    defer f.Close()

    bfRd := bufio.NewReader(f)
    buf := make([]byte, this.block_size) //TODO:一次读取多少个字节

    var part_num int64 = 0
    var completes []*s3.CompletedPart

    for {
        n, err := bfRd.Read(buf)
        if err == io.EOF {
            fmt.Println("read data finished")
            break
        }
        if int64(n) != this.block_size {
            data := make([]byte, n)
            data = buf[0:n]
            buf = data
        }
        //////////////////////// 每次上传一个分片,每次的PartNumber都要唯一
        part_num++
        param := &s3.UploadPartInput{
            Bucket:        aws.String(this.bucket_id), // Required bucket
            Key:           aws.String(dst_name),       // Required {path}/{filename}
            PartNumber:    aws.Int64(part_num),        // Required 每次的序号唯一且递增
            UploadId:      up_id,                      // Required 创建context时返回的值
            Body:          bytes.NewReader(buf),       // Required 数据内容
            ContentLength: aws.Int64(int64(n)),        // Required 数据长度
        }

        resp2, err := s3Svc.UploadPart(param)
        if err != nil {
            fmt.Printf("Hello ,i am wrong[%s][%d][%d]\n", dst_name, part_num, n)
            return err
        }
        fmt.Println(resp2) // 需要保存该resp,因为resp.Etag 需要在通知完成上传时使用

        var c s3.CompletedPart
        c.PartNumber = aws.Int64(part_num) // Required Etag对应的PartNumber, 上一步返回的
        c.ETag = resp2.ETag                // Required 上传分片时返回的值 Etag
        completes = append(completes, &c)
    }

    /////////////////////////// 结束上传
    params := &s3.CompleteMultipartUploadInput{
        Bucket:   aws.String(this.bucket_id), // Required {bucket}
        Key:      aws.String(dst_name),       // Required {path}/{filename}
        UploadId: up_id,                      // Required 创建context时返回的值
        MultipartUpload: &s3.CompletedMultipartUpload{
            Parts: completes,
        },
        //RequestPayer: aws.String("RequestPayer"),
    }
    resp_comp, err := s3Svc.CompleteMultipartUpload(params)
    if err != nil {
        // Print the error, cast err to awserr.Error to get the Code and
        // Message from an error.
        fmt.Println(err.Error())
        return err
    }
    // Pretty-print the response data.
    fmt.Println(resp_comp)
    return nil
}

func main() {
    //既可以使用域名,也可以使用ip+端口的形式
    Ceph.Init2( /*"http://172.16.20.15"*/ "http://s3.devopscloud.com:80", "new-bucket-5de4d39a", "9YGYCFH1V3QUT4B9KZD1", "9hdiwSjCfyrZPKGObH8Kctdur8PiBPJu3B4zGYaZ")
    Ceph.Download("1.mp4", "./1.mp4")
    Ceph.Upload("1.mp4", "api/rest/2017-3-16-10-38.mp4")
}

 

posted @ 2019-07-11 17:23  ElNinoT  阅读(3278)  评论(0编辑  收藏  举报