文件上传服务.ym 9 KB

文件上传服务基于 tus.io 协议构建。

1   API 说明与约定

  • 本文档所列出的 API 响应格式均为 JSON
  • 各个 API 都有说明使用哪种 HTTP 请求
  • 文档只列出的 API 路径,实际调用请在前面加上 Upload API 的服务器地址作为基准

2   在wo获取上传凭证

在使用 tus.io 协议上传文件之前,需要先通过调用站点内容管理的 /api/v1/content/get_upload_signcode 接口,从文档系统拿到上传凭证。 上传凭证中的以下字段不需要传递给上传服务器:

  • upload_server: 仅用于向调用方指示上传服务的地址
  • upload_service: 仅用于向调用方指示通过哪种服务进行上传

3   在uplaod服务创建上传会话 /api/v1/upload/upload_resumable

向此地址发送 HTTP POST 请求可以创建一个上传会话。

参数分为两部分:

  • HTTP 头:
    • Tus-Resumable: 指示 tus.io 协议的版本,当前为 1.0.0
    • Upload-Length: 要上传的文件大小,以字节计
    • Upload-Metadata: 文件的其他元数据,每个 key-value 都使用空格连接 key  value的 base64编码值 ,多个 key-value 对使用 , 连接。目前使用的元数据有:
      • callback_url: 上传凭证中的 callback_url 值,用于文件上传完毕后回调文档系统接口
  • POST 表单字段值全部从上传凭证信息中取得:
    • deadline: 会话截止的时间戳
    • expire: 上传会话的过期时间。必须在会话过期前开始上传
    • maxsize: 此会话上传的文件最大大小
    • signcode: 会话签名
    • device: 文件实际存储的设备名称
    • prefix: 文件前缀
    • suffix: 文件后缀

会话成功创建后会返回一个 HTTP 201 响应,响应头中的 Location 字段值就是会话地址。

4   查询文件上传进度

在会话创建之后、文件上传完毕之前,可以向会话地址发起 HTTP HEAD 请求来查询文件的上传进度。 一旦文件上传完毕,或者在会话过期前没有开始上传文件内容,会话自动失效,查询将会返回 HTTP 404 错误。

参数:

  • HTTP 头:
    • Tus-Resumable: 指示协议版本,当前为 1.0.0

在响应头中的 Upload-Offset 字段值即为文件的上传进度,以字节计。

5   上传文件内容

创建会话后,向会话地址发起 HTTP PATCH 请求可以上传文件内容。

参数:

  • HTTP 头:
    • Tus-Resumable: 指示协议版本,当前为 1.0.0
    • Content-Type: 必须为 application/offset+octet-stream
    • Upload-Offset: 从何处开始上传
    • Upload-Checksum: 可选,所上传内容的校验值,格式为以空格分隔 校验算法的全小写名称 和校验值。目前上传服务只支持 SHA1 校验。
  • HTTP body:
    • 文件从 Upload-Offset 处开始,本次请求需要上传的内容

响应:

  • 若本次请求后文件尚未上传完成,返回一个 HTTP 204 响应,响应头中 Upload-Offset 字段值为本次上传到的字节数
  • 若本次请求后文件上传已经完成,响应 body 中是文件信息的 JSON 数据,格式与文档系统的 /api/v1/content/properties 接口一致,同时 headers 中不再带有 Upload-Offset 字段

6   使用 edo_client 来进行断点上传

edo_client 对于使用 Python 语言来上传文件的过程做了简化,并支持回调函数,可以用于显示进度等。

上传之前,需要先从工作站点拿到签名许可:

upload_sign = wo_client.content.get_upload_signcode(
    expire=signcode_expiration,
    maxsize=2**20,
    uid=folder_uid,
    filename = filename,
)

上传本地文件:

def progress_callback(offset, file_size, filepath=None):
    print '{:.1f}%'.format(100.0 * offset / file_size)

metadata = upload_client.upload.upload(
    local_file_path, upload_sign,
    store=None,
    on_progress=progress_callback,
    chunk_size=2**20
)

其中 on_progress 是一个接受 3 个参数的回调函数,文件每上传完一块(块大小由 chunk_size 参数指定)都会被调用一次。调用时参数为:

  • offset: 文件已经上传的字节数
  • fsize: 文件的总大小
  • filepath: 文件的完整路径

如果文件在上传过程中被修改(mtime 发生变化),上传会中断,并抛出 AbortUpload(419, 419, '上传期间文件被修改') 错误。 文件的上传会话信息保存在 store 参数指定的目录中,默认是 ~/.edo/upload_progress

如果需要,也可以直接按以下方式调用底层接口来完成上传:

先创建一个会话:

session_url = upload_client.upload.create_session(
    size=filesize,
    parent_rev=None,
    **upload_sign
)

保存这个session_url,之后可以断点续传。先看看文件先前上传了多少:

offset = upload_client.upload.get_offset(session_url)

然后连续分片上传:

while offset is not None:
    resp = upload_client.upload.put_chunk(
        session_url, f, offset, chunk_size=2*2**20
    )
    offset = resp.get('offset')
    rate = offset * 100.0 / filesize if offset else 100
    print '[%-100s] %%%.2f\r' % ('='*int(rate), rate),