# tus 可恢复上传协议
**版本:** 1.0.0 ([SemVer](http://semver.org))
**日期:** 2016年3月25日
**作者:** [Felix Geisendörfer](https://twitter.com/felixge), [Kevin van Zonneveld](https://twitter.com/kvz), [Tim Koschützki](https://twitter.com/tim_kos), [Naren Venkataraman](https://github.com/vayam), [Marius Kleidl](https://twitter.com/Acconut_)
**贡献者:**
[Bruno de Carvalho](https://github.com/biasedbit),
[James Butler](https://github.com/sandfox),
[Øystein Steimler](https://github.com/cybic),
[Sam Rijs](https://github.com/srijs),
[Khang Toh](https://github.com/khangtoh),
[Jacques Boscq](https://github.com/Amodio),
[Jérémy FRERE](https://github.com/jerefrer),
[Pieter Hintjens](https://github.com/hintjens),
[Stephan Seidt](https://github.com/ehd),
[Aran Wilkinson](https://github.com/aranw),
[Svein Ove Aas](https://github.com/Baughn),
[Oliver Anan](https://github.com/noptic),
[Tim](https://github.com/schmerg),
[j4james](https://github.com/j4james),
[Julian Reschke](https://github.com/reschke),
[Evert Pot](https://github.com/evert),
[Jochen Kupperschmidt](https://github.com/homeworkprod),
[Andrew Fenn](https://github.com/andrewfenn),
[Kevin Swiber](https://github.com/kevinswiber),
[Jan Kohlhof](https://github.com/0x20h),
[eno](https://github.com/radiospiel),
[Luke Arduini](https://github.com/luk-),
[Jim Schmid](https://github.com/sheeep),
[Jeffrey 'jf' Lim](https://github.com/jf),
[Daniel Lopretto](https://github.com/timemachine3030),
[Mark Murphy](https://github.com/MarkMurphy),
[Peter Darrow](https://github.com/pmdarrow),
[Gargaj](https://github.com/Gargaj),
[Tomasz Rydzyński](https://github.com/qsorix),
[Tino de Bruijn](https://github.com/tino),
[Jonas mg](https://github.com/kless),
[Christian Ulbrich](https://github.com/ChristianUlbrich),
[Jon Gjengset](https://github.com/jonhoo),
[Michael Elovskikh](https://github.com/wronglink),
[Rick Olson](https://github.com/technoweenie),
[J. Ryan Stinnett](https://convolv.es),
[Ifedapo Olarewaju](https://github.com/ifedapoolarewaju),
[Robert Nagy](https://github.com/ronag)
文档中的关键词**必须**(MUST)、**不得**(MUST NOT)、**需要**(REQUIRED)、**应**(SHALL)、**不应**(SHALL NOT)、**应该**(SHOULD)、**不应该**(SHOULD NOT)、**推荐**(RECOMMENDED)、**可以**(MAY)和**可选**(OPTIONAL)应按照[RFC 2119](http://www.ietf.org/rfc/rfc2119.txt)进行解释。
## 协议状态
根据[SemVer](http://semver.org),1.0.0版本表示tus协议已可用于生产环境。我们预计不会做破坏性变更,如有必要将在2.0.0版本进行。新增扩展或向后兼容的功能更新将提升MINOR版本号。
## 贡献方式
本协议由tus社区维护,欢迎通过[GitHub](https://github.com/tus/tus-resumable-upload-protocol)提交修改建议。所有贡献者将列于协议头部。
请通过[此链接](https://github.com/tus/tus.io/issues/new)告知您的实现方案(开源或商业),我们将在[实现方案页面](https://www.tus.io/implementations.html)列出。
## 摘要
本协议通过HTTP/1.1([RFC 7230](https://tools.ietf.org/html/rfc7230))和HTTP/2([RFC 7540](https://tools.ietf.org/html/rfc7540))提供可恢复文件上传机制。
## 符号说明
方括号中的字符表示占位符(如`[size]`)。
术语space、comma和semicolon指其ASCII字符。
## 核心协议
核心协议描述如何恢复中断的上传。通常通过[创建](#创建)扩展生成上传URL后使用。
所有客户端和服务端**必须**(MUST)实现核心协议。
本规范不规定URL结构,由具体实现决定。文档中所有URL均为示例。
认证和授权机制由服务端实现。
### 示例
`HEAD` 请求用于确定应从哪个偏移量继续上传。
下面的示例显示了一个 100 字节上传的中断后的继续,该中断发生在传输了 70 字节之后。
**请求:**
```
HEAD /files/24e533e02ec3bc40c387f1a0e460e216 HTTP/1.1
Host: tus.example.org
Tus-Resumable: 1.0.0
```
**响应:**
```
HTTP/1.1 200 OK
Upload-Offset: 70
Tus-Resumable: 1.0.0
```
给定偏移量,客户端使用 `PATCH` 方法来恢复上传:
**请求:**
```
PATCH /files/24e533e02ec3bc40c387f1a0e460e216 HTTP/1.1
Host: tus.example.org
Content-Type: application/offset+octet-stream
Content-Length: 30
Upload-Offset: 70
Tus-Resumable: 1.0.0
[剩余30字节]
```
**响应:**
```
HTTP/1.1 204 No Content
Tus-Resumable: 1.0.0
Upload-Offset: 100
```
### 头部字段
#### Upload-Offset
`Upload-Offset` 请求和响应头部指示资源中的字节偏移量。该值**必须**(MUST)是一个非负整数。
#### Upload-Length
`Upload-Length` 请求和响应头部指示整个上传的大小(以字节为单位)。该值**必须**(MUST)是一个非负整数。
#### Tus-Version
`Tus-Version` 响应头部**必须**(MUST)是由服务器支持的协议版本组成的逗号分隔列表。该列表**必须**(MUST)按照服务器的偏好排序,其中第一个是最优选的版本。
#### Tus-Resumable
`Tus-Resumable`头部**必须**(MUST)包含在所有请求和响应中(`OPTIONS`请求除外),其值**必须**(MUST)为客户端或服务端使用的协议版本。
如果服务端不支持客户端指定的版本,**必须**(MUST)返回`412 Precondition Failed`状态码,并**必须**(MUST)在响应中包含`Tus-Version`头部。
此外,服务端**不得**(MUST NOT)处理该请求。
#### Tus-Extension
`Tus-Extension`响应头部**必须**(MUST)为服务端支持的扩展列表(逗号分隔)。若无支持的扩展,**必须**(MUST)省略该头部。
#### Tus-Max-Size
`Tus-Max-Size`响应头部**必须**(MUST)为非负整数,表示允许的最大上传字节数。若存在已知硬性限制,服务端**应该**(SHOULD)设置该头部。
#### X-HTTP-Method-Override
`X-HTTP-Method-Override`请求头部**必须**(MUST)为字符串,若存在则该值**必须**(MUST)被服务端解释为请求方法,实际请求方法**必须**(MUST)被忽略。当客户端环境不支持PATCH或DELETE方法时,**应该**(SHOULD)使用此头部。
### 请求方法
#### HEAD
服务器**必须**(MUST)始终在 `HEAD` 请求的响应中包含 `Upload-Offset` 头部,即使偏移量为 `0`,或者上传已经被认为是已完成的。如果上传的大小已知,服务器**必须**(MUST)在响应中包含 `Upload-Length` 头部。如果未找到资源,服务器**应该**(SHOULD)返回 `404 Not Found`、`410 Gone` 或 `403 Forbidden` 状态,而不包含 `Upload-Offset` 头部。
服务器**应该**(SHOULD)使用 `200 OK` 或 `204 No Content` 状态确认成功的 `HEAD` 请求。
服务器**必须**(MUST)将 `Cache-Control: no-store` 头部添加到响应中,来阻止客户端和/或代理缓存响应。
#### PATCH
服务器**应该**(SHOULD)接受针对任何上传 URL 的 `PATCH` 请求,并将消息中包含的字节应用到由 `Upload-Offset` 头部指定的给定偏移量。所有 `PATCH` 请求**必须**(MUST)使用 `Content-Type: application/offset+octet-stream`,否则服务器**应该**(SHOULD)返回 `415 Unsupported Media Type` 状态。
`Upload-Offset` 头部的值**必须**(MUST)等于资源的当前偏移量。为了实现并行上传,**可以**(MAY)使用[连接](#连接)扩展。如果偏移量不匹配,服务器**必须**(MUST)以 `409 Conflict` 状态响应,而不修改上传资源。
客户端**应该**(SHOULD)在单个 `PATCH` 请求中发送上传的所有剩余字节,但**可以**(MAY)为了某些需要的场景,连续使用多个小请求。这些情况的一个例子是当使用[Checksum](#checksum)扩展时。
服务器**必须**(MUST)使用 `204 No Content` 状态确认成功的 `PATCH` 请求。它**必须**(MUST)包含 `Upload-Offset` 头部,其中包含新的偏移量。新的偏移量**必须**(MUST)是 `PATCH` 请求之前的偏移量与当前 `PATCH` 请求期间接收、处理或存储的字节数之和。
如果服务器收到针对不存在资源的 `PATCH` 请求,它**应该**(SHOULD)返回 `404 Not Found` 状态。
客户端和服务器都**应该**(SHOULD)尝试以可预测的方式检测和处理网络错误。它们**可以**(MAY)通过检查读/写套接字错误以及设置读/写超时来实现。超时**应该**(SHOULD)通过关闭底层连接来处理。
服务器**应该**(SHOULD)始终尝试存储尽可能多的接收数据。
#### OPTIONS
一个 `OPTIONS` 请求**可以**(MAY)用于收集关于服务器当前配置的信息。一个成功的响应,由 `204 No Content` 或 `200 OK` 状态指示,**必须**(MUST)包含 `Tus-Version` 头部。它**可以**(MAY)包含 `Tus-Extension` 和 `Tus-Max-Size` 头部。
客户端**不应该**(SHOULD NOT)在请求中包含 `Tus-Resumable` 头部,服务器**必须**(MUST)忽略该头部。
##### 示例
此示例阐明了 `OPTIONS` 请求的响应。响应中使用的版本是 `1.0.0`,而服务器也能够处理 `0.2.2` 和 `0.2.1`。允许总大小高达 1GB 的上传,并且启用了[创建](#创建)和[过期](#过期)扩展。
**请求:**
```
OPTIONS /files HTTP/1.1
Host: tus.example.org
```
**响应:**
```
HTTP/1.1 204 No Content
Tus-Resumable: 1.0.0
Tus-Version: 1.0.0,0.2.2,0.2.1
Tus-Max-Size: 1073741824
Tus-Extension: creation,expiration
```
## 协议扩展
鼓励客户端和服务器尽可能多地实现扩展。客户端**应该**(SHOULD)通过发送 `OPTIONS` 请求,服务器通过 `Tus-Extension` 头部响应来实现特性检测。
### 创建
客户端和服务器**应该**(SHOULD)实现上传创建扩展。如果服务器支持此扩展,它**必须**(MUST)将 `creation` 添加到 `Tus-Extension` 头部。
#### 示例
使用空的 `POST` 请求来创建一个新的上传资源。`Upload-Length` 头部表示整个上传的大小(以字节为单位)。
**请求:**
```
POST /files HTTP/1.1
Host: tus.example.org
Content-Length: 0
Upload-Length: 100
Tus-Resumable: 1.0.0
Upload-Metadata: filename d29ybGRfZG9taW5hdGlvbl9wbGFuLnBkZg==,is_confidential
```
**响应:**
```
HTTP/1.1 201 Created
Location: https://tus.example.org/files/24e533e02ec3bc40c387f1a0e460e216
Tus-Resumable: 1.0.0
```
新的资源具有隐式的偏移量 `0`,允许客户端使用核心协议来执行实际的上传。
#### 头部
##### Upload-Defer-Length
`Upload-Defer-Length` 请求和响应头部表明当前不知道上传的大小,稍后会传输。它的值**必须**(MUST)是 `1`。如果上传的长度没有被延迟,则**必须**(MUST)省略此头部。
##### Upload-Metadata
`Upload-Metadata` 请求和响应头部**必须**(MUST)由一个或多个逗号分隔的键值对组成。键和值**必须**(MUST)用空格分隔。键**不得**(MUST NOT)包含空格和逗号,并且**不得**(MUST NOT)为空。键**应该**(SHOULD)是 ASCII 编码的,值**必须**(MUST)是 Base64 编码的。所有键**必须**(MUST)是唯一的。值**可以**(MAY)为空。在这些情况下,通常分隔键和值的空格**可以**(MAY)省略。
由于元数据可能包含任意二进制值,服务器**应该**(SHOULD)仔细验证元数据值或在使用它们作为头部值之前对其进行清理,以避免**头部注入攻击**。
#### 请求
##### POST
客户端**必须**(MUST)向已知的上传创建 URL 发送一个 `POST` 请求,以请求一个新的上传资源。该请求**必须**(MUST)包含以下头部之一:
a) `Upload-Length`,用于指示整个上传的大小(以字节为单位)。
b) `Upload-Defer-Length: 1`,如果客户端不知道要上传多大。
如果 `Upload-Defer-Length` 头部不为1,服务器应返回 `400 Bad Request` 状态。
如果使用 `Upload-Defer-Length: 1` 延迟了长度,则客户端**必须**(MUST)在下一个 `PATCH` 请求中设置 `Upload-Length` 头部,一旦知道长度。一旦设置,长度**不得**(MUST NOT)更改。只要不知道上传的长度,服务器**必须**(MUST)在对 `HEAD` 请求的所有响应中设置 `Upload-Defer-Length: 1`。
如果服务器支持延迟长度,它**必须**(MUST)将 `creation-defer-length` 添加到 `Tus-Extension` 头部。
`Upload-Length` 头部**可以**(MAY)设置为 0,表示客户端想要上传一个空文件。这样的上传在创建后立即完成,而无需使用 `PATCH` 请求传输数据。
客户端**可以**(MAY)提供 `Upload-Metadata` 头部,以向上传创建请求添加额外的元数据。服务器**可以**(MAY)决定忽略或使用此信息来进一步处理请求或拒绝它。如果上传包含额外的元数据,则对 `HEAD` 请求的响应**必须**(MUST)包含 `Upload-Metadata` 头部及其值,如客户端在创建期间指定的那样。
如果上传的长度超过了最大值,这**可以**(MAY)使用 `Tus-Max-Size` 头部指定,服务器**必须**(MUST)响应 `413 Request Entity Too Large` 状态。
服务器**必须**(MUST)使用 `201 Created` 状态确认成功的上传创建。服务器**必须**(MUST)将 `Location` 头部设置为已创建资源的 URL。此 URL**可以**(MAY)是绝对的或相对的。
客户端**必须**(MUST)使用核心协议执行实际的上传。
### 带上传的创建
客户端**可以**(MAY)使用带上传的创建扩展在初始创建请求中包含上传的部分内容。
如果服务器支持此扩展,它**必须**(MUST)通过在 `Tus-Extension` 头部中包含 `creation-with-upload` 来声明这一点。此外,此扩展直接依赖于创建扩展。因此,如果服务器不提供创建扩展,它**不得**(MUST NOT)提供带上传的创建扩展。
客户端**可以**(MAY)在 `POST` 请求的正文中包含整个或一部分上传数据。在这种情况下,与 `PATCH` 请求和响应类似的规则适用。客户端**必须**(MUST)包含 `Content-Type: application/offset+octet-stream` 头部。服务器**应该**(SHOULD)接受尽可能多的字节,并且**必须**(MUST)在响应中包含 `Upload-Offset` 头部,并且**必须**(MUST)将其值设置为应用接受的字节后的上传偏移量。
如果客户端想要使用此扩展,客户端**应该**(SHOULD)在发送 `POST` 请求之前验证服务器是否支持它。此外,客户端**应该**(SHOULD)在请求中包含 `Expect: 100-continue` 头部,以接收来自服务器的早期反馈,了解它是否会接受创建请求,然后再尝试传输第一个块。
#### 示例
使用非空的 `POST` 请求来创建一个新的上传资源。响应中的 `Upload-Offset` 头部指示已接受了多少数据。
**请求:**
```
POST /files HTTP/1.1
Host: tus.example.org
Content-Length: 5
Upload-Length: 100
Tus-Resumable: 1.0.0
Content-Type: application/offset+octet-stream
hello
```
**响应:**
```
HTTP/1.1 201 Created
Location: https://tus.example.org/files/24e533e02ec3bc40c387f1a0e460e216
Tus-Resumable: 1.0.0
Upload-Offset: 5
```
### 过期
服务器**可以**(MAY)在未完成的上传过期后将其删除。为了向客户端表明此行为,服务器**必须**(MUST)将 `expiration` 添加到 `Tus-Extension` 头部。
#### 示例
未完成的上传在 `Upload-Expires` 中指定的时间之前可用。在此日期之后,无法恢复上传。
**请求:**
```
PATCH /files/24e533e02ec3bc40c387f1a0e460e216 HTTP/1.1
Host: tus.example.org
Content-Type: application/offset+octet-stream
Content-Length: 30
Upload-Offset: 70
Tus-Resumable: 1.0.0
[remaining 30 bytes]
```
**响应:**
```
HTTP/1.1 204 No Content
Upload-Expires: Wed, 25 Jun 2014 16:00:00 GMT
Tus-Resumable: 1.0.0
Upload-Offset: 100
```
#### 头部
##### Upload-Expires
`Upload-Expires` 响应头部指示未完成的上传过期后的时间。服务器**可以**(MAY)希望在给定的时间段后删除未完成的上传,以防止废弃的上传占用额外的存储空间。客户端**应该**(SHOULD)使用此头部来确定上传在尝试恢复上传之前是否仍然有效。
如果上传将要过期,则**必须**(MUST)在每个 `PATCH` 响应中包含此头部。如果在创建时已知过期时间,则**必须**(MUST)在对初始 `POST` 请求的响应中包含 `Upload-Expires` 头部。它的值**可以**(MAY)随时间变化。
如果客户端确实尝试恢复已被服务器删除的上传,则服务器**应该**(SHOULD)响应 `404 Not Found` 或 `410 Gone` 状态。如果服务器正在跟踪过期的上传,则**应该**(SHOULD)使用后者。在这两种情况下,客户端**应该**(SHOULD)开始新的上传。
`Upload-Expires` 头部的值**必须**(MUST)采用[RFC 7231](https://tools.ietf.org/html/rfc7231#section-7.1.1.1)日期时间格式。
### 校验和
客户端和服务器**可以**(MAY)实现和使用此扩展来验证每个 `PATCH` 请求的数据完整性。如果支持,服务器**必须**(MUST)将 `checksum` 添加到 `Tus-Extension` 头部。
客户端**可以**(MAY)在 `PATCH` 请求中包含 `Upload-Checksum` 头部。一旦收到整个请求,服务器**必须**(MUST)使用指定的算法针对提供的校验和验证上传的块。根据结果,服务器**可以**(MAY)响应以下状态代码之一:
1) `400 Bad Request`,如果服务器不支持校验和算法,
2) `460 Checksum Mismatch`,如果校验和不匹配,或者
3) `204 No Content`,如果校验和匹配并且数据的处理成功。
在前两种情况下,**必须**(MUST)丢弃上传的块,并且**不得**(MUST NOT)更新上传及其偏移量。
服务器**必须**(MUST)至少支持由 `sha1` 标识的 SHA1 校验和算法。校验和算法的名称**必须**(MUST)仅由 ASCII 字符组成,但排除大写字符的修改除外。
`Tus-Checksum-Algorithm` 头部**必须**(MUST)包含在对 `OPTIONS` 请求的响应中。
如果在上传开始时无法计算哈希值,**可以**(MAY)将其作为尾部包含。如果服务器可以处理尾部,则**必须**(MUST)通过将 `checksum-trailer` 添加到 `Tus-Extension` 头部来声明此行为。尾部,也称为尾随头部,是在请求的正文已经传输之后发送的头部。按照[RFC 7230](https://tools.ietf.org/html/rfc7230#section-4.1.2)的规定,它们**必须**(MUST)使用 `Trailer` 头部声明,并且只允许在分块传输中使用。
#### 头部
##### Tus-Checksum-Algorithm
`Tus-Checksum-Algorithm` 响应头部**必须**(MUST)是服务器支持的校验和算法的逗号分隔列表。
##### Upload-Checksum
`Upload-Checksum` 请求头部包含有关当前正文有效负载的校验和的信息。头部**必须**(MUST)由使用的校验和算法的名称和 Base64 编码的校验和组成,两者之间用空格分隔。
#### 示例
**请求**:
```
OPTIONS /files HTTP/1.1
Host: tus.example.org
```
**响应**:
```
HTTP/1.1 204 No Content
Tus-Resumable: 1.0.0
Tus-Version: 1.0.0
Tus-Extension: checksum
Tus-Checksum-Algorithm: md5,sha1,crc32
```
**请求**:
```
PATCH /files/17f44dbe1c4bace0e18ab850cf2b3a83 HTTP/1.1
Content-Length: 11
Upload-Offset: 0
Tus-Resumable: 1.0.0
Upload-Checksum: sha1 Kq5sNclPz7QV2+lfQIuc6R7oRu0=
hello world
```
**响应**:
```
HTTP/1.1 204 No Content
Tus-Resumable: 1.0.0
Upload-Offset: 11
```
### 终止
此扩展定义了一种客户端终止已完成和未完成的上传的方式,允许服务器释放已使用的资源。
如果服务器支持此扩展,则**必须**(MUST)通过将 `termination` 添加到 `Tus-Extension` 头部来声明它。
#### 请求
##### DELETE
当收到对现有上传的 `DELETE` 请求时,服务器**应该**(SHOULD)释放关联的资源,并且**必须**(MUST)响应 `204 No Content` 状态,确认上传已终止。对于将来对此 URL 的所有请求,服务器**应该**(SHOULD)响应 `404 Not Found` 或 `410 Gone` 状态。
#### 示例
**请求:**
```
DELETE /files/24e533e02ec3bc40c387f1a0e460e216 HTTP/1.1
Host: tus.example.org
Content-Length: 0
Tus-Resumable: 1.0.0
```
**响应:**
```
HTTP/1.1 204 No Content
Tus-Resumable: 1.0.0
```
### 连接
此扩展可用于将多个上传连接成一个,使客户端能够执行并行上传和上传非连续的块。如果服务器支持此扩展,则**必须**(MUST)将 `concatenation` 添加到 `Tus-Extension` 头部来声明它。
部分上传表示文件的一个块。它通过在创建新上传时包含 `Upload-Concat: partial` 头部来构造,使用[创建](#创建)扩展。多个部分上传按指定的顺序连接成一个最终上传。服务器**不应该**(SHOULD NOT)处理这些部分上传,直到它们被连接成一个最终上传。最终上传的长度**必须%(MUST)是所有部分上传的长度之和。
为了创建一个新的最终上传,客户端 *必须**(MUST)将 `Upload-Concat` 头部添加到上传创建请求。该值**必须**是 `final`,后跟一个分号和一个空格分隔的需要连接的部分上传 URL 列表。部分上传**必须** 按照列表中指定的顺序进行连接。此连接请求**应该**(SHOULD)在所有相应的部分上传完成后发生。客户端**不得**(MUST NOT)最终上传创建中包含 `Upload-Length` 头部。
客户端**可以**(MAY)在部分上传仍在进行中时发送连接请求。服务器**必须**(MUST)通过将 `concatenation-unfinished` 添加到 `Tus-Extension` 头部来显式声明此功能。
当创建一个新的最终上传时,部分上传的元数据**不应**(SHALL NOT)传输到新的最终上传。所有元数据**应该**(SHOULD)使用 `Upload-Metadata` 头部包含在连接请求中。
服务器**可以**(MAY)在连接后删除部分上传。但是,客户端**可以**(MAY)尝试多次使用部分上传。同一个部分上传**可以**(MAY)在一次上传创建请求中的 `Upload-Concat` 头部中多次出现,或者**可以**(MAY)在多个上传创建请求中使用。
服务器**必须**(MUST)响应 `403 Forbidden` 状态,以针对最终上传URL的`PATCH`请求,并且**不得**(MUST NOT)修改最终上传或其部分上传。
对最终上传的HEAD请求的响应**不应该**(SHOULD NOT)包含`Upload-Offset`头部,除非已成功完成连接。成功连接后**必须**(MUST)设置`Upload-Offset` 和 `Upload-Length`,并且它们的值**必须**(MUST)相等。在连接之前,未定义最终上传的 `Upload-Offset` 头部的值。
对部分上传的HEAD请求的响应**必须**(MUST)包含 `Upload-Offset` 头部。
如果可以在请求时计算最终资源的长度,则**必须**(MUST)包含 `Upload-Length` 头部。对部分或最终上传的HEAD请求的响应**必须**(MUST)包含`Upload-Concat` 头部及其在上传创建请求中收到的值。
#### 头部
##### Upload-Concat
`Upload-Concat` 请求和响应头部**必须**(MUST)在部分和最终上传创建请求中设置。它指示上传是部分上传还是最终上传。如果上传是部分上传,则头部值**必须**(MUST)是 `partial`。在最终上传的情况下,其值**必须** 是 `final`,后跟一个分号和一个空格分隔的将要连接的部分上传 URL 列表。部分上传 URL**可以**(MAY)是绝对的或相对的,并且**不得**(MUST NOT)包含空格,如[RFC 3986](https://tools.ietf.org/html/rfc3986)中定义的那样。
#### 示例
在以下示例中,为了便于阅读,省略了 `Host` 和 `Tus-Resumable` 头部,尽管规范要求它们。
首先创建两个部分上传:
```
POST /files HTTP/1.1
Upload-Concat: partial
Upload-Length: 5
HTTP/1.1 201 Created
Location: https://tus.example.org/files/a
```
```
POST /files HTTP/1.1
Upload-Concat: partial
Upload-Length: 6
HTTP/1.1 201 Created
Location: https://tus.example.org/files/b
```
现在可以使用 `PATCH` 请求将数据上传到两个部分资源:
```
PATCH /files/a HTTP/1.1
Upload-Offset: 0
Content-Length: 5
hello
HTTP/1.1 204 No Content
```
```
PATCH /files/b HTTP/1.1
Upload-Offset: 0
Content-Length: 6
world
HTTP/1.1 204 No Content
```
在第一个请求中,上传了字符串 `hello`,而第二个文件现在包含 ` world`,带有一个前导空格。
下一步是创建由两个先前生成的部分上传组成的最终上传。在以下请求中,没有提供 `Upload-Length` 头部。
```
POST /files HTTP/1.1
Upload-Concat: final;/files/a /files/b
HTTP/1.1 201 Created
Location: https://tus.example.org/files/ab
```
最终资源的长度现在为 11 字节,由字符串 `hello world` 组成。
```
HEAD /files/ab HTTP/1.1
HTTP/1.1 200 OK
Upload-Length: 11
Upload-Concat: final;/files/a /files/b
```
## FAQ
常见问题解答可在 上在线获取。
## 许可协议
根据 MIT 许可证授权,请参见[LICENSE.txt](https://github.com/tus/tus-resumable-upload-protocol/blob/main/LICENSE.txt)。
版权所有 (c)2013-2016 Transloadit Ltd 及贡献者。