介绍

腾迅云提供 COS 服务,专门用于存储静态文件。

之前有试过使用 COSCMD 工具 部署文件到对象存储 CDN - 狂飙,但是工具最大的问题在于每次调用都会检查要上传目录的所有文件,每个文件都会发送一个 HEAD 请求来查看是否需要更新,校验时长会随着文件数量线性增加。

环境

  • Windows 10 21H2
  • Python 3.10
  • PyCharm 2021.3

调试

上传文件后需要使用官方的 GUI 客户端 COSBrowser 构验文件属性,确认文件是否正确上传。

本地缓存

本地上传时一般都会指定将整个资源根目录上传,里面大部分文件可能都是上传过的。为了减少不必要的检查,需要实现一层本地缓存,可以在缓存中保存文件的相对路径、长度与最后修改时间,每次上传前检查。

远程校验

  1. 远程上传文件前先使用 HEAD 接口获取文件信息
  2. 文件存在则比对本地文件 MD5 与远程文件 MD5
    1. 相同则认为文件就是要上传的版本,跳过上传
    2. 不同则需要先删除文件再上传
  3. 文件不存在则上传
  4. 上传时要设置校验值,推荐使用比较通用的 MD5

数据在客户端和服务器间传输时可能会出现错误,COS 可以通过 MD5 校验的方式保证上传数据的完整性,只有当 COS 服务器接收到的数据 MD5 校验值与用户设置的 MD5 校验值一致时,数据才可上传成功。

COS 里每个对象对应一个 ETag,ETag 是对象被创建时对象内容的信息标识,但 ETag 不一定等同于对象内容的 MD5 校验值,因此不能通过 ETag 来校验下载对象与原对象是否一致,但用户可使用自定义对象元数据(x-cos-meta-*)来实现下载对象与原对象的一致性校验。

腾迅云官方文档介绍了如何实现 MD5 校验,因此需要本地先算出 MD5,存储到自定义的 Key 中供后续使用。

SDK

腾迅云官方提供了各种语言的 SDK,这里使用 Python SDK 实现上传:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def cos_upload_file(thread_pool, cos_client, bucket, local_path, remote_path):
    local_md5, local_md5_base64 = calculate_md5(local_path)
    exists = False
    try:
        exist_response = cos_client.head_object(Bucket=bucket, Key=remote_path)
        remote_md5 = exist_response[metadata_md5_key]
        if local_md5 == remote_md5:
            exists = True
        else:
            delete_response = cos_client.delete_object(Bucket=bucket, Key=remote_path)
            if 'x-cos-request-id' not in delete_response:
                print(f'{remote_path} delete fail and md5 {remote_md5} different local md5 {local_md5}')
                raise Exception(f'{remote_path} delete fail and md5 {remote_md5} different local md5 {local_md5}')
    except CosServiceError as e:
        if e.get_status_code() == 404:
            exists = False
        else:
            print("Error happened, upload it again.")

    if not exists:
        print(f'Upload {local_path}')
        metadata = {
            metadata_md5_key: local_md5,
        }
        thread_pool.add_task(cos_client.upload_file, bucket, remote_path, local_path,
                             ContentMD5=local_md5_base64, Metadata=metadata)

集成

需要手工构建出来不同的情况来校验整个上传流程是否正确。强烈建议将所有情况与分支都测试到,因为后续在生产环境中使用出问题调试就比较麻烦。