tus
tus是一个可续穿文件上传协议,它以Http协议为载体,统一了一个文件断点续传的标准。
这篇文章翻译自https://tus.io/
目前该协议版本信息如下:
Version: 1.0.0 (SemVer)
Date: 2016-03-25
Authors: Felix Geisendörfer, Kevin van Zonneveld, Tim Koschützki, Naren Venkataraman, Marius Kleidl
Collaborators: Bruno de Carvalho, James Butler, Øystein Steimler, Sam Rijs, Khang Toh, Jacques Boscq, Jérémy FRERE, Pieter Hintjens, Stephan Seidt, Aran Wilkinson, Svein Ove Aas, Oliver Anan, Tim, j4james, Julian Reschke, Evert Pot, Jochen Kupperschmidt, Andrew Fenn, Kevin Swiber, Jan Kohlhof, eno, Luke Arduini, Jim Schmid, Jeffrey ‘jf’ Lim, Daniel Lopretto, Mark Murphy, Peter Darrow, Gargaj, Tomasz Rydzyński, Tino de Bruijn, Jonas mg, Christian Ulbrich, Jon Gjengset, Michael Elovskikh, Rick Olson, J. Ryan Stinnett, Ifedapo Olarewaju Robert Nagy
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.
第一部分:状态
在SemVer(https://semver.org/)之后,从1.0.0开始,tus就可以被广泛采用了。我们不期望做出破坏性的更改,但是如果我们这样做了,这些更改就必须在2.0.0中。引入一个新的扩展或任何向后兼容的更改,添加新的功能,都会导致一个意外的次要版本。
NOTES:SemVer就是Semantic Versioning 2.0.0是一个语义版本控制,Tus协议采用了这个协议,我也不知道这个协议到底他妈的是干啥的。可能是描述注入MUST、SHOULD、MAY等等这些词的语气的吧。比如MUST这个词你可能会在脑海中闪现出一副老板训人的那种语气,要求你必须完成某项工作的表情之类的吧。困惑中。
第二部分:贡献
这个协议由Tus社区创建,并归属于Tus社区,欢迎通过Github来提交协议的补丁和反馈意见,所有的作者和贡献者的名字都会在协议的开头列出。
如果你有开源的或者商业的任何关于该协议的实现想要列在我们的官方网站上(implementations),请一定让我们知道(let us know)。
第三部分:摘要
协议通过 HTTP/1.1 (RFC 7230) 和 HTTP/2 (RFC 7540)提供了支持文件断点恢复上传的机制和原理。
第四部分:提示
被方括号包起来的字符表示一个占位符(例如[size])。
术语空格、逗号和分号指的是它们的ASCII表示形式。
第五部分:核心协议
核心协议描述了如何恢复一个中断了的上传操作。它假设你已经有了一个上传的URL,这个URl通常是由Creation扩展来创建的。(Creation扩展会在下面介绍)
客户端协议和服务端协议必须(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 Method恢复上传:
请求:
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 Tus-Resumable: 1.0.0 Upload-Offset: 100
头部
Upload-Offset
通用头,可以用在请求和响应中,它必须(MUST)是一个非负的整数。它指示上传资源中的字节偏移量。
Upload-Length
通用头,可以用在请求或响应中,它必须(MUST)是一个非负整数,它指示所要上传的资源的大小。
Tus-Version
响应头,它必须是一个服务端支持的以逗号分隔的协议版本列表。这个列表必须(MUST)按照服务端推荐程序排序,其中,列表中所列出的第一项是最为推荐的协议版本。
Tus-Resumable
通用头,这个头是除了OPTION Method请求之外必须包含在每一个请求和响应中的头。它的值必须(MUST)是客户端和服务端都使用的一个版本号。
如果客户端中指定的版本号不被服务端支持,服务端必须(MUST)返回412 Precondition Failed
状态码并且必须(MUST)包含Tus-Resumable头。此外,服务端必须不能(MUST NOT)处理请求。
Tus-Extension
响应头,这个头必须(MUST)是一个服务端支持的逗号分隔的可扩展功能的列表,如果服务端没有任何扩展功能支持,那么这个头必须(MUST)省略。
Tus-Max-Size
响应头,它必须是一个非负整数,以字节为单位指示整个上载的最大允许大小。如果存在已知的硬性限制,服务器应该(SHOULD)设置此头。
X-HTTP-Method-Override
请求头,如果该请求头出现了,它必须(MUST)是一个字符串,服务器必须(MUST)将其解释为请求的方法。而真正的请求方法必须被忽略。当客户端环境不允许使用PATCH 或 DELETE 等方法时,客户端应该(SHOULD)使用这个头。
请求
HEAD请求
对于HEAD请求,服务端相应必须(MUST)包含Upload-Offset
头,甚至当Upload-Offset
是0或者上传过程已结束的情况下也应该包含。在上传大小已知的情况下,服务端响应必须(MUST)包含Upload-Length
头。如果资源未发现(意味着Upload-Offset无效
),服务端响应应该(SHOULD)返回404 Not Found
, 410 Gone
或 403 Forbidden状态码中的一个。
服务端必须(MUST)通过添加Cache-Control: no-store
头来防止客户端和/或代理缓存响应。
PATCH请求
服务端应该(SHOULD)支持任何使用上传URL的path method请求并应用请求消息中Upload-Offset
头指定的偏移量。所有的PATCH请求必须包含Content-Type: application/offset+octet-stream,否则服务端会返回
415 Unsupported Media Type
状态码。
Upload-Offset
头的值必须和当前上传资源的偏移量相等,为了实现并行上传,可以使用连接扩展(Concatenation)。如果偏移量不匹配,服务器必须以409 Conflict
状态响应,而不修改上载资源。
客户机应该在单个补丁请求中发送上载的所有剩余字节,但也可以在需要的情况下连续使用多个小请求。这些情况的一个例子是使用Checksum扩展。
服务器必须以204 No Content状态确认成功的PATCH请求。它必须包含Upload-Offset头用于
表示的新的偏移量。新偏移量必须是PATCH请求之前的偏移量和当前PATCH请求期间接收和处理或存储的字节数之和。
如果服务器接收到针对不存在资源的PATCH请求,它应该返回404 Not Found状态。
客户机和服务器都应该尝试检测和处理可预测的网络错误。他们可以通过检查读/写socket(套接字)错误,以及设置读/写超时来做到这一点。应该(SHOULD)通过关闭底层连接来处理超时。
服务器应该总是尝试存储尽可能多的接收数据。
OPTIONS请求
OPTIONS请求可以被用来收集服务端当前的配置信息。204 No Content或200 OK状态所指示的成功响应必须包含Tus-version头部。它可能包括Tus-Extension和Tus-Max-Size头文件。
客户机不应该在请求中包含Tus-Resumable头,服务器必须忽略该头。
示例
这个例子阐明了OPTIONS请求的响应。请求和响应中使用的版本都是1.0.0,而服务器也能够处理0.2.2和0.2.1。服务器允许上传的总大小不超过1GB,并且支持创建(Creation)和过期(Expiration)扩展。
请求:
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
第六部分:协议的扩展
刚才谈到了一些协议的扩展比如说Creation和Expiration扩展协议,现在来真正介绍它吧。
鼓励客户端和服务器实现尽可能多的扩展。特征检测应该通过客户端发送一个OPTIONS请求和服务器使用Tus-Extension头响应来实现。
Creation,创建扩展
客户端和服务端应该(SHOULD)来实现这个上传创建扩展(upload creation extension).如果服务端支持这个功能扩展,应该在Tus-Extensions头里面加入creation。
举例
一个空的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==
响应:
HTTP/1.1 201 Created Location: https://tus.example.org/files/24e533e02ec3bc40c387f1a0e460e216 Tus-Resumable: 1.0.0
这个新的资源隐式的含有一个0的偏移量来允许客户端使用核心协议完成实际的上传过程。
头部
Upload-Defer-Length
通用头,请求和响应都会用到这个头。它表示上传的大小目前未知,稍后将被传输。它的值必须是1。如果上传的长度没有延迟,则必须省略此标题。(Defer本身就是有延迟之意)。
Upload-Metadata
通用头,请求和响应都会用到这个头。这个头必须是由一些逗号分隔的键值对组成的。键和值之间必须(MUST)由一个空格隔开。键不能(MUST NOT)有空格和逗号,并且不能为空。键应该(SHOULD)是ASCII编码的,同时值必须(MUST)是Base64编码过的。所有的键必须是唯一的。
请求
POST请求
客户端必须根据已知的上传创建URL发送POST请求,以请求新的上传资源。该请求必须包含以下头之一:
Upload-Length
用于指示上传的资源的大小,以字节为单位。
Upload-Defer-Length: 1
,如果上传资源的大小在当时未知,那么这个头就排上用场了。但是一旦知道了长度那么客户端必须在下一个PATCH请求中设置Upload-Length头。一旦设置了长度,就不能更改。只要上传的长度未知,服务器必须在对HEAD请求的所有响应中设置upload-deferred-length: 1。如果Upload-Defer-Length
头包含1以外的任何值,服务器应该返回400 bad请求状态。
如果服务器支持延迟长度,那么必须在Tus-Extensions头中设置creation-defer-length。
客户端可以提供upload-metadata头来向upload创建请求添加额外的元数据。服务器可能决定忽略或使用此信息来进一步处理请求或拒绝请求(比如使用这个头的信息来进行授权、认证等等)。如果上传包含其他元数据,则对HEAD请求的响应必须包括upload-metadata头及其值,该值在创建过程中由客户机指定。
如果上传的长度超过最大值(可以使用Tus-Max-Size头指定),服务器必须以413 request entity Too Large status响应。
服务器必须以201创建状态确认成功的上传创建操作。服务器必须将Location头设置为所创建资源的URL。这个URL可以是绝对的,也可以是相对的。
客户端必须使用核心协议来实施上传的过程。
Expiration,过期扩展
服务器可以在未完成的上传当过期后删除它们。为了向客户端表明这种行为,服务器必须向Tus-Extension头添加expiration。
例子
未完成的上传直到Upload-Expires
中指定的时间为止。在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响应头指示未完成的上载的过期时间。服务器可能希望在给定的时间之后删除不完整的上传,以防止被放弃的上传占用额外的存储空间。在尝试恢复上传之前,客户端应该使用这个头来确定上传是否仍然有效。
如果上传将要过期,这个头必须包含在每个PATCH响应中。如果创建时知道过期时间,则必须在对初始POST请求的响应中包含Upload-Expires头部。它的价值可能会随着时间而改变。
如果客户端确实试图恢复已被服务器删除的上传,服务器应以404 Not Found或410 Gone状态响应。如果服务器正在跟踪过期上传,则应该使用后者。在这两种情况下,客户机都应该启动一个新的上载。
Upload-Expires
的值必须(MUST)符合RFC 7231中对DateTime格式的规范。
Checksum,校验和扩展
客户端和服务器可以实现并使用此扩展来验证每个PATCH请求的数据完整性。如果受支持,服务器必须将checksum
添加到Tus-extension头部。
客户端可以在PATCH请求中包含Upload-Checksum头。一旦接收到整个请求,服务器必须使用指定的算法根据提供的校验和验证上传的块。根据结果服务器可能应对的一个状态码:1)400 bad request,如果校验和(checksum)算法不支持服务器;2)460 460 Checksum Mismatch
,校验和(checksum)不匹配;3)204 No Content,
校验和匹配并且数据的处理成功。在前两种情况下,上传的块必须被丢弃,上传和它的偏移量不能被更新。
服务器必须至少支持SHA1标识的SHA1校验和算法。校验和算法的名称必须只由ASCII字符组成,修改后不包含大写字符。
Tus-Checksum-Algorithm
头必须包含到OPTIONS请求的响应中。
如果不能在上传开始时计算散列,则可以将其作为trailer头包含。如果服务器能够处理trailer头部,则必须通过在Tus-Extension头中添加checksum-trailer来宣布此行为。trailer,也称为trailer头,是在请求主体已经传输之后发送的标头。在RFC 7230之后,它们必须使用Trailer
报头来发布,并且只允许分块传输(中使用)。
关于Trailer:trailer是一个响应首部,允许发送方在分块发送的消息后面添加额外的元信息,这些元信息可能是随着消息主体的发送动态生成的,比如消息的完整性校验,消息的数字签名,或者消息经过处理之后的最终状态等。
头部
Tus-Checksum-Algorithm
响应头,Tus-Checksum-Algorithm响应头必须是服务器支持的校验和算法的以逗号分隔的列表。
Upload-Checksum
请求头,Upload-Checksum请求头包含关于当前主体有效负载的校验和(checksum)的信息。头必须由使用的校验和算法的名称和由空格分隔的Base64编码的校验和(checksum)组成。
例子
请求:
OPTIONS /files HTTP/1.1 Host: tus.example.org Tus-Resumable: 1.0.0
响应:
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
Termination,结束扩展
这个扩展定义了客户端终止已完成和未完成上传的方法,允许服务器释放使用的资源。
如果服务端支持该扩展,必须在 Tus-Extension
头中写入termination
请求
DELETE请求
当接收到对现有上载的删除请求时,服务器应该释放相关资源,并且必须响应204 No Content status,确认上载已终止。对于以后对这个URL的所有请求,服务器应该以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
Concatenation,串联/连接扩展
这部分内容基本是机翻,有时间再来校验吧。
建议先看下面的例子然后再回来读这段文字,否则会很晦涩。
此扩展可用于将多个上传连接到单个上传,使客户机能够执行并行上传和上传非连续块。如果服务器支持此扩展,则必须将Tus-Extension
头中加入concatenation
。
部分上传(partial upload)表示文件的一块。它是通过在使用创建扩展(Creation extension)创建新上载时包含upload-concat:partial头来构造的。多个部分上传按指定的顺序连接到最终上传。服务器不应该处理这些部分上传,直到它们连接起来形成最终上传。最终上传的长度必须是所有部分上传的长度之和。
为了创建一个新的最终上传,客户端必须将upload-concat头添加到上传创建请求中。该值必须是final,后跟分号和需要连接的部分上传url的空格分隔列表。部分上传必须按照列表中指定的顺序连接起来。这个连接请求应该在所有相应的部分上传完成之后发生。客户端不能在最终的上传创建中包含上传长度头。
当部分上传仍在进行时,客户端可以发送连接请求。服务器必须通过将concatenation-unfinished
添加到Tus-Extension头显式地声明该特性。
当创建一个新的最终上传时,部分上传的元数据不应被传输到新的最终上传。所有元数据都应该包含在使用Upload-Metadata头的连接请求中。
服务器可以在连接后(指Concatenation)删除部分上传。但是,可以多次使用它们来形成最终的资源。
服务器必须以403 forbidden响应针对最终上传URL的PATCH请求,并且不能修改最终上传或部分上传。
对最终上传的头部请求的响应不应该包含upload-offset头,除非连接(concatenation)已经成功完成。连接(concatenation)成功后,必须设置Upload-Offset和Upload-Length,它们的值必须相等。对于最终上传,没有定义连接之前的upload-offset头的值。
对部分上载的HEAD请求的响应必须包含upload-offset头。
如果可以在请求时计算最终资源的长度,则必须包含Upload-Length头。对部分上传或最终上传的HEAD请求的响应必须包括upload-concat头及其在上传创建请求中接收到的值。
头部
Upload-Concat
通用头,upload-concat请求和响应头必须在部分(partial)和最终上传(final)创建请求中设置。它指示上载是部分(partial)上载还是最终(final)上载。如果上传是部分的,那么头的值必须是partial。在最终上传的情况下,它的值必须是final,后跟分号和一个空格分隔的部分上传url列表,这些url将被连接(concatenated)起来。部分上传url可以是绝对的或相对的,并且不能包含空格,就像RFC 3986.中定义的那样。
例子
在下面的示例中,为了可读性,省略了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,问答
这部分先不译了,有时间再进行。
Why is the protocol using custom headers?
We have carefully investigated the use of existing headers such as Range
and Content-Range
, but unfortunately they are defined in a way that makes them unsuitable for resumable file uploads.
We also considered using existing PATCH
payload formats such as multipart/byteranges, but unfortunately the XHR2 FormData interface does not support custom headers for multipart parts, and the send() method does not allow streaming arbitrary data without loading all of it into memory.
That being said, custom headers also allowed us to greatly simplify the Client and Server implementations, so we’re quite happy with them.
Why are you not using the “X-“ prefix for your headers?
The “X-“ prefix for headers has been deprecated, see RFC 6648.
How can I deal with bad HTTP proxies?
If you are dealing with HTTP proxies that strip/modify HTTP headers or can’t handle PATCH
requests properly, you should consider using HTTPS which will make it impossible for proxies to modify your requests and use the X-HTTP-Method-Override
header which allows you to use POST
requests.
If that is not an option for you, please reach out to us, we are open to defining a compatibility protocol extension.
How are pause/resume handled? When should I delete partial uploads?
The tus protocol is built upon the principles of simple pausing and resuming. In order to pause an upload you are allowed to end the current open request. The Server will store the uploaded data as long as no violations against other constraints (e.g. checksums) or internal errors occur. Once you are ready to resume an upload, send a HEAD
request to the corresponding upload URL in order to obtain the available offset. After receiving a valid response you can upload more data using PATCH
requests. You should keep in mind that the Server may delete an unfinished upload if it is not continued for a longer time period (see Expiration extension).
Before deleting an outstanding upload the Server should give the Client enough time to resolve potential networking issues. Since this duration depends heavily on the underlying application model, the protocol does not contain a specific number, but we recommend one week for a general use case.
How can I get the file name or file type for an upload?
For itself, the tus protocol does not have a direct mechanism to obtain the type or filename of an upload as the specification does not have the principle of a disk-based file, allowing you to upload arbitrary data using tus. However, the wanted functionality can be achieved by utilizing metadata. A Client can attach the file’s name and type to an upload when it’s being created by setting the Upload-Metadata
header. On the other side, the Server can read these values and determine the name and type of the upload.