# 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 及贡献者。