QingStor 对象存储支持 Fetch 接口和批量迁移工具 qscamel

Fetch 接口

PUT Object – Fetch 接口用于从指定的源链接抓取资源,保存到指定的对象中。 Qingstor 对象存储在抓取时能够自动处理源链接服务器返回的 301/302/307 等重定向请求。

请求示例:

PUT /<object-name> HTTP/1.1
X-QS-Fetch-Source: <source site>
Host: <bucket-name>.pek3a.qingstor.com
Date: <date>
Authorization: <authorization-string>

下面是使用 QingStor Golang SDK 的例子:

import (
    "github.com/yunify/qingstor-sdk-go/config"
    qs "github.com/yunify/qingstor-sdk-go/service"
)
configuration, err := config.New("ACCESS_KEY_ID", "SECRET_ACCESS_KEY")
service, err := qs.Init(configuration)
bucket, err := service.Bucket("test-bucket", "pek3a")
inputArgs := qs.PutObjectInput{
    XQSFetchSource: "http://img.example.com/blog.png",
}
bucket.PutObject("new_object", &inputArgs)

具体使用请参考文档和相应的 SDK

 

批量迁移工具 qscamel

qscamel 是把 HTTP(s) 形式的数据高效地批量迁移到 QingStor 的命令行工具。其输入可以是包含源链接的文件,也可以是其他对象存储平台的 Bucket 名称。更多介绍请参考 qscamel 文档。qscamel 已开源,请见 github 项目地址

 qscamel 有如下特点:

1.支持并行迁移,即同时迁移多个对象。

2.支持给迁移任务命名,用来继续迁移未完成的迁移任务。qscamel 会记录本次迁移任务中成功迁移的源链接。在退出后、重新执行时,qscamel 会跳过已经成功迁移的源链接,迁移剩下未完成的源链接。

3.支持灵活的覆盖模式。qscamel 默认进行增量迁移 (比较源站资源和 QingStor Bucket 中对象的最后更新时间),只迁移 Bucket 中已存在但未更新的对象,和 Bucket 中不存在的对象。但同时提供选项 “-i, –ignore-existing” 来忽略 QingStor Bucket 中已经存在的对象,及选项 “-o, –overwrite” 来强制覆盖 QingStor Bucket 中已经存在的对象。

4.支持指定日志文件。qscamel 默认输出到 stdout,可以指定输出到指定的日志文件。

 

下面给出 qscamel 的运行示例:

(1) 读取源站列表文件,进行批量迁移

(2) 退出后、重新执行时,根据记录文件,跳过成功迁移的源链接。示例跳过了成功迁移的两条源链接,只迁移 https://img3.doubanio.com/lpic/s29154751.jpg

(3) 下面演示 -i –ignore-existing 跳过迁移 Bucket 中已经存在的对象,和-n –dry-run

可以看到 Bucket 中已经有 3 个对象。 源站列表文件中,共有 4 个源链接。其中前 3 个在 Bucket 中已存在,使用 -i 选项时跳过了这 3 个。使用 -n 选项时,qscamel 会尝试运行,并输出运行结果,但不做实际迁移。

 

QingStor 对象存储支持请求参数签名

用户通过 API 请求 QingStor 时,需要对请求进行签名,现有的签名方式是首先计算签名串,然后添加到请求头 (Request Headers),和 API 请求一起发送到 QingStor 服务端。

然而并不是所有客户端都有能力设置请求头字段,又或者用户想要分享某个私有权限的文件链接给其它用户,基于这样的需求,QingStor 新增了基于请求参数 (Query String) 的签名方式。

请求参数签名的方式不需要在 HTTP 请求头中附加任何内容,只需要在请求参数中添加以下三项必要参数:

  • access_key_id – 在 QingCloud 控制台申请的 Access Key ID
  • expires – 签名过期时间,该时间为 Unix Time (也称为 Epoch Time), 表示方法是自历元(1970-01-01 00:00, The Epoch) 之后的秒数, 类型为整数。在过期时间之后到达的请求将被 QingStor 拒绝
  • signature – 对签名串 string_to_sign 经过 HMAC-SHA256 加密后,再使用 Base64 编码,最后使用 URI 编码后的结果

请求示例:

GET /music.mp3?access_key_id=PLLZOBTTZXGBNOWUFHZZ&expires=1479107162&signature=tuXu/KcggHWPAfEmraUHDwEUdiIPSXVRsO%2BT2rxomBQ%3D
Host: mybucket.pek3a.qingstor.com
Date: Mon, 14 Nov 2016 14:05:00 GMT

相比于请求头签名方法,签名串 string_to_sign 的生成过程只有一点不同,即把 Date 替换为 Expires:

string_to_sign = Verb + "\n"
               + Content-MD5 + "\n"
               + Content-Type + "\n"
               + Expires + "\n"
              (+ Canonicalized Headers + "\n")
               + Canonicalized Resource

通过上述方法得到签名串 string_to_sign 以后计算 signature (以 Python 为例):

import hmac
import base64
import urllib
from hashlib import sha256

h = hmac.new(secret_access_key, digestmod=sha256)
h.update(string_to_sign)
signature = urllib.quote(base64.b64encode(h.digest()).strip())

最后拼接成请求参数,追加到请求地址的后面:

access_key_id=PLLZOBTTZXGBNOWUFHZZ&expires=1479107162&signature=tuXu/KcggHWPAfEmraUHDwEUdiIPSXVRsO%2BT2rxomBQ%3D

用户还可以使用 QingStor SDK 生成参数签名的 URL 链接,下面以 QingStor Go SDK 为例:

import (
    "github.com/yunify/qingstor-sdk-go/config"
    qs "github.com/yunify/qingstor-sdk-go/service"
)

configuration, err := config.New("ACCESS_KEY_ID", "SECRET_ACCESS_KEY")
qsService, err := qs.Init(configuration)
bucket, err := qsService.Bucket("test-bucket", "pek3a")

request, _, err := bucket.GetObjectRequest("test-object", nil)
err = request.SignQuery(3600) // Expires in 1 hour
signedURL = request.HTTPRequest.URL.String()
* 注:QingStor 各语言 SDK 均已支持请求参数签名,SDK 的安装和使用方法请参考文档

参数签名使用的场合较为广泛,它所能达成的效果跟请求头签名完全一样,只是让附带签名的过程更加方便和易于使用。更多 QingStor 签名有关的内容请参考文档

 

SDK 代码生成工具 Snips 发布

Snips 是一个在命令行界面使用的 SDK 代码生成工具,借助 Snips,可以在短时间内开发出 QingCloud 或 QingStor 的 SDK。Snips 已在 GitHub 开源,更多详情可参见 https://github.com/yunify/snips

Snips 读取 API Specifications(API 描述,例如 QingStor 对象存储的 API Specification: https://github.com/yunify/qingstor-api-specs)文件和模版文件来生成 SDK 中 API 调用部分的代码,再加上与各语言相关的代码,便可组成一个完整的 SDK。

另外,为了保证 SDK 的可用性和功能一致性,我们还引入了测试场景描述项目 https://github.com/yunify/qingstor-sdk-test-scenarios

注: QingCloud 的 API 描述及测试场景描述项目将稍后开源在 GitHub 上。

使用 Snips 开发 SDK 的流程如下图:

Snips Workflow

 使用 Snips 增加一门新语言的 SDK

1. 编写语言相关的、不适合生成的代码

2. 编写 API 代码模版

3. 使用 Snips 生成代码

4. 测试

5. 发布

使用 Snips 更新现有 SDK

1. 更新 API Specifications

2. 使用 Snips 重新生成代码

3. 测试

4. 发布

使用 Snips 生成 SDK 演示

下面以 QingStor 的 Go SDK(qingstor-sdk-go)为例演示 Snips 的使用方法:

1. 安装

可以直接使用 go get 安装:

go get -u github.com/yunify/snips

也可以从 Snips 项目的 GitHub Releases 页面下载编译好的二进制文件,支持 macOS、Linux 和 Windows 平台。

2. 使用 git submodule 引入 QingStor API Specifications 和 QingStor SDK test scenarios

3. 编写语言相关的不适合生成的代码,编写模版文件。

4. 使用 Snips 生成代码,并格式化代码。

$ snips --service=qingstor \
        --service-api-version=latest \
        --spec="./specs" \
        --template="./template" \
        --output="./service"
Loaded templates from ./template
4 template(s) detected.
Loaded service QingStor (2016-01-06) from ./specs

Generating to: ./service/qingstor.go
Generating to: ./service/object.go
Generating to: ./service/bucket.go
Generating to: ./service/types.go

Everything looks fine.
$ gofmt -w .

5. 使用生成好的代码实现测试用例,并运行场景化测试

$ pushd ./test
$ go run *.go
...
38 scenarios (38 passed)
84 steps (84 passed)
1m2.408357076s
$ popd

6. 以后每次 API 发生变更,只需要更新 git submodule,然后重新生成代码,通过场景化测试即可。

QingStor 对象存储支持数据加密服务

QingStor 对象存储已支持由用户提供密钥的数据加密方式。用户在上传对象时提供密钥,对象存储服务端用该密钥对上传的对象进行加密处理。相应地,在下载加密过的对象时,用户也须提供密钥,对象存储服务端用该密钥对下载的对象进行解密处理后返回。

NOTE: QingStor 对象存储服务端不保存用户的密钥,只会保存加密算法和密钥的 MD5 值。用户需要自己妥善保管上传时指定的密钥。

支持加密的 APIs 有:PUT ObjectMultipart Upload。支持解密的 APIs 有:GET ObjectHEAD ObjectPUT Object – Copy 。更详细的信息请参考QingStor 对象存储官方文档中数据加密一节。

1.加密过程

1. 客户端提供密钥及密钥的 MD5 值,在上传对象的请求的请求头中指定加密算法、密钥、和密钥 MD5 值。目前支持的加密算法是 AES256 。

2. 服务端根据密钥生成 MD5 值,与用户上传的密钥 MD5 值比对,以确认密钥在传输过程中的完整性。

3. 服务端对上传的对象进行加密。

4. 服务端丢弃用户上传的密钥,只保存加密算法和密钥 MD5 值。

5. 用户需要自己保存给该对象加密的密钥。

2.解密过程

1. 客户端在获取对象的请求的请求头中指定加密算法、密钥、密钥 MD5 值。密钥须为上传时所指定的加密密钥。

2. 服务端根据密钥生成 MD5 值,与用户上传的密钥 MD5 值比对,以确认密钥在传输过程中的完整性。

3. 服务端比对上传对象时保存的 MD5 值和请求中的密钥的 MD5 值,以验证密钥是否正确。

4. 服务端将对象用请求头中指定的密钥解密并返回。

3.示例

下面通过 PUT Object 和 GET Object 接口,演示数据加密服务流程。

1.上传对象时指定加密算法,密钥和密钥 MD5。

PUT /myphoto.jpg HTTP/1.1
Host: mybucket.pek3a.qingstor.com
Date: Sun, 16 Aug 2016 10:15:00 GMT
Content-Length: 7987
Authorization: authorization string
X-QS-Encryption-Customer-Algorithm: AES256
X-QS-Encryption-Customer-Key: M9Thi2bO7tsdbsuMo679ojaq0G6L5laHbxwjo0JVBeI=
X-QS-Encryption-Customer-Key-MD5: OTIxZjI3YzNhYjA4MTAyMzI3ZmViOGIxNWYzMjRhODA=
[7987 bytes of object data]

2.获取对象时提供密钥,由对象存储服务端解密

GET /myphoto.jpg HTTP/1.1
Host: mybucket.pek3a.qingstor.com
Date: Sun, 16 Aug 2016 10:15:02 GMT
Authorization: authorization string
X-QS-Encryption-Algorithm: AES256
X-QS-Encryption-Customer-Key: M9Thi2bO7tsdbsuMo679ojaq0G6L5laHbxwjo0JVBeI=
X-QS-Encryption-Customer-Key-MD5: OTIxZjI3YzNhYjA4MTAyMzI3ZmViOGIxNWYzMjRhODA=

QingStor 对象存储支持移动对象和批量删除对象

移动对象

QingStor 对象存储已经支持从一个存储空间中移动对象到另一个的存储空间中去,类似 *nix 系统中的 `mv` 命令,这个功能可以用于对象的重命名等操作。

下面是使用 QingStor Golang SDK 的例子:

import (
    "github.com/yunify/qingstor-sdk-go/config"
    qs "github.com/yunify/qingstor-sdk-go/service"
)

configuration, err := config.New("ACCESS_KEY_ID", "SECRET_ACCESS_KEY")
qsService, err := qs.Init(configuration)
bucket, err := qsService.Bucket("test-bucket", "pek3a")

inputArgs := qs.PutObjectInput{
    XQSMoveSource: "/" + "your_bucket" + "/" + "old_object",
}
bucket.PutObject("new_object", &inputArgs)

具体使用请参考文档和相应的 SDK

批量删除对象

现在用户可以一次删除最多 1000 个对象,配合 List Objects API , 可以实现快速地清空存储空间等操作。

下面是使用 QingStor Golang SDK 的例子:

import (
    "github.com/yunify/qingstor-sdk-go/config"
    qs "github.com/yunify/qingstor-sdk-go/service"
)

configuration, err := config.New("ACCESS_KEY_ID", "SECRET_ACCESS_KEY")
qsService, err := qs.Init(configuration)
bucket, err := qsService.Bucket("test-bucket", "pek3a")

inputArgs := qs.DeleteMultipleObjectsInput{
    Objects: []*qs.KeyType{
       {
          Key: "test-object",
       },
    },
}
bucket.DeleteMultipleObjects(&inputArgs)

具体使用请参考文档和相应的 SDK

QingStor 对象存储新增六种语言 SDK

QingStor 对象存储作为海量通用的非结构化数据存储系统,自上线以来只提供了 Python 语言的 SDK。为了让更多的用户能够快速便捷的使用 QingStor 对象存储服务,我们新发布了包括 Go、Ruby、Swift、Java、JavaScript、PHP 在内的六种语言的 SDK。目前 QingStor 对象存储服务的 SDK 已覆盖主流编程语言和开发平台,极大降低了用户使用 QingStor 对象存储服务的成本。

与此同时,我们开源了 QingStor 对象存储的 API Specifications (OpenAPI 格式,亦称 Swagger 2.0 格式),及 SDK 生成工具 Snips。在 Snips 的帮助下,能够在短时间内开发出另一种语言的 SDK。我们欢迎第三方开发者参与到 Snips 项目中,贡献其它语言的 SDK。

QingStor SDKs

下面以 Go SDK 为例,演示如何使用 SDK 接入 QingStor。

1. 安装

可以直接使用 go get 安装:

$ go get -u github.com/yunify/qingstor-sdk-go

2. 初始化服务

发起请求前需要初始化服务

import (
    "github.com/yunify/qingstor-sdk-go/config"
    "github.com/yunify/qingstor-sdk-go/service"
)

configuration, err := config.New("ACCESS_KEY_ID", "SECRET_ACCESS_KEY")
qsService, err := service.Init(configuration)

 3. 创建 Bucket

初始化并创建 Bucket, 需要指定 Bucket 名称和所在 Zone

bucket, err := qsService.Bucket("test-bucket", "pek3a")
putBucketOutput, err := bucket.Put()

4. 上传一个 Object

// Open file
file, err := os.Open("~/Desktop/Screenshot.jpg")
defer file.Close()

// Put object
output, err := bucket.PutObject(
    "Screenshot.jpg",
    &service.PutObjectInput{
        Body: file,
    },
)

// Print the HTTP status code.
// Example: 201
fmt.Println(output.StatusCode)

 5. 删除一个 Object

output, err := bucket.DeleteObject("Screenshot.jpg")

// Print the HTTP status code.
// Example: 204
fmt.Println(output.StatusCode)

 

有关 SDK 的具体使用方法,请参考文档 https://docs.qingcloud.com/qingstor/sdk/

QingStor 支持存储空间外部镜像

QingStor 已支持存储空间外部镜像。对于设置了外部镜像源站的 Bucket,当请求的对象在 Bucket 中不存在时,系统将自动从源站获取(回源), 并写入到 Bucket 当中。在回源过程中,请求这个对象的客户端,有可能会下载到源站文件,也有可能收到重定向到源站相应路径的 302 请求。在回源完成后,客户端能够直接从 Bucket 中获取这个对象。

外部镜像属性

  • 源站点:  外部镜像回源的源站,形式为 “<protocol>://<host><:port>/<path>”。 protocol 的值可为 “http” 或 “https”,默认为 “http”。port 默认为 protocol 对应的端口。path 可为空。如果存储空间多次设置不同的源站,该存储空间的源站采用最后一次设置的值。

操作流程

  1. 示例源站是网易开源镜像站的 ubuntu 源(http://mirrors.163.com/ubuntu/)。要通过外部镜像功能回源的文件是 ls-lR.gz。external-mirror-1
  2. 设置外部镜像源站: 在 Bucket 页面点击设置 -> 外部镜像  ->  源站点external-mirror-2
  3. 示例 Bucket 的默认域名是 http://external-mirror.pek3a.qingstor.com。在浏览器内访问 http://external-mirror.pek3a.qingstor.com/ls-lR.gz (默认域名/要回源的文件),QingStor 将从 http://mirrors.163.com/ubuntu/ls-lR.gz 回源 。从下面的下载对话框可以看出,此次回源客户端可以直接下载源站文件。external-mirror-3external-mirror-4
  4. 在回源完毕后,可以在 Bucket 文件列表看到该对象。external-mirror-5
  5. 设置外部镜像的源站点之后,可以编辑源站并应用修改,或者关闭镜像功能。external-mirror-6

QingStor 支持为存储空间绑定自定义域名

QingStor 已支持为存储空间绑定自定义域名。单个存储空间可绑定多个自定义域名。绑定后,存储空间既可通过默认的域名 (Virtual-host Style, e.g. mybucket.pek3a.qingstor.com)或URL (Path Style, e.g. pek3a.qingstor.com/mybucket) 来访问,也可以通过自定义域名来访问。

自定义域名属性

  • domain 要绑定到存储空间的域名。该域名必须已完成备案,并且该域名到存储空间域名的CNAME记录必须已于域名服务商处注册且生效。

操作流程

  1. 在 Bucket 页面点击设置 -> 自定义域名 -> 绑定自定义域名user-defined-domain-0
  2. 配置绑定自定义域名user-defined-domain-1
  3. 配置完成。右键点击自定义域名,可以进行修改和删除操作。user-defined-domain-2

 

QingStor 支持为存储空间开启 CDN

QingStor 已经支持为存储空间开启 CDN ,可以提高存储空间中静态资源文件的访问速度。CDN 的回源地址将指向存储空间的访问地址。开启 CDN 的存储空间必须公开可读。

CDN属性

  • 节点类型
    CDN加速的服务类型,可以选择”网页”,“下载”或者“点播”。
  • 服务区域
    CDN加速的服务区域

操作流程

  1. 设置 Bucket 权限为公开读写cdn-0
  2. 在 Bucket 页面点击设置 -> CDN -> 启动 CDNcdn-1
  3. 配置 CDNcdn-2
  4. 配置完成cdn-3
  5. 点击”规则与监控”,可以配置缓存策略和访问控制,查看 CDN 的监控和日志cdn-4

QingStor 兼容 AWS S3 接口

为了使众多基于 AWS S3 开发的应用程序、开发工具及第三方服务,在不修改代码的前提下,更容易的接入到 QingStorQingStor 现已提供兼容 AWS S3 的接口。

如果用户的后台系统正在使用 AWS S3 存储数据,现在不需要修改任何程序代码,直接接入到 QingStor。并且 AWS S3 生态中提供的各类客户端工具 (AWS SDK, CLI),现在也可以无缝接入到 QingStor 中继续使用。

目前 QingStor 已经兼容AWS S3 所有主要功能的接口 ,后续随着 QingStor 功能的不断丰富,还会进一步提高兼容程度。更多兼容相关的内容,可查阅 官方文档

访问方式

QingStor 原生接口一样,AWS S3 兼容接口也支持两种形式的访问地址。

qingstor_s3_0

这里的访问地址AWS S3 语境里被称为 Endpoint

当用户使用以上任何一种风格 URL 发起请求时,将开始以 AWS S3 接口访问 QingStor。除了请求 Host 不同,所有请求头、请求正文、签名方式都应该符合 AWS S3 的规范。也就是说,从应用的视角看到的仿佛是 AWS S3 服务,但实际上操作的是 QingStor

签名验证

QingStor 兼容接口同时支持签名方法 AWS Signature Version 2 AWS Signature Version 4

在签名过程中,用户需要使用 QingCloud 密钥对 (QingCloud Access Key) 替换 AWS 密钥对 (AWS Access Key)Signature V4 版本中,使用 QingCloud Zone 替换 AWS Region

使用示例

大多数基于 AWS S3 开发的工具都有自定义访问地址的方法,下面以一些常用工具为例,说明如何接入 QingStor

 AWS SDK for Python

1. 安装 Boto 类库:

$ pip install boto

2. 编写程序:

$ cat boto-to-qingstor.py
import boto
conn = boto.connect_s3(
aws_access_key_id='PLLZOBTTZXGBNOWUFHZZ',
aws_secret_access_key='MnIjI58zC8AX07xotHXcm6grwFgOXhaJQHkTCX2X',
host='s3.pek3a.qingstor.com'
)
bucket = conn.get_bucket('mybucket')
key = bucket.get_key("mykey")

AWS CLI

1. 安装 AWS CLI 工具:

pip install awscli

2. 编辑配置文件:

$ cat ~/.aws/config
[profile qingstor]
region = pek3a
output = json
s3 =
signature_version = s3v4

3. 设置访问密钥:

$ cat ~/.aws/credentials
[qingstor]
aws_access_key_id = PLLZOBTTZXGBNOWUFHZZ
aws_secret_access_key = MnIjI58zC8AX07xotHXcm6grwFgOXhaJQHkTCX2X

4. 命令行执行:

$ aws s3api put-object --bucket mybucket --key puppy.jpg --body ~/Pictures/puppy.jpg --endpoint-url 'https://s3.pek3a.qingstor.com' --profile qingstor
{
"ETag": "\"c3872b49cb244269aad8cd4275a41c4a\""
}

s3fs

1. 安装 s3fs 工具 (Ubuntu 为例):

apt-get install s3fs

2. 设置访问密钥:

# cat /root/.s3fs/credentials
PLLZOBTTZXGBNOWUFHZZ:MnIjI58zC8AX07xotHXcm6grwFgOXhaJQHkTCX2X

3. 挂载 bucket 到本地目录:

# mkdir -p /mnt/mybucket
# s3fs mybucket /mnt/mybucket -o passwd_file=/root/.s3fs/credentials -o url=http://s3.pek3a.qingstor.com
# df -T | grep s3fs
s3fs fuse.s3fs 274877906944 0 274877906944 0% /mnt/mybucket

4. 测试文件系统操作:

# echo 'hello world' > /tmp/hello.txt
# cp -v /tmp/hello.txt /mnt/mybucket/
‘/tmp/hello.txt’ -> ‘/mnt/mybucket/hello.txt’
# ls -l /mnt/mybucket/hello.txt
---------- 1 root root 4635 Aug 11 23:26 /mnt/mybucket/hello.txt
# cat /mnt/mybucket/hello.txt
hello world

Transmit for iOS

1. 首先在AppStore 下载应用 Transmit

2. 打开应用添加新的 Server,选择 Amazon S3 并在 Address 处填写 s3.qingstor.com (注意这里与上文提到的访问地址有所不同,因为 Transmit 首屏需要获取所有的 bucket 列表,所以需要填写 QingStor global 的地址)

3. 填入在 QingCloud 控制台申请的密钥对,点击保存

4. 设置完成,点击文件夹进入查看文件列表

qingstor_s3_1 qingstor_s3_3 qingstor_s3_2

 

 

 

 

 

 

 

兼容工具

理论上所有基于 AWS S3 接口开发的客户端,只要涉及的接口实现了兼容,都可以直接对接 QingStor

实际测试中,我们已经证实可以兼容的工具包括:

  • AWS SDK for Python / PHP / Java / Ruby
  • AWS Command Line Interface (CLI)
  • s3fs
  • Hadoop s3a / distcp
  • ownCloud external storage
  • Transmit for iOS / macOS
  • Commvault