實現Dart版本物件儲存(COS)外掛

一起漫部技術團隊發表於2022-11-24

前言

Flutter Web 在《一起漫部》的效能最佳化探索與實踐 一文中,
在做載入最佳化時需要實現資原始檔cdn化,意味著要將資原始檔上傳到騰訊的COS或者阿里的OSS這樣的第三方物件儲存伺服器。 目前公司使用的是騰訊的物件儲存(COS),原本想用官方提供的SDK去實現資源上傳功能,但是官方並沒有提供Dart版本的SDK, 去pub.dev搜了下關於cos的相關外掛,也沒有找到功能相對完善的外掛,於是便打算手寫一個Dart版本物件儲存(COS)外掛。

簡介

在官方提供的API功能過於豐富和時間有限的情況下,只實現了部分功能:

  • 支援Bucket介面的基本操作,增加、刪除、查詢儲存桶等
  • 支援Bucket介面的訪問控制(acl)
  • 支援Bucket介面的跨域資源共享(cors)
  • 支援Bucket介面的防盜鏈(referer)
  • 支援Object介面的基本操作,上傳、刪除、查詢儲存物件等
  • 支援Object介面的訪問控制(acl)

結構

工程主要包括示例(example)、核心程式碼(lib)和單元測試(test)三部分

├── CHANGELOG.md
├── LICENSE
├── README.md
├── analysis_options.yaml
├── example                                         // 示例
├── lib                                             // 核心程式碼
│   ├── src
│   │   ├── api
│   │   ├── client
│   │   ├── model
│   │   └── src.dart
│   └── tencent_cos_plus.dart
├── pubspec.lock
├── pubspec.yaml
├── tencent_cos_plus.iml
└── test                                            // 單元測試

示例

示例目前僅包括tencent_cos_plus_example.dart檔案,主要是介紹如何使用外掛,包括初始化配置、儲存桶Api呼叫和儲存物件 Api呼叫。

├── example
│   └── tencent_cos_plus_example.dart

核心程式碼

核心程式碼(lib)部分由clientapimodel三層構成:

  • client層封裝了http請求和介面簽名功能
  • api層實現了儲存桶(bucket)和儲存物件(object)部分api的呼叫
  • model層負責xml資料和實體物件的解析
├── api
│   ├── api.dart
│   ├── cos_abstract_api.dart
│   ├── cos_api_factory.dart
│   ├── cos_api_mixin.dart
│   ├── cos_bucket_api.dart
│   └── cos_object_api.dart
├── client
│   ├── client.dart
│   └── cos_client.dart
├── model
│   ├── common
│   │   ├── cos_access_control_policy.dart
│   │   ├── cos_common.dart
│   │   ├── cos_config.dart
│   │   ├── cos_cors_configuration.dart
│   │   ├── cos_exception.dart
│   │   └── cos_referer_configuration.dart
│   ├── model.dart
│   ├── request
│   │   ├── cos_bucket_acl_header.dart
│   │   ├── cos_create_bucket_configuration.dart
│   │   ├── cos_delete.dart
│   │   ├── cos_get_object.dart
│   │   └── cos_restore_request.dart
│   └── response
│       ├── cos_copy_object_result.dart
│       ├── cos_delete_result.dart
│       ├── cos_list_all_my_buckets_result.dart
│       ├── cos_list_bucket_result.dart
│       └── cos_list_versions_result.dart

單元測試

單元測試包括cos_bucket_api_test.dartcos_object_api_test.dart兩個檔案,主要是覆蓋了儲存桶(bucket)
和儲存物件(object)部分api的單元測試

└── test
    ├── cos_bucket_api_test.dart
    └── cos_object_api_test.dart

使用說明

關於如何使用,請參考 README

碰到的問題

在開發過程中,實現需要提交xml資料的介面時,出現簽名不匹配的問題。

<?xml version='1.0' encoding='utf-8' ?>
<Error>
    <Code>SignatureDoesNotMatch</Code>
    <Message>The Signature you specified is invalid.</Message>
    <StringToSign>sha1
1666158568;1666218568
488ab174d5f7a6ec0966aeeb82600e185114d7d1
</StringToSign>
    <FormatString>put
/
cors=
content-length=454&amp;content-md5=DfuiC7IOBwIeiVanWPG%2FKg%3D%3D&amp;content-type=application%2Fxml%3B%20charset%3Dutf-8&amp;date=Wed%2C%2019%20Oct%202022%2005%3A49%3A28%20GMT&amp;host=test-app-1251021022.cos.ap-guangzhou.myqcloud.com
</FormatString>
    <Resource>/</Resource>
    <RequestId>NjM0ZjhmZThfNDgzNjQwMGJfMTMwZWFfNDhmODEyZg==</RequestId>
    <TraceId>OGVmYzZiMmQzYjA2OWNhODk0NTRkMTBiOWVmMDAxODc0OWRkZjk0ZDM1NmI1M2E2MTRlY2MzZDhmNmI5MWI1OTBjYzE2MjAxN2M1MzJiOTdkZjMxMDVlYTZjN2FiMmI0MjFmNzE4ZjVmM2M0ZTcxNjYwMmQ3N2QzYjA3NzYyNmM=</TraceId>
</Error>

出現這個問題後,先是確認了簽名文件提供的簽名規則是否一致,然後使用 COS 簽名工具
去驗證簽名結果是否一致,發現在都沒有問題。最後透過api介面返回的錯誤資訊,發現參與簽名的content-type和提交給api的content-type
不一致,導致簽名不匹配。
在這裡插入圖片描述
在這裡插入圖片描述
透過斷點除錯,排查到是使用的 http 外掛造成的,在設定body時如果content-type沒有charset時會賦值預設的charset,問題找到了。

其它

  • 目前不足的是上傳檔案和下載檔案時,沒有進度的回撥

總結

整個外掛實現起來不難,主要需要花時間閱讀騰訊物件儲存(COS)文件,根據Api文件進行封裝和測試,這裡開源出來給需要的人提供幫助。

相關文章