附录
最后更新于:2022-04-01 00:54:33
## URL安全的Base64编码
URL安全的Base64编码适用于以URL方式传递Base64编码结果的场景。该编码方式的基本过程是先将内容以Base64格式编码为字符串,然后检查该结果字符串,将字符串中的加号`+`换成中划线`-`,并且将斜杠`/`换成下划线`_`,同时尾部保持填充等号`=`。
详细编码规范请参见[RFC4648](http://www.ietf.org/rfc/rfc4648.txt)标准中的相关描述。
## 域名绑定
每个空间都可以绑定一个到多个自定义域名,以便于更方便的访问资源。
比如`www.qiniu.com`的所有静态资源均存放于一个叫`qiniu-resources`的公开空间中。并将该空间绑定到一个二级域名`i1.qiniu.com`,那么如果要在一个HTML页面中引用该空间的`logo.png`资源,大概的写法如下:
~~~
<img source="http://i1.qiniu.com/logo.png"></img>
~~~
这样既可以在一定程度上隐藏正在使用七牛云存储的事实,但更大的好处是如果需要从一个云存储迁移到另一个云存储,只需要修改域名DNS的CNAME设置,而无需更新网页源代码。
## 七牛ETag算法
七牛的 `hash/etag` 算法是公开的。算法大体如下:
### 小于或等于4M的文件
~~~
1. 对文件内容做sha1计算;
+---------------+
| <=4MB |
+---------------+
\ | /
\ sha1() /
\ | /
\ V /
+--+-----+
|1B| 20B |
2. 在sha1值(20字节)前拼上单个字节,值为0x16;
+--+-----+
| |
| \--- 文件内容的sha1值
|
\------ 固定为0x16
3. 对拼接好的21字节的二进制数据做url_safe_base64计算,所得结果即为ETag值。
~~~
### 大于4M的文件
~~~
1. 对文件内容按4M大小切块;
2. 对每个块做sha1计算;
+----------+----------+-------
| 4MB | 4MB | ...
+----------+----------+-------
\ | | | /
\ sha1() | sha1() /
\ | | | /
\ V | V /
+-----+-----+-------
| 20B | 20B | ...
+-----+-----+-------
\ | /
\ sha1() /
\ | /
\ V /
+--+-----+
|1B| 20B |
3. 对所有的 sha1 值拼接后做二次 sha1,
+--+-----+ 然后在二次 sha1 值前拼上单个字节,值为0x96;
| |
| \---- 二次sha1的值
\------- 固定为0x96
4. 对拼接好的21字节的二进制数据做url_safe_base64计算,所得结果即为ETag值。
~~~
### FAQ
1. 为何需要公开 `hash/etag` 算法?这个和 “消重” 问题有关,详细见:[如何避免用户上传相同的文件](http://kb.qiniu.com/53tubk96)。
2. 为何在 sha1 值前面加一个字节的标记位(0x16或0x96)?
0x16 = 22,而 2^22 = 4M。所以前面的 `0x16` 其实是文件按 4M 分块的意思。
0x96 = 0x80 | 0x16。其中的 `0x80` 表示这个文件是大文件(有多个分块),hash 值也经过了2重的 sha1 计算。
### 相关工具
[qetag](https://github.com/qiniu/qetag) 是一个计算文件在七牛云存储上的 hash 值(也是文件下载时的 etag 值)的实用程序。
内置数据处理功能
最后更新于:2022-04-01 00:54:31
为了有效提升开发者的工作效率,七牛云存储已经提供了大量非常有价值的内置数据处理功能。
## 图片处理
图片处理包括各种规格的缩略和裁剪,具体请参见[图片处理规格](http://developer.qiniu.com/docs/v6/api/reference/fop/image/index.html)。
## 图片水印
开发者可以在任何一张图片上按指定位置叠加一个水印。具体请参见[水印规格](http://developer.qiniu.com/docs/v6/api/reference/fop/image/watermark.html)。
## 音视频处理
七牛云存储服务内置了音频的转码等相关的数据处理功能。具体请参见[音频处理规格](http://developer.qiniu.com/docs/v6/api/reference/fop/av/avthumb.html)。
因为视频处理是非常耗时的操作,因此应使用异步的处理结果持久化机制。请参见[处理结果持久化](http://developer.qiniu.com/docs/v6/api/overview/fop/persistent-fop.html)。
## 音视频切片
音视频切片是指从一个音频或视频源按指定的偏移位置截取指定长度的音视频片段,主要用于支持HTTP Live Streaming回放。具体请参见[音视频切片](http://developer.qiniu.com/docs/v6/api/reference/fop/av/segtime.html)。
## 视频水印
七牛云存储服务内置了对视频打水印的功能。具体请参见[视频水印规格](http://developer.qiniu.com/docs/v6/api/reference/fop/av/video-watermark.html)。
## 视频截图
可以从视频中截取指定时刻的单帧画面并按指定大小缩放成图片。具体请参见[视频截图](http://developer.qiniu.com/docs/v6/api/reference/fop/av/vframe.html)。
## 其他
除以上常见的数据处理功能外,七牛云存储还内置了一些其他的数据处理功能,比如[资源下载二维码(qrcode)](http://developer.qiniu.com/docs/v6/api/reference/fop/qrcode.html)和[MD转HTML(md2html)](http://developer.qiniu.com/docs/v6/api/reference/fop/md2html.html)。
样式
最后更新于:2022-04-01 00:54:28
如果觉得 `url?|||` 这样的形式够冗长,还可以为这些串行的 `` 集合定义一个友好别名。如此一来,就可以用友好URL风格进行访问。
我们先来熟悉 [qrsctl](http://developer.qiniu.com/docs/v6/tools/qrsctl.html) 的两个命令行,
~~~
// 定义 url 和 fop 之间的分隔符为 separator
qrsctl separator <bucket> <separator>
// 定义 fop 的别名为 aliasName
qrsctl style <bucket> <aliasName> <fop>
~~~
例如:
~~~
// 定义 url 和 fop 之间的分隔符为 "-"
qrsctl separator <bucket> "-"
// 定义该管道fop 样式名为 "pipeline"
qrsctl style <bucket> "pipeline" "imageView2/2/h/200|watermark/1/image/aHR0cDovL3d3dy5iMS5xaW5pdWRuLmNvbS9pbWFnZXMvbG9nby0yLnBuZw=="
~~~
那么,以下两个 URL 则等价:
原始URL:
* [http://qiniuphotos.qiniudn.com/gogopher.jpg?imageView2/2/h/200|watermark/1/image/aHR0cDovL3d3dy5iMS5xaW5pdWRuLmNvbS9pbWFnZXMvbG9nby0yLnBuZw==](http://qiniuphotos.qiniudn.com/gogopher.jpg?imageView2/2/h/200|watermark/1/image/aHR0cDovL3d3dy5iMS5xaW5pdWRuLmNvbS9pbWFnZXMvbG9nby0yLnBuZw==)
友好风格URL:
* [http://qiniuphotos.qiniudn.com/gogopher.jpg-pipeline](http://qiniuphotos.qiniudn.com/gogopher.jpg-pipeline)
数据结果持久化
最后更新于:2022-04-01 00:54:26
之前我们介绍了常规的访问时数据处理机制,那种机制很适合像图片缩略图之类的访问,但无法应用数据处理过程较长的资源,比如花费时间超过一分钟的音频转码,更不用说可能处理时间超过一小时的视频转码。
**处理结果持久化机制(pfop)**用于满足这种处理时间较长的场景。
> 开发者可使用该功能对音视频进行异步转码,并将转码结果永久存储于空间中,从而大幅提升访问体验。
>
> 处理结果持久化功能还提供即时的处理状态通知和查询功能,因此开发者在开始执行音视频转码后还能随时获取转码进度信息。
处理结果持久化功能可以在两种场景下触发:
1. 在资源上传完成后自动触发处理流程;
2. 针对已存在空间中的资源手动触发处理流程;
下面分别描述这两种详细用法。
## 上传后自动触发数据处理
开发者如果希望在上传文件后自动触发数据处理过程,需要在构造[上传凭证](http://developer.qiniu.com/docs/v6/api/reference/security/upload-token.html "上传凭证")时在[上传策略](http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html "上传策略")中设置`persistentOps`和`persistentNotifyUrl`两个字段。
| 字段 | 类型 | 含义 |
| --- | --- | --- |
| `persistentOps` | string | 需要进行的数据处理命令,可以指定多个命令,以`;`分隔,具体含义见[persistentOps详解](http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html#put-policy-persistent-ops-explanation)。每一个数据处理命令都应遵循标准格式,参见[数据处理(fop)](http://developer.qiniu.com/docs/v6/api/overview/fop/fop.html "数据处理")。 |
| `persistentNotifyUrl` | string | 用户接收视频处理结果的接口URL。设置`persistentOps`字段时,本字段必须同时设置。未来该设置项将改为可选,如未设置,则只能使用返回的`persistentId`主动查询处理进度。 |
用户使用指定了`persistentOps`和`persistentNotifyUrl`的上传凭证上传一个文件之后,服务端返回的响应内容中会包含此次异步处理的进程ID`persistentId`,该ID可用于获取处理的进度和结果。
针对用户上传策略的不同,返回的`persistentId`字段会出现在不同的位置:
1. 设置`returnUrl`或`callbackUrl`,响应内容中直接带有`persistentId`字段;
2. 设置了`returnUrl`但没有设置`returnBody`,跳转过程附带的upload_ret参数解码后获得的结果中默认带有`persistentId`字段;
3. 设置了callbackUrl,但没有设置callbackBody,和之前一样,这种情况下上传会失败;
4. 设置了returnUrl或callbackUrl,且根据需求自定义了相应的Body(`returnBody` 或 `callbackBody`),要在Body中使用魔法变量`$(persistentId)` 来得到。
## 对已有资源手动触发数据处理
如果需要对已存在于空间中的资源进行处理并持久化处理结果,可按以下方式使用我们的异步处理接口:
~~~
POST /pfop HTTP/1.1
Host: api.qiniu.com
Content-Type: application/x-www-form-urlencoded
Authorization: QBox <AccessToken>
bucket=<urlEncodedBucket>&key=<urlEncodedKey>&fops=<urlEncodedFops>¬ifyURL=<urlEncodedPersistentNotifyUrl>&force=<Force>
~~~
其中的`AccessToken`的生成算法可参见[管理凭证规格](http://developer.qiniu.com/docs/v6/api/reference/security/access-token.html "管理凭证")。
正常情况下获得的返回:
~~~
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: <length>
{"persistentId": <persistentId>}
~~~
处理完成后会向用户指定的`notifyURL`发送处理结果,用户也可以根据`persistentId`来主动查询。详情可以参考:[处理状态通知和查询](http://developer.qiniu.com/docs/v6/api/overview/fop/persistent-fop.html#pfop-status)。
## 状态通知和查询
处理过程的状态可通过两种方式获取:
1. 上传时设定`persistentNotifyUrl`字段,则该URL会收到主动的通知。
服务端完成所有的数据处理后,会以 HTTP POST 的方式将处理状态发送给用户指定的通知URL。
2. 随时手动发起一个查询请求。
开发者可以使用上传时返回的`persistentId`来随时查询数据处理的状态。查询的接口为:
~~~
[GET] http://api.qiniu.com/status/get/prefop?id=<persistentId>
~~~
用户获得的数据处理状态是一个JSON字符串,内容格式如下:
~~~
{
"id": "<persistentId>",
"code": <overall_result>,
"desc": "<overall_result_description>",
"items": [
...
{
"cmd": "<fop_cmd>",
"code": <result>,
"desc": "<result_description>",
"error": "<error_description>",
"hash": "<result_hash>",
"key": "<result_key>"
}
...
]
}
~~~
状态内容中每个字段的详细含义如下:
| 字段 | 类型 | 含义 |
| --- | --- | --- |
| id | string | 数据处理的进程ID,即前文中的`persistentId`。 |
| code | int | 状态码,0 表示成功,1 表示等待处理,2 表示正在处理,3 表示处理失败,4 表示回调失败。 |
| desc | string | 状态码对应的详细描述。 |
| items | | JSON数组。包含每个数据处理操作的进度信息。 |
| cmd | string | 对应一个特定的数据处理命令。 |
| error | string | 如果处理失败,这个字段会给出详细的失败原因。 |
| hash | string | 数据处理结果的哈希值。 |
| key | string | 数据处理结果的唯一资源ID。数据处理结果可通过`http:///`访问。 |
## 示例
上传一个音频文件persistent.mp3,并设置上传策略中的`persistentOp`字段为这两个命令:`avthumb/mp3/aq/6/ar/16000`、`avthumb/mp3/ar/44100/ab/32k`。
通知接口或主动查询收到的处理状态内容将如下所示:
~~~
{
'code': 0,
'id': '168739cd2fn1g76f13',
'desc': 'The fop was completed successfully',
'items': [
{
'code': 0,
'hash': 'FvvxM7gMI6WfiuXlBgKbkzU67Tpa',
'cmd': 'avthumb/mp3/ar/44100/ab/32k',
'key': 'sFhZ4dSjB1zvL3De1UBX2qZ_VR0=/lgxucMCQso_KOW_YDM-_KVIeX6o5',
'error': '',
'desc': 'The fop was completed successfully'
},
{
'code': 0,
'hash': 'FpSzDMYJtP_UY_6EMIyaBe4awXp3',
'cmd': 'avthumb/mp3/aq/6/ar/16000',
'key': '1G8-OWwP3jPLvi7O3qOf7yCl4YI=/lgxucMCQso_KOW_YDM-_KVIeX6o5',
'error': '',
'desc': 'The fop was completed successfully'
}
]
}
~~~
访问链接:
* [原文件](http://t-test.qiniudn.com/persistent.mp3)
* [处理1(avthumb/mp3/aq/6/ar/16000)结果](http://t-test.qiniudn.com/1G8-OWwP3jPLvi7O3qOf7yCl4YI=/lgxucMCQso_KOW_YDM-_KVIeX6o5)
* [处理2(avthumb/mp3/ar/44100/ab/32k)结果](http://t-test.qiniudn.com/sFhZ4dSjB1zvL3De1UBX2qZ_VR0=/lgxucMCQso_KOW_YDM-_KVIeX6o5)
管道
最后更新于:2022-04-01 00:54:24
## 管道规格
七牛云存储的数据处理功能规格如下:
~~~
[GET] url?<fop>
~~~
即基于文件的URL通过问号传参来实现数据处理,`<fop>`表示数据处理指令及其所需要的参数,是File Operation的缩写,表示文件处理。
那么,将一个资源经由多个 `<fop>` 链式处理,各 `<fop>` 之间用竖线(`|`)分割,我们称之为管道(pipeline)。也称之为管道操作,熟悉Linux命令行的开发者可能会有更透彻的理解。
管道操作的规格如下:
~~~
[GET] url?<fop1>|<fop2>|<fop3>|<fopN>
~~~
`url` 获取可以参考[资源下载](http://developer.qiniu.com/docs/v6/api/overview/dn/)。
## 样例
将一个原图缩略,然后在缩略图上打上另外一个图片作为水印
* 原图
* [http://qiniuphotos.qiniudn.com/gogopher.jpg](http://qiniuphotos.qiniudn.com/gogopher.jpg)
* 基于原图生成缩略图
* [http://qiniuphotos.qiniudn.com/gogopher.jpg?imageView/2/h/200](http://qiniuphotos.qiniudn.com/gogopher.jpg?imageView/2/h/200)
* 在生成的缩略图之上打水印
* [http://qiniuphotos.qiniudn.com/gogopher.jpg?imageView/2/h/200|watermark/1/image/aHR0cDovL3d3dy5iMS5xaW5pdWRuLmNvbS9pbWFnZXMvbG9nby0yLnBuZw==](http://qiniuphotos.qiniudn.com/gogopher.jpg?imageView/2/h/200|watermark/1/image/aHR0cDovL3d3dy5iMS5xaW5pdWRuLmNvbS9pbWFnZXMvbG9nby0yLnBuZw==)
数据处理机制
最后更新于:2022-04-01 00:54:21
七牛云存储内建了一个非常高效易用的数据处理框架。数据处理框架可以管理和执行一系列符合规范的数据处理操作(fop)。开发者可以在访问资源时制定执行一个或多个数据处理指令,以直接获取经过处理后的结果。
比较典型的一个场景是图片查看。客户端可以上传一张高精度的图片,然后在查看图片的时候根据屏幕规格生成一张大小适宜的缩略图,比如为iPhone生成一张960x640大小的缩略图。这样既可以明显降低网络流量,而且可以提高图片显示速度,还能降低移动设备的内存占用。而要达到这样的效果非常简单:
原图([链接](http://qiniu-images.qiniudn.com/gogopher.jpg)):
~~~
http://qiniu-images.qiniudn.com/gogopher.jpg
~~~
针对该原图获取一个适合iPhone5屏幕尺寸的图片([链接](http://qiniu-images.qiniudn.com/gogopher.jpg?imageView/2/w/640/h/960)):
~~~
http://qiniu-images.qiniudn.com/gogopher.jpg?imageView/2/w/640/h/960
~~~
我们可以再定义图片样式比如叫iphone5,以缩短URL并提高可读性([链接](http://qiniu-images.qiniudn.com/gogopher.jpg-iphone5)): `http://qiniu-images.qiniudn.com/gogopher.jpg-iphone5`
一个常规的数据处理操作包含一个操作指令和若干操作参数,如下所示:
~~~
<fop>/<param1_value>/<param2_name>/<param2_value>/...
~~~
数据处理框架对于资源类型并没有限制,但是特定某个数据处理操作则会有各自适合的处理对象,比如对非图片类型的资源类型上应用缩略图操作可能会返回错误的结果。
数据处理操作的触发有以下几个机会和方式:
1. 访问资源时。如上面的例子所示范的,只需要在资源URL后加上具体数据操作指令和参数即可。
2. 资源上传时。上传时可在上传策略中设置异步数据处理,在资源上传完成时七牛云存储会以异步的方式执行数据处理操作,并持久化存储数据处理结果。支持查询数据处理操作的进度。具体请参见[上传后续动作 - 数据预处理](http://developer.qiniu.com/docs/v6/api/overview/up/response/persistent-op.html)。
3. 对已有资源手动触发处理流程。与上传时的数据处理支持相同,这个过程也为异步且可查询操作进度。具体请参见[处理结果持久化](http://developer.qiniu.com/docs/v6/api/overview/fop/persistent-fop.html)。
数据处理
最后更新于:2022-04-01 00:54:19
管理操作
最后更新于:2022-04-01 00:54:17
用户可以对存储在七牛云存储的资源进行管理和操作。与文件管理类似,七牛云存储资源管理的主要操作有:查看、移动、复制、删除及其对应的批量操作,另外还有更新镜像资源操作和修改资源元信息操作。
## 查看 (stat)
查看操作用于查看资源的基本信息,包含:文件哈希值、文件大小、媒体类型及上传时间。具体参考[获取资源信息(stat)](http://developer.qiniu.com/docs/v6/api/reference/rs/stat.html)。
## 移动 (move)
移动操作允许将一个资源在同一个Bucket或者不同的Bucket之间移动。例如,在bucket1下面有一个名为key1的资源,可以将这个资源key1移动到bucket1下面的key2,也可以将其移动到bucket2下的key2。通过移动操作可以实现文件重命名。具体参考[移动资源(move)](http://developer.qiniu.com/docs/v6/api/reference/rs/move.html)。
## 复制 (copy)
复制操作允许在同一个bucket进行,也可以在两个不同的bucket之间进行。与资源的移动操作相比,复制操作保留原文件,因此会增加用户的存储空间。具体参考[复制资源(copy)](http://developer.qiniu.com/docs/v6/api/reference/rs/copy.html)。
## 删除 (delete)
删除资源与删除文件类似,但是七牛云存储不提供回收站的功能,因此在删除前请确认待删除的资源确实不再需要。具体参考[删除资源(delete)](http://developer.qiniu.com/docs/v6/api/reference/rs/delete.html)。
## 批量操作 (batch)
除了对单一资源进行操作,还可以在一次请求中对多个资源进行批量的查看、移动、复制及删除操作。具体参考[批量操作(batch)](http://developer.qiniu.com/docs/v6/api/reference/rs/batch.html)。
## 更新镜像资源(prefetch)
对于设置了镜像存储的空间,从镜像源站抓取指定名称的资源并存储到该空间中。如果该空间中已存在该名称的资源,则将镜像源站的资源覆盖空间的资源。具体参考[更新镜像资源(prefetch)](http://developer.qiniu.com/docs/v6/api/reference/rs/prefetch.html)。
## 修改元信息 (chgm)
修改元信息操作用于修改资源的mimeType,可以在七牛服务器自动识别文件类型错误或者是类型描述不够详细的情况下,自定义的对空间已有资源的类型进行修改。具体参考[修改资源元信息 (chgm) ](http://developer.qiniu.com/docs/v6/api/reference/rs/chgm.html)。
安全机制
最后更新于:2022-04-01 00:54:15
资源管理属于比较敏感的操作。原则上所有的资源管理操作均应在业务服务端进行。
每一个资源管理请求均需在HTTP请求头部增加一个`Authorization`字段,其值为符合管理凭证规格的字符串,样例如下:
~~~
GET /stat/<EncodedEntryURI> HTTP/1.1
Host: rs.qiniu.com
Authorization: QBox <管理凭证>
~~~
关于凭证的生成规则,请参见[管理凭证规格](http://developer.qiniu.com/docs/v6/api/reference/security/access-token.html)。
资源管理
最后更新于:2022-04-01 00:54:12
高级下载
最后更新于:2022-04-01 00:54:10
## 特殊名字资源
特殊名字资源指资源名中包含了URL保留字符,如`?`、首字符为`/`、以及多个连续的`/`等。如果直接访问导致服务器错误理解URL格式。
要正常访问此类资源,请参见[特殊key资源的访问](http://kb.qiniu.com/52slk76w)。开发者应该在命名资源时尽小心使用这些URL保留字符。
下载设置
最后更新于:2022-04-01 00:54:08
## 自定义404响应
开发者可以要求七牛云存储在目标资源不存在时返回一个特定的内容,比如在网站上为这些找不到的资源显示一张特定的提示图片。
通过自定义404响应可以达成这个效果。开发者只需向指定的空间上传一个资源名为`errno-404`的文件即可。
## 自定义资源下载名
默认情况下,如果在浏览器中访问一个资源URL,浏览器都会试图直接在浏览器中打开这个资源,比如一张图片。如果希望浏览器的动作是下载而不是打开,可以给该URL添加参数`?attname=`,如下所示:
~~~
http://<domain>/<key>?attname=<file_name>
~~~
如果访问的URL是带[数据处理操作](http://developer.qiniu.com/docs/v6/api/overview/fop/index.html)的,那么可以给URL添加参数`&attname=`,多个[数据处理操作](http://developer.qiniu.com/docs/v6/api/overview/fop/index.html)间用[管道](http://developer.qiniu.com/docs/v6/api/overview/fop/pipeline.html)连接,如下所示:
~~~
http://<domain>/<key>?<fop>&attname=<file_name>
http://<domain>/<key>?<fop1>|<fop2>|<fop3>&attname=<file_name> (被下载的是fop3的处理结果)
~~~
当收到此指令时,七牛云存储会在响应中增加一个标准HTTP字段`Content-Disposition`,格式如下:
~~~
Content-Disposition: attachment;filename="<file_name>"
~~~
该字段告诉浏览器将资源下载成为指定的文件名`<file_name>`。下面是几个可体验的完整示例:
* **原图按照原图文件名下载:**
* [http://newdocs.qiniudn.com/gogopher.jpg?attname=](http://newdocs.qiniudn.com/gogopher.jpg?attname=)
* **原图按照文件名down.jpg下载:**
* [http://newdocs.qiniudn.com/gogopher.jpg?attname=down.jpg](http://newdocs.qiniudn.com/gogopher.jpg?attname=down.jpg)
* **原图先按照200x200大小缩放,再将处理结果按照文件名down2.jpg下载:**
* [http://newdocs.qiniudn.com/gogopher.jpg?imageView2/1/w/200/h/200&attname=down2.jpg](http://newdocs.qiniudn.com/gogopher.jpg?imageView2/1/w/200/h/200&attname=down2.jpg)
* **原图先按照200x200大小缩放,然后将缩放结果按照50x50裁剪,再将最后裁剪结果结果按照文件名down3.jpg下载:**
* [http://newdocs.qiniudn.com/gogopher.jpg?imageView2/1/w/200/h/200|imageMogr2/crop/50x50&attname=down3.jpg](http://newdocs.qiniudn.com/gogopher.jpg?imageView2/1/w/200/h/200|imageMogr2/crop/50x50&attname=down3.jpg)
资源下载
最后更新于:2022-04-01 00:54:05
## 下载机制
资源的下载采用`HTTP GET`方式(详见[RFC2616 标准](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35))。下载过程所需的参数作为URL参数:
~~~
http://<domain>/<key>?<param1>=<value1>&<param2>=<value2>...
~~~
下载过程也通过标准的HTTP头`Range`字段支持分片下载。用户可以在下载时通过设定该字段指定只下载该资源的一部分内容:
~~~
Range: bytes=<first-byte-pos>-<last-byte-pos>
~~~
支持`Range`字段相当于提供了断点续传功能,对于大资源的下载可以提供比较好的用户体验,比如暂停下载、网络中断并恢复后继续下载。
在移动应用我们经常看到一个设置叫**只在WIFI连接时下载**。这个功能就可以通过资源下载对`Range`字段的支持而比较容易的实现。客户端通过在网络连接切换时判断当前连接类型而自动判断是否应该暂停下载,这样可以避免因为大资源的下载而耗尽3G流量(比如安装包的下载)。
## 下载响应
资源下载的响应符合HTTP GET的规范,比如200表示下载成功。除了标准的HTTP字段比如`Content-Type`、`Content-Length`外还会携带一些扩展字段,如`ETag`、`X-Log`、`X-Reqid`等。这些扩展字段非常有助于排查问题。
关于HTTP扩展字段的更多信息,请参见[HTTP扩展字段](http://developer.qiniu.com/docs/v6/api/reference/extended-headers.html)。
如果下载过程中遇到任何错误,我们建议开发者将这些详细信息都写入日志,在请求技术支持时提供这些错误信息以便快速排查。
安全机制
最后更新于:2022-04-01 00:54:03
## 公开资源下载
公开资源下载非常简单,以HTTP GET方式访问资源URL即可。资源URL的构成如下:
~~~
http://<domain>/<key>
~~~
以上的``有两种形态:七牛子域名,自定义域名。
七牛子域名是一个创建空间时缺省分配的域名,开发者可以在[开发者平台 - 空间设置 - 域名设置](https://portal.qiniu.com/)查看该子域名。子域名通常类似于`example-images.u.qiniudn.com`,用户可以通过以下URL下载名为sunflower.jpg的资源:
~~~
http://example-images.u.qiniudn.com/sunflower.jpg
~~~
开发者可以申请为某特定空间绑定一个自定义域名,以通过这个域名访问资源,比如绑定了一个自定义域名`i.example.com`,就可以这样的方式访问同样的资源:
~~~
http://i.example.com/sunflower.jpg
~~~
## 私有资源下载
当用户将空间设置成私有后,所有对空间内资源的访问都必须获得授权。
私有资源下载也是通过以HTTP GET方式访问一个特定URL完成。私有资源URL与公开资源URL相比只是增加了两个参数`e`和`token`,分别表示过期时间和下载凭证。一个完整的私有资源URL如下所示:
~~~
http://<domain>/<key>?e=<deadline>&token=<downloadToken>
~~~
参数`e`表示URL的过期时间,采用[UNIX Epoch时间戳格式](http://en.wikipedia.org/wiki/Unix_time),单位为秒。超时的访问将返回401错误。
> 如果请求方的时钟未校准,可能会造成有效期验证不正常,比如直接认为已过期。因此需要进行时钟校准。
>
> 由于开发者无法保证客户端的时间都校准,所以应该在业务服务器上创建时间戳,并周期性校准业务服务器时钟。
参数`token`携带下载凭证。下载凭证是对资源访问的授权,不带下载凭证或下载凭证不合法都会导致401错误,表示验证失败。关于下载凭证的生成,请参见[下载凭证](http://developer.qiniu.com/docs/v6/api/reference/security/download-token.html "下载凭证")。
## 防盗链
下载还有一种常见的场景,即公开资源的防盗链,比如禁止特定来源域名的访问,禁止非浏览器发起的访问等。
我们可以通过HTTP协议支持的Referer机制(参见HTTP Referer)来进行相应的来源识别和管理。
防盗链是一个系统设置,不影响开发工作。如发现有盗链情况,开发者可在开发者平台的空间设置页面进行相应的设置。
下载
最后更新于:2022-04-01 00:54:01
上传后续动作
最后更新于:2022-04-01 00:53:58
## 简单反馈
如果资源上传成功,服务端会响应HTTP 200返回码,且在响应内容中包含两个字段:
* `hash`:已上传资源的校验码,供用户核对使用。
* `key`:目标资源的最终名字,可由七牛云存储自动命名;
以下是一个典型的上传成功反馈:
~~~
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"hash": "Fh8xVqod2MQ1mocfI4S4KpRL6D98",
"key": "gogopher.jpg",
}
~~~
如果资源上传失败,服务端会反馈相应的错误信息。比如,HTTP 401代表验证失败。此时相应内容中会包含详细错误信息。错误信息同样为JSON格式:{“error”:””}
以下是一个典型的上传失败反馈:
~~~
HTTP/1.1 400 Bad Request
Date: Mon, 05 Aug 2013 13:56:34 GMT
Server: nginx/1.0.14
Content-Type: application/json
Access-Control-Allow-Origin: *
Content-Length: 28
X-Log: MC;SBD:10;RBD:11;BDT:12;FOPD/400;FOPG:63/400;IO:109/400
X-Reqid: -RIAAIAI8UjcgRcT
X-Via: 1.1 jssq179:8080 (Cdn Cache Server V2.0), 1.1 jsyc96:9080 (Cdn Cache Server V2.0)
Connection: close
{
"error":"invalid argument"
}
~~~
这些返回的错误信息可以帮助开发者分析问题原因。完整的返回码信息请参见[返回码](http://developer.qiniu.com/docs/v6/api/reference/codes.html)。
从上面的错误示例中可以看到,响应头中还包含了一些以`X-`为前缀的扩展字段,如`X-Reqid`和`X-Log`等。这些扩展信息非常有助于问题定位。我们建议开发者将所有接收到的错误信息写到日志中,以便于我们的技术支持人员在协助分析问题时有足够详细的线索。
关于这些扩展字段的详细描述,请参见[HTTP扩展字段](http://developer.qiniu.com/docs/v6/api/reference/extended-headers.html)。
## 回调(callback)
开发者可以要求七牛云存储在某文件上传完成后向特定的URL发起一个回调请求。七牛云存储会将该回调的响应内容作为文件上传响应的一部分一并返回给客户端。回调的流程如下图所示:
![带回调的上传流程](http://developer.qiniu.com/docs/v6/api/overview/up/response/img/upload-with-callback.png)
要启用回调功能,业务服务器在签发上传凭证时需要设置[上传策略](http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html)中的`callbackUrl`字段,并且设置`callbackBody`字段。
### 回调地址
通过设定上传策略中的`callbackUrl`字段为一个有效的地址,即可让七牛云存储在文件上传完成后向该地址发起回调请求。
该地址可以是一个HTTP或者HTTPS的URL,允许公网访问。
如果需要传递自定义的请求内容,开发者可以考虑配合使用上传策略中的`callbackBody`字段。如果只有`callbackUrl`而没有`callbackBody`,回调服务器收到的请求内容将为空。
### 回调内容
同普通客户端直传和重定向上传一样,用户也可以控制回调中传递到客户回调服务器的反馈信息。`callbackBody`的格式如下:
~~~
<item>=(<magicvar>|<xvar>)[&<item>=(<magicvar>|<xvar>)...]
~~~
一个典型的`callbackBody`设置如下:
~~~
put_policy = '{
...
"callbackBody" : "name=$(fname)&hash=$(etag)&location=$(x:location)\
&price=$(x:price)&uid=123"
...
}'
~~~
上面的 `callbackBody` 示例中,混合使用了魔法变量(name,hash)、自定义变量(location,price)及自定义常量(uid)。 假设应用客户端发出了如下的上传请求:
~~~
<form method="post" action="http://upload.qiniu.com/" enctype="multipart/form-data">
<input name="key" type="hidden" value="sunflower.jpg">
<input name="x:location" value="Shanghai">
<input name="x:price" value="1500.00">
<input name="token" type="hidden" value="...">
<input name="file" type="file" />
</form>
~~~
其中,客户端发送了自定义变量的值`x:location = Shanghai`和`x:price = 1500.00`,七牛云存储将根据上传资源的实际情况填写魔法变量`$(fname)`和`$(etag)`的值。
完成上传后,七牛云存储便会构造出如下的回调信息:
~~~
name=sunflower.jpg&hash=Fn6qeQi4VDLQ347NiRm- \
RlQx_4O2&location=Shanghai&price=1500.00&uid=123
~~~
七牛云存储将这组数据作为请求Body发送至用户指定的回调服务器,请求方式为POST。回调服务器将接收到以下格式的请求内容:
~~~
POST /callback HTTP/1.1
Content-Type: application/x-www-form-urlencoded
User-Agent: qiniu go-sdk v6.0.0
Host: api.examples.com
Authorization: QBox iN7NgwM31j4-BZacMjPrOQBs34UG1maYCAQmhdCV:tDK-3f5xF3SJYEAwsll5g=
name=sunflower.jpg&hash=Fn6qeQi4VDLQ347NiRm- \
RlQx_4O2&location=Shanghai&price=1500.00&uid=123
~~~
回调服务器接收到回调请求后,负责生成七牛返回给客户端的数据(json格式),该数据作为此次回调请求的响应内容。如果回调成功,回调服务应对七牛云存储作出类似如下的响应(注意:回调响应内容由回调服务生成,以下仅作为示例):
~~~
HTTP/1.1 200 OK
Server: nginx/1.1.19
Date: Thu, 19 Dec 2013 06:27:30 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
{"success":true,"name":"sunflowerb.jpg"}
~~~
七牛云存储将上面的回调结果返回给客户端,客户端接收到以下回应:
~~~
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Server: nginx/1.4.4
Date: Thu, 19 Dec 2013 08:04:56 GMT
Pragma: no-cache
X-Log: BDT:4;BDT:2;LBD:13;rs.put:1048;rs-upload.putFile:2514;UP.CB:3088;UP:5603
X-Reqid: iDYAAPBicOGXLUET
{"success":true,"name":"sunflowerb.jpg"}
~~~
如果回调失败,七牛云存储会将返回给应用客户端[HTTP状态码579](http://developer.qiniu.com/docs/v6/api/reference/codes.html)以及详细的失败信息。
### 安全性
由于回调地址是公网可任意访问的,回调服务如何确认一次回调是合法的呢?
七牛云存储在回调时会对请求数据签名,并将结果包含在请求头Authorization字段中,示例如下:
~~~
Authorization:QBox iN7NgwM31j4-BZacMjPrOQBs34UG1maYCAQmhdCV:tDK-3f5xF3SJYEAwsll5g=
~~~
其中`QBox`为固定值,`iN7Ngw...dCV`为用户的Accesskey,`tDK-3f...5g=`为签名结果(encoded_data)
回调服务器可以通过以下方法验证其合法性:
* 获取AUTHORIZATION字段值中的签名结果部分encoded_data
* 根据Accesskey选取正确的SecretKey
* 获取明文:data = Request.URL.Path +”\n” +Request.Body
* 部分语言或框架无法直接获取请求body的原始数据,在自行拼接时应当注意,body中的数据是经过[URL转义](http://zh.wikipedia.org/wiki/%E7%99%BE%E5%88%86%E5%8F%B7%E7%BC%96%E7%A0%81)的
* 采用HMAC-SHA1签名算法,对明文data签名,秘钥为SecretKey,比较签名结果是否与Authorization中的encoded_data字段相同,如相同则表明这是一个合法的回调请求
以PHP语言为示例,验证代码如下:
~~~
/**
*C('accessKey')取得 AccessKey
*C('secretKey')取得 SecretKey
*callback.php 为回调地址的Path部分
*file_get_contents('php://input')获取RequestBody,其值形如:
*name=sunflower.jpg&hash=Fn6qeQi4VDLQ347NiRm-RlQx_4O2\
*&location=Shanghai&price=1500.00&uid=123
*/
function IsQiniuCallback(){
$authstr = $_SERVER['HTTP_AUTHORIZATION'];
if(strpos($authstr,"QBox ")!=0){
return false;
}
$auth = explode(":",substr($authstr,5));
if(sizeof($auth)!=2||$auth[0]!=C('accessKey')){
return false;
}
$data = "/callback.php\n".file_get_contents('php://input');
return URLSafeBase64Encode(hash_hmac('sha1',$data,C("secretKey"), true)) == $auth[1];
}
~~~
注意:如果回调数据包含用户的敏感数据,建议回调地址使用HTTPS协议
## 303重定向
HTTP 303重定向(参见[RFC 2616 - 10.3.4](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.4))是HTTP 1.1规范的组成部分。
服务器可以通过返回303状态码告诉客户端,本次请求的内容可以通过返回的跳转URL取到,因此客户端应该重定向到新的URL。
该技术被广泛用于网页开发领域,如在文件上传完成后让客户端自动重定向到一个上传成功的结果页面。
七牛云存储的资源上传后续动作也支持303重定向功能。
在构造[上传凭证](http://developer.qiniu.com/docs/v6/api/reference/security/upload-token.html)时,开发者可以通过设置[上传策略](http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html)中的`returnUrl`参数以激活303重定向功能。在成功完成上传后,服务端会向客户端返回HTTP 303状态码,并在`Location`字段中携带上传时指定的重定向地址。如下所示:
~~~
HTTP/1.1 303 See Other
Location: <returnUrl>
~~~
客户端收到这样的反馈后,应按HTTP 1.1标准将当前页面重定向到`Location`字段所指定的URL。
主流浏览器都能正确的支持该跳转操作。
如果上传策略中还设定了自定义返回内容`returnBody`,则到时服务端返回的303响应中的`Location`字段也会包含自定义返回内容。参数值采用[URL安全的Base64编码](http://developer.qiniu.com/docs/v6/api/overview/appendix.html#urlsafe-base64)。此时的响应内容如下所示:
~~~
HTTP/1.1 303 See Other
Location: <returnUrl>?upload_ret=<encoded_return_body>
~~~
如果希望返回的自定义返回内容能得到正确处理,重定向URL所对应的服务器需支持请求参数`upload_ret`。
## 自定义响应内容(returnBody)
[简单反馈](http://developer.qiniu.com/docs/v6/api/overview/up/response/simple-response.html)只会包含资源的最基本信息,但很多情况下用户都希望得到更多的资源信息。
用户可以通过[资源管理](http://developer.qiniu.com/docs/v6/api/overview/rs/index.html)和[数据处理](http://developer.qiniu.com/docs/v6/api/overview/fop/index.html)功能获得资源的扩展信息。但这些都需要用户发起一个新请求。七牛云存储支持在上传请求的响应中直接返回客户端需要的资源扩展信息。
在生成[上传凭证](http://developer.qiniu.com/docs/v6/api/reference/security/upload-token.html)时,开发者可以通过设置[上传策略](http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html)中的`returnBody`字段指定需要返回的信息,比如资源的大小、类型,图片的尺寸等等。
`returnBody`实际上是一个用户定义的反馈信息模板,**内容必须用JSON格式表达**。下面是一个示例:
~~~
{
"foo": "bar",
"name": $(fname),
"size": $(fsize),
"type": $(mimeType),
"hash": $(etag),
"w": $(imageInfo.width),
"h": $(imageInfo.height),
"color": $(exif.ColorSpace.val)
}
~~~
用户可以在在`returnBody`中使用[变量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html),包括[魔法变量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#magicvar)和[自定义变量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar)。七牛云存储会将这些变量替换为对应实际值,然后作为响应内容反馈给用户,如下所示:
~~~
{
"foo": "bar",
"name": "gogopher.jpg",
"size": 214513,
"type": "image/jpg",
"hash": "Fh8xVqod2MQ1mocfI4S4KpRL6D98",
"w": 640,
"h": 480,
"color": "sRGB"
}
~~~
需要注意的是,`returnBody`不能在启用了回调时使用。如果上传策略中通过设置`callbackUrl`字段启用了回调,`returnBody`将直接被忽略。
在回调模式中如果也想自定义响应内容,请在生成上传凭证时设置上传策略中的`callbackBody`字段。更多关于回调模式的解释,请参见[回调(callback)](http://developer.qiniu.com/docs/v6/api/overview/up/response/callback.html)。
## 变量
变量是七牛云存储同用户交换数据的机制,引入变量概念的目的在于更灵活的控制上传后续动作中的内容组织和传递。可以认为变量是一种占位符,七牛云存储会将占位符按约定替换为实际内容。
在构造[上传策略](http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html)时,可在上传策略的`returnBody`和`callbackBody`字段内容中使用变量。
变量分为两种:[魔法变量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#magicvar)和[自定义变量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar)。魔法变量是系统提供的一系列预定义变量,可直接使用,而自定义变量则由调用方指定,通常应对应于上传时的表单参数。服务端会将这些上传参数的具体值返回给调用方。
### 魔法变量
魔法变量是一组预先定义的变量,可以使用 `$(var)` 或 `$(var.field_name)` 形式求值。
目前可用的魔法变量如下:
| 变量名 | 包含子项 | 变量说明 | 适用范围 |
| :-- | :-- | --- | --- |
| bucket | 获得上传的目标空间名。 |
| key | 获得文件保存在空间中的资源名。 |
| etag | 文件上传成功后的[Etag](http://en.wikipedia.org/wiki/HTTP_ETag)。若上传时未指定资源ID,Etag将作为资源ID使用。 |
| fname | 上传的原始文件名。 | 不支持用于`分片上传` |
| fsize | 资源尺寸,单位为字节。 |
| mimeType | 资源类型,比如JPG图片的资源类型为`image/jpg`。 |
| endUser | 上传时指定的`endUser`字段,通常用于区分不同终端用户的请求。 |
| persistentId | 音视频转码持久化的进度查询ID。 |
| exif | 是 | 获取所上传图片的[EXIF](http://en.wikipedia.org/wiki/Exchangeable_image_file_format)信息。
该变量包含子字段,比如对`$(exif.ApertureValue.val)`取值将得到该图片拍摄时的光圈值。
| 暂不支持用于`saveKey`中 |
| imageInfo | 是 | 获取所上传图片的基本信息。
该变量包含子字段,比如对`$(imageInfo.width)`取值将得到该图片的宽度。
| 暂不支持用于`saveKey`中 |
| year | 上传时的年份。 | 暂不支持用于’returnBody’、’callbackBody’中 |
| mon | 上传时的月份。 | 暂不支持用于’returnBody’、’callbackBody’中 |
| day | 上传时的日期。 | 暂不支持用于’returnBody’、’callbackBody’中 |
| hour | 上传时的小时。 | 暂不支持用于’returnBody’、’callbackBody’中 |
| min | 上传时的分钟。 | 暂不支持用于’returnBody’、’callbackBody’中 |
| sec | 上传时的秒钟。 | 暂不支持用于’returnBody’、’callbackBody’中 |
| avinfo | 是 | 音视频资源的元信息。 | 暂不支持用于’saveKey’中 |
| imageAve | 图片主色调,算法由[Camera360](http://hr.camera360.com/)友情提供。 |
| ext | 上传资源的后缀名,通过自动检测的 mimeType 或者原文件的后缀来获取。 | 不支持用于`分片上传` |
| uuid | 生成uuid | 暂不支持用于’saveKey’中 |
| bodySha1 | callbackBody的sha1(hex编码) | 只支持用于’callbackUrl’中 |
魔法变量支持`$(.)`形式的访问子项,例如:
* $()
* $(.)
* $(..)
求值举例:
* `$(bucket)` - 获得上传目标bucket名字
* `$(imageInfo)` - 获取当前上传图片的基本属性信息
* `$(imageInfo.height)` - 获取当前上传图片的原始高度
魔法变量不支持访问子项为数组形式
* **不支持**$([0])
* **不支持**$(.[0])
变量`avinfo`在[`returnBody`](http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html#put-policy-return-body)中返回的格式不同于url触发返回的`avinfo`格式,`avinfo`在[魔法变量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#magicvar)中的类型如下(内容经过格式化以便阅读):
~~~
{
"audio":{
"bit_rate":"64028",
"channels":1,
"codec_name":"mp3",
"codec_type":"audio",
"duration":"30.105556",
"index":1,
"nb_frames":"1153",
"r_frame_rate":"0/0",
"sample_fmt":"s16p",
"sample_rate":"44100",
"start_time":"0.000000",
"tags":{
"creation_time":"2012-10-21 01:13:54"
}
},
"format":{
"bit_rate":"918325",
"duration":"30.106000",
"format_long_name":"QuickTime / MOV",
"format_name":"mov,mp4,m4a,3gp,3g2,mj2",
"nb_streams":2,
"size":"3455888",
"start_time":"0.000000",
"tags":{
"creation_time":"2012-10-21 01:13:54"
}
},
"video":{
"bit_rate":"856559",
"codec_name":"h264",
"codec_type":"video",
"display_aspect_ratio":"4:3",
"duration":"29.791667",
"height":480,
"index":0,
"nb_frames":"715",
"pix_fmt":"yuv420p",
"r_frame_rate":"24/1",
"sample_aspect_ratio":"1:1",
"start_time":"0.000000",
"tags":{
"creation_time":"2012-10-21 01:13:54"
},
"width":640
}
}
~~~
变量`exif`的类型如下(内容经过格式化以便阅读,具体细节请参考[EXIF技术白皮书](http://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf)):
~~~
{
"DateTime" : {
"type" : 2,
"val" : "2011:11:19 17:09:23"
},
"ExposureBiasValue" : {
"type" : 10,
"val" : "0.33 EV"
},
"ExposureTime" : {
"type" : 5,
"val" : "1/50 sec."
},
"Model" : {
"type" : 2,
"val" : "Canon EOS 600D"
},
"ISOSpeedRatings" : {
"type" : 3,
"val" : "3200"
},
"ResolutionUnit" : {
"type" : 3,
"val" : " 英寸"
},
...后续内容已省略...
}
~~~
变量`imageInfo`对应的类型如下(内容经过格式化以便阅读):
~~~
{
"format": "jpeg",
"width": 640,
"height": 427,
"colorModel": "ycbcr"
}
~~~
如果变量取值失败(比如在上传策略中指定了一个并不存在的魔法变量),响应内容中对应的变量将被赋予空值。
### 自定义变量
应用客户端则在上传请求中设定自定义变量的值。七牛云存储收到这些变量信息后,置换掉`returnBody`和`callbackBody`中的自定义变量设置,形成最终的反馈结果。
自定义变量的行为同魔法变量基本一致,但变量名必须以`x:`开始。下面是一个自定义变量的示例:
用户设置了如下的`callbackBody`:
~~~
put_policy = '{
...
"callbackBody" : "name=$(fname)&hash=$(etag)&location=$(x:location)&price=$(x:price)"
...
}'
~~~
这个例子中的`$(x:location)`和`$(x:price)`就是自定义变量。
之后,用户的客户端构造了如下请求:
~~~
<form method="post" action="http://upload.qiniu.com/"
enctype="multipart/form-data">
<input name="key" type="hidden" value="sunflower.jpg">
<input name="x:location" type="hidden" value="Shanghai">
<input name="x:price" type="hidden" value="1500.00">
<input name="token" type="hidden" value="...">
<input name="file" type="file" />
</form>
~~~
文件上传完成后,服务端会将请求中`x:location`和`x:price`的值,替换`callbackBody`中的自定义变量:
~~~
name=sunflower.jpg&hash=Fn6qeQi4VDLQ347NiRm- \
RlQx_4O2&location=Shanghai&price=1500.00
~~~
然后,七牛云存储将此结果进行[URL安全的Base64编码](http://developer.qiniu.com/docs/v6/api/overview/appendix.html#urlsafe-base64),作为回调请求的Body调用`callbackUrl`指定的回调服务器。
如果变量取值失败(比如在上传策略中指定了一个并不存在的表单变量),响应内容中对应的变量将被赋予空值。
## 数据预处理
在生成上传凭证时,开发者可以通过在[上传策略](http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html)中指定`persistentOp`和`persistentNotifyUrl`字段来设置异步数据处理动作。当资源上传完成后,设置的数据处理动作就会被以异步方式启动。七牛云存储将立刻将响应内容返回给客户端,并不会等待数据处理动作完成。
关于数据处理结果持久化相关的详细内容,请参见[处理结果持久化(pfop)](http://developer.qiniu.com/docs/v6/api/overview/fop/persistent-fop.html)中的相关描述。
分片上传(断点续传)
最后更新于:2022-04-01 00:53:56
分片上传功能支持将一个文件切割为一系列特定大小的小数据片,分别将这些小数据片分别上传到服务端,全部上传完后再在服务端将这些小数据片合并成为一个资源。[上传模型](http://developer.qiniu.com/docs/v6/api/overview/up/upload-models.html "上传模型")中对分片上传的特点进行了完整的阐述。
## 关键概念
分片上传引入了两个概念:**块**(block)和**片**(chunk)。每个**块**由一到多个**片**组成,而一个资源则由一到多个**块**组成。他们之间的关系可以用下图表述:
![资源、块、片的关系](http://developer.qiniu.com/docs/v6/api/overview/up/img/chunk-concept.png "资源、块、片的关系")
**块**是服务端的永久数据存储单位,**片**则只在分片上传过程中作为临时存储的单位。服务端会以约一个月为单位周期性的清除上传后未被合并为块的数据片。
## 基本流程
与分片上传相关的API有这几个:[创建块(mkblk)](http://developer.qiniu.com/docs/v6/api/reference/up/mkblk.html)、[上传片(bput)](http://developer.qiniu.com/docs/v6/api/reference/up/bput.html)、[创建资源(mkfile)](http://developer.qiniu.com/docs/v6/api/reference/up/mkfile.html)。一个完整的分片上传流程可用下图表示:
![分片上传流程](http://developer.qiniu.com/docs/v6/api/overview/up/img/chunked-upload-workflow.png)
其中的关键要点如下:
1. 将待上传的文件按预定义的4MB块大小切分为若干个块。如果这个文件小于4MB,当然也就只有一个块;
2. 将每个块再按预定义的片大小切分为若干个片,先在服务端创建一个相应块(通过调用[mkblk](http://developer.qiniu.com/docs/v6/api/reference/up/mkblk.html),并带上第一个片的内容),然后再循环将所有剩下的片全部上传(通过调用[bput](http://developer.qiniu.com/docs/v6/api/reference/up/bput.html),从而完成一个块的上传);
3. 在所有块上传完成后,通过调用[mkfile](http://developer.qiniu.com/docs/v6/api/reference/up/mkfile.html)将这些上传完成的块信息再严格的按顺序组装出一个逻辑资源的元信息,从而完成整个资源的分片上传过程。
如要更准确的理解这个基本流程,可以通过阅读SDK源代码。所有SDK的源代码都公开托管在[Github](http://github.com/qiniu)上。
## 并发上传
由于之前介绍的片上传过程中的Context机制,每个块内部只能按顺序逐一上传该块所切分好的片。而每个块之间相互独立,因此若干个块可以同时进行传输而不会相互干扰,因此我们可以利用这个特征实现并发上传特性。
每个文件对应的最大理论并发上传数量也即该文件可划分的块数量。当然这个理论数量也受到很多其他因素的制约,比如像iOS限定了每个APP最多只能开4个并发HTTP连接,也即在iOS上,无论有多少个块,最大的并发上传数量不可能超过4个。并不是并发数量越大上传速度就会越快。因此在实际开发中,通常会使用线程池(Thread Pool)技术来控制并发数量。
## 断点续传
虽然片的存在周期并非永久,但已足以实现断点续传机制。
每成功上传一个片,客户端都会收到服务端返回一个代表当前已上传多少片的进度信息,我们称之为Context。上传下一个片时应提供前一个片上传成功后返回的Context。因此,这个Context可以认为是片传输进度的一个标记。
如果上传过程中,服务端发现一个块已经被片数据装满,那么最后一个片上传成功后返回的Context将是一个特殊的值`EOB`,告诉客户端不要再往这个块附加更多的片。
如果客户端在每次收到Context信息时都将其持久化到本地,即使客户端程序意外崩溃或正常重启,都可以在启动时读取上一次上传成功的片对应的Context,从而接着继续传输剩余片。这个效果我们称之为断点续传。
断点续传功能在上传一个需要较长时间比如一天时间才能上传完毕的大文件时尤其有价值,毕竟我们很难保证这段很长的时间内客户端都不会被关闭,且网络也一直处于连接状态。当前主流的移动平台(iOS、Android、Windows Phone 8)都有监测非活动应用并自动将其关闭的功能,这意味着在移动平台上我们要上传一个大文件时更容易遇到中途程序突然被关闭的情况,断点续传也就更有价值。
支持断点续传功能之后,在客户端很自然可以支持一个新功能:暂停或恢复某个文传的上传过程。
## 上传后续动作
我们曾在[上传模型](http://developer.qiniu.com/docs/v6/api/overview/up/upload-models.html "上传模型")中提过,在上传时开发者可以指定上传完成后服务端的后续动作,比如回调、自定义返回内容、303重定向等。可设置的后续动作与[表单上传](http://developer.qiniu.com/docs/v6/api/overview/up/form-upload.html "表单上传")中完全一致。
这里需要明确的是,虽然后续动作在生成[上传凭证](http://developer.qiniu.com/docs/v6/api/reference/security/upload-token.html "上传凭证")时已经指定,但这些后续动作只在服务端处理完mkfile请求后才会发生,而且也只有mkfile请求的内容可以包含[变量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html "变量")。
## 在线示例
[在线断点继上传示例](http://jsfiddle.net/gh/get/extjs/4.2/icattlecoder/jsfiddle/tree/master/resumbleupload)
表单上传
最后更新于:2022-04-01 00:53:54
表单上传功能适用于文件内容可以在一次HTTP请求即可传递完成的场景。该功能非常适合于在浏览器中使用HTML表单上传资源,或者在不需要处理复杂情况的客户端开发中使用。
开发者只要组装一个符合**HTML文件上传表单**规范(参见[RFC1867](http://www.ietf.org/rfc/rfc1867.txt))的HTTP请求,并以POST方式向域名`upload.qiniu.com`发起这个请求,即可将指定文件上传到服务端。业务逻辑非常简单明了。
## 使用方法
我们可以用如下的HTML表单来描述表单上传的基本用法:
~~~
<form method="post" action="http://upload.qiniu.com/"
enctype="multipart/form-data">
<input name="key" type="hidden" value="<resource_key>">
<input name="x:<custom_name>" type="hidden" value="<custom_value>">
<input name="token" type="hidden" value="<upload_token>">
<input name="file" type="file" />
<input name="crc32" type="hidden" />
<input name="accept" type="hidden" />
</form>
~~~
HTML表单上传的几个关键参数说明如下:
| 名称 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| token | string | 是 | 必须是一个符合相应规格的[上传凭证](http://developer.qiniu.com/docs/v6/api/reference/security/upload-token.html),否则会返回401表示权限认证失败。 |
| file | file | 是 | 文件本身。 |
| key | string | 否 | 资源名,必须为UTF-8编码。`注:` 如果在生成上传凭证的[上传策略](http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html) 中 `scope`指定为:`<bucket>:<key>`, 则该字段也必须指定。 |
| x: | string | 否 | [自定义变量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar),必须以 `x:` 开头命名,不限个数。里面的内容将在 `callbackBody` 参数中的 `$(x:custom_field_name)` 求值时使用。 |
| crc32 | string | 否 | 上传内容的 CRC32 校验码。如填入,则七牛服务器会使用此值进行内容检验。 |
| accept | string | 否 | 当 HTTP 请求指定 `Accept` 头部时,七牛会返回的 `Content-Type` 头部的值。该值用于兼容低版本 IE 浏览器行为。低版本 IE 浏览器在 multiform 返回 `application/json` 的时候会表现为下载,返回`text/plain` 才会显示返回内容。 |
上传过程在一个HTTP请求和响应中完成,因此该过程将阻塞直到文件传输成功完成或失败为止。如果文件较大,或者网络环境较差,可能会导致HTTP连接超时而上传失败。若发生这种情况,开发者需要考虑换用更安全但也相对复杂的[分片上传](http://developer.qiniu.com/docs/v6/api/overview/up/chunked-upload.html)功能。
提交以上这个HTML表单而生成的HTTP请求内容大致如下所示:
~~~
POST http://upload.qiniu.com/
Content-Type: multipart/form-data; boundary=<Boundary>
--<Boundary>
Content-Disposition: form-data; name="key"
<resource_key>
--<Boundary>
Content-Disposition: form-data; name="x:<custom_field_name>"
<custom_value>
--<Boundary>
Content-Disposition: form-data; name="token"
<upload_token>
--<Boundary>
Content-Disposition: form-data; name="file"; filename="[文件名]"
Content-Type: <MimeType>
[文件内容]
--<Boundary>--
~~~
在非网页开发的场景中,开发者完全可以自行组装这个HTML表单请求。考虑到各个平台上的网络库都已经对HTML文件上传表单有非常完整的支持,组装这个请求的过程将会非常轻松。
## 后续动作
我们可以在生成上传凭证时指定一系列的参数,以控制服务器在文件上传完成后的后续动作。我们将在[上传后续动作](http://developer.qiniu.com/docs/v6/api/overview/up/response/)中详细描述各种参数的用法和作用。
另外如果需要,我们可以在表单参数中增加一系列的[魔法变量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#magicvar)和[自定义变量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar)。上述表单例子中的``就是变量的使用方法示意。我们可以将其更换为一系列魔法变量或自定义变量。
变量将会在回调和自定义返回内容中起到极大的作用。具体用法请参见[回调](http://developer.qiniu.com/docs/v6/api/overview/up/response/callback.html)和[自定义返回内容](http://developer.qiniu.com/docs/v6/api/overview/up/response/response-body.html)对应的使用方法描述。
上传模型
最后更新于:2022-04-01 00:53:52
资源上传服务支持多种上传模式和响应模式。开发者可以根据需要组合使用两种模式,以最快的速度完成期望的业务流程。
## 上传类型
开发者可以选择以下几种上传类型来完成一个文件的上传过程。
1. 表单上传
该模型在一个单一的HTTP POST请求中完成一个文件的上传,比较适合于简单的应用场景和尺寸较小的文件。
关于表单上传的使用细节,请参见[表单上传](http://developer.qiniu.com/docs/v6/api/overview/up/form-upload.html)。
2. 分片上传
顾名思义,分片上传是将一个文件分为多个尺寸相同的小数据块,每个小数据块以一个独立的HTTP请求分别上传。所有小数据块都上传完成后,再发送一个请求在服务端将这些小数据块组织回一个逻辑资源,以完成这个上传过程。
相比简单上传而言,切片上传机制可以提供以下几个明显的好处:
1. 适合于尺寸较大的文件传输,通过切片来避免单个HTTP数据量过大而导致连接超时;
2. 在网络条件较差的环境下,较小的尺寸可以有较高的上传成功率,从而避免无休止的失败重试;
3. 超过4MB的大文件可以划分为多个4MB大小的数据块并发上传;
4. 支持断点续传;
相比简单上传,分片上传需要多次HTTP请求才能完成上传过程,因此必然会有额外的成本开销。另外代码的复杂度也会有明显增加,因此选择是否使用分片上传时应谨慎评估使用该方法的必要性。
分片上传的相关细节请参见[分片上传](chunked-upload)。
## 响应类型
从结果响应的角度,上传模型支持几种不同的响应方式和通知目标。
1. 简单反馈
简单反馈是指最直接的HTTP响应方式。客户端发起一次上传请求,然后等待服务端返回结果。服务端在处理完该次上传请求后,将处理结果以HTTP响应的方式反馈给客户端。
简单反馈的相关细节请参见[简单反馈](http://developer.qiniu.com/docs/v6/api/overview/up/response/simple-response.html)。
2. 303重定向
303重定向通常在浏览器上传的场景中使用。浏览器中的网页可以在发起上传请求的同时通知服务器,一旦上传成功,服务器应该返回HTTP 303状态码并带上一个重定向URL。浏览器在收到服务器返回的这个重定向指令后,将当前页面跳转到对应的重定向URL。
303重定向的使用细节请参见[303重定向](http://developer.qiniu.com/docs/v6/api/overview/up/response/redirect.html)。
3. 回调通知
回调通知是指客户端在上传时指定服务端在处理完上传请求后,应该通知某个特定服务器,在该服务器确认接收了该回调后才将所有结果返回给客户端。
因为加入了回调请求和响应的过程,相比简单上传,使用回调通知机制一般会导致客户端花费更多的等待时间。
回调通知的使用细节请参见[回调通知](http://developer.qiniu.com/docs/v6/api/overview/up/response/callback.html)。
4. 异步数据处理
客户端可以要求服务端在资源上传完成后以异步的方式开始处理刚刚上传的资源。要达到这个目标,可以在上传时指定相应的数据处理操作和参数。所有七牛云存储已经支持的数据处理服务均可以作为该请求的设置目标。
异步数据处理的使用细节请参见[异步数据处理](http://developer.qiniu.com/docs/v6/api/overview/up/response/persistent-op.html)。
## 授权机制
资源的上传授权机制采用[上传凭证](http://developer.qiniu.com/docs/v6/api/overview/security.html#upload-token)。
## 常见问题
[上传成功后返回的JSON会导致IE弹出下载框?](http://kb.qiniu.com/5487y5np)
上传
最后更新于:2022-04-01 00:53:49