From 4600439633f1fb7fa850ace4baf9bd54e34a7ce5 Mon Sep 17 00:00:00 2001 From: babybaby <90250014+summerboy2134@users.noreply.github.com> Date: Fri, 25 Oct 2024 15:03:09 +0800 Subject: [PATCH] Added JavaScript and Node.js content (#21) * js added * nodejs added * nodejs added * nodejs added * nodejs added * nodejs added * nodejs added * table changed * table changed * js changed * js changed * add-s3 * add s3 * updeate s3 * test s3 * test2 s3 * test3 s3 * test4 s3 * test5 s3 * test6 s3 * test7 s3 * test8 s3 * test9 s3 * test10 s3 * Adjust the order * all * Adjust s3 * modify node.js url * modify node.js url * modify node.js url * modify node.js url * modify js url --------- Co-authored-by: user Co-authored-by: liucheng.yao --- .idea/.gitignore | 5 + .idea/inspectionProfiles/Project_Default.xml | 7 + .idea/modules.xml | 8 + .idea/ucloud-us3.github.io.iml | 13 + .idea/vcs.xml | 6 + _config.yml | 4 + _data/navigation.yml | 158 ++++- ...70\347\224\250\345\267\245\345\205\267.md" | 599 ++++++++++++++++++ ...57\346\214\201\350\257\264\346\230\216.md" | 176 +++++ collections/_js-sdk/img/cors.png | Bin 0 -> 60980 bytes collections/_js-sdk/img/demo.png | Bin 0 -> 64699 bytes ...06\347\211\207\344\270\212\344\274\240.md" | 125 ++++ ...40\351\231\244\346\226\207\344\273\266.md" | 73 +++ ...73\345\236\213\350\275\254\346\215\242.md" | 68 ++ .../_js-sdk/\345\256\211\350\243\205.md" | 28 + ...53\351\200\237\344\275\277\347\224\250.md" | 77 +++ ...67\350\264\235\346\226\207\344\273\266.md" | 50 ++ ...73\345\236\213\344\270\212\344\274\240.md" | 65 ++ ...07\344\273\266\344\270\213\350\275\275.md" | 58 ++ .../_js-sdk/\346\246\202\350\277\260.md" | 28 + ...00\345\215\225\344\270\212\344\274\240.md" | 57 ++ ...41\345\205\203\346\225\260\346\215\256.md" | 51 ++ ...03\351\231\220\344\277\241\346\201\257.md" | 57 ++ ...07\344\273\266\345\210\227\350\241\250.md" | 76 +++ ...22\346\241\243\346\226\207\344\273\266.md" | 80 +++ ...70\347\224\250\345\267\245\345\205\267.md" | 599 ++++++++++++++++++ ...57\346\214\201\350\257\264\346\230\216.md" | 176 +++++ collections/_node.js-sdk/img/cors.png | Bin 0 -> 60980 bytes ...06\347\211\207\344\270\212\344\274\240.md" | 134 ++++ ...40\351\231\244\346\226\207\344\273\266.md" | 53 ++ ...73\345\236\213\350\275\254\346\215\242.md" | 65 ++ .../_node.js-sdk/\345\256\211\350\243\205.md" | 26 + ...53\351\200\237\344\275\277\347\224\250.md" | 80 +++ ...67\350\264\235\346\226\207\344\273\266.md" | 53 ++ ...73\345\236\213\344\270\212\344\274\240.md" | 62 ++ ...07\344\273\266\344\270\213\350\275\275.md" | 66 ++ .../_node.js-sdk/\346\246\202\350\277\260.md" | 29 + ...00\345\215\225\344\270\212\344\274\240.md" | 52 ++ ...41\345\205\203\346\225\260\346\215\256.md" | 54 ++ ...03\351\231\220\344\277\241\346\201\257.md" | 58 ++ ...07\344\273\266\345\210\227\350\241\250.md" | 66 ++ ...22\346\241\243\346\226\207\344\273\266.md" | 76 +++ index.html | 5 +- 43 files changed, 3487 insertions(+), 36 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/ucloud-us3.github.io.iml create mode 100644 .idea/vcs.xml create mode 100644 "collections/_js-sdk/S3\345\215\217\350\256\256\345\270\270\347\224\250\345\267\245\345\205\267.md" create mode 100644 "collections/_js-sdk/S3\345\215\217\350\256\256\346\224\257\346\214\201\350\257\264\346\230\216.md" create mode 100644 collections/_js-sdk/img/cors.png create mode 100644 collections/_js-sdk/img/demo.png create mode 100644 "collections/_js-sdk/\345\210\206\347\211\207\344\270\212\344\274\240.md" create mode 100644 "collections/_js-sdk/\345\210\240\351\231\244\346\226\207\344\273\266.md" create mode 100644 "collections/_js-sdk/\345\255\230\345\202\250\347\261\273\345\236\213\350\275\254\346\215\242.md" create mode 100644 "collections/_js-sdk/\345\256\211\350\243\205.md" create mode 100644 "collections/_js-sdk/\345\277\253\351\200\237\344\275\277\347\224\250.md" create mode 100644 "collections/_js-sdk/\346\213\267\350\264\235\346\226\207\344\273\266.md" create mode 100644 "collections/_js-sdk/\346\214\207\345\256\232\345\255\230\345\202\250\347\261\273\345\236\213\344\270\212\344\274\240.md" create mode 100644 "collections/_js-sdk/\346\226\207\344\273\266\344\270\213\350\275\275.md" create mode 100644 "collections/_js-sdk/\346\246\202\350\277\260.md" create mode 100644 "collections/_js-sdk/\347\256\200\345\215\225\344\270\212\344\274\240.md" create mode 100644 "collections/_js-sdk/\350\216\267\345\217\226\345\257\271\350\261\241\345\205\203\346\225\260\346\215\256.md" create mode 100644 "collections/_js-sdk/\350\216\267\345\217\226\345\257\271\350\261\241\346\235\203\351\231\220\344\277\241\346\201\257.md" create mode 100644 "collections/_js-sdk/\350\216\267\345\217\226\347\233\256\345\275\225\346\226\207\344\273\266\345\210\227\350\241\250.md" create mode 100644 "collections/_js-sdk/\350\247\243\345\206\273\345\275\222\346\241\243\346\226\207\344\273\266.md" create mode 100644 "collections/_node.js-sdk/S3\345\215\217\350\256\256\345\270\270\347\224\250\345\267\245\345\205\267.md" create mode 100644 "collections/_node.js-sdk/S3\345\215\217\350\256\256\346\224\257\346\214\201\350\257\264\346\230\216.md" create mode 100644 collections/_node.js-sdk/img/cors.png create mode 100644 "collections/_node.js-sdk/\345\210\206\347\211\207\344\270\212\344\274\240.md" create mode 100644 "collections/_node.js-sdk/\345\210\240\351\231\244\346\226\207\344\273\266.md" create mode 100644 "collections/_node.js-sdk/\345\255\230\345\202\250\347\261\273\345\236\213\350\275\254\346\215\242.md" create mode 100644 "collections/_node.js-sdk/\345\256\211\350\243\205.md" create mode 100644 "collections/_node.js-sdk/\345\277\253\351\200\237\344\275\277\347\224\250.md" create mode 100644 "collections/_node.js-sdk/\346\213\267\350\264\235\346\226\207\344\273\266.md" create mode 100644 "collections/_node.js-sdk/\346\214\207\345\256\232\345\255\230\345\202\250\347\261\273\345\236\213\344\270\212\344\274\240.md" create mode 100644 "collections/_node.js-sdk/\346\226\207\344\273\266\344\270\213\350\275\275.md" create mode 100644 "collections/_node.js-sdk/\346\246\202\350\277\260.md" create mode 100644 "collections/_node.js-sdk/\347\256\200\345\215\225\344\270\212\344\274\240.md" create mode 100644 "collections/_node.js-sdk/\350\216\267\345\217\226\345\257\271\350\261\241\345\205\203\346\225\260\346\215\256.md" create mode 100644 "collections/_node.js-sdk/\350\216\267\345\217\226\345\257\271\350\261\241\346\235\203\351\231\220\344\277\241\346\201\257.md" create mode 100644 "collections/_node.js-sdk/\350\216\267\345\217\226\347\233\256\345\275\225\346\226\207\344\273\266\345\210\227\350\241\250.md" create mode 100644 "collections/_node.js-sdk/\350\247\243\345\206\273\345\275\222\346\241\243\346\226\207\344\273\266.md" diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..d9343f9 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..7c4836f --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..d775250 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/ucloud-us3.github.io.iml b/.idea/ucloud-us3.github.io.iml new file mode 100644 index 0000000..cf2da3e --- /dev/null +++ b/.idea/ucloud-us3.github.io.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/_config.yml b/_config.yml index e42ff46..fef077b 100644 --- a/_config.yml +++ b/_config.yml @@ -245,3 +245,7 @@ collections: output: true java-sdk: output: true + js-sdk: + output: true + node.js-sdk: + output: true diff --git a/_data/navigation.yml b/_data/navigation.yml index 75979ab..346097d 100644 --- a/_data/navigation.yml +++ b/_data/navigation.yml @@ -57,11 +57,11 @@ go-sdk: - title: 快速使用 url: /go-sdk/快速使用.html - title: 编译指南(ARM) - url: /go-sdk/编译指南(ARM).html + url: /go-sdk/编译指南(ARM).html - title: 初始化请求 - url: /go-sdk/初始化请求.html + url: /go-sdk/初始化请求.html - title: 存储空间管理 - url: /go-sdk/存储空间管理.html + url: /go-sdk/存储空间管理.html - title: 文件上传 children: - title: 简单上传 @@ -69,16 +69,16 @@ go-sdk: - title: 表单上传 url: /go-sdk/表单上传.html - title: 流式上传 - url: /go-sdk/流式上传.html + url: /go-sdk/流式上传.html - title: 秒传 url: /go-sdk/秒传.html - title: 分片上传 - url: /go-sdk/分片上传.html + url: /go-sdk/分片上传.html - title: 指定存储类型上传 - url: /go-sdk/指定存储类型上传.html + url: /go-sdk/指定存储类型上传.html - title: 上传回调 - url: /go-sdk/上传回调.html - + url: /go-sdk/上传回调.html + - title: 文件管理 children: - title: 比较本地文件和远程文件etag @@ -94,14 +94,14 @@ go-sdk: - title: 文件下载 url: /go-sdk/文件下载.html - title: 文件删除 - url: /go-sdk/文件删除.html + url: /go-sdk/文件删除.html - title: 文件拷贝 - url: /go-sdk/文件拷贝.html + url: /go-sdk/文件拷贝.html - title: 文件重命名 - url: /go-sdk/文件重命名.html + url: /go-sdk/文件重命名.html - title: 文件解冻 - url: /go-sdk/文件解冻.html - + url: /go-sdk/文件解冻.html + python-sdk: - title: Python-SDK @@ -113,7 +113,7 @@ python-sdk: - title: 快速使用 url: /python-sdk/快速使用.html - title: 初始化 - url: /python-sdk/初始化.html + url: /python-sdk/初始化.html - title: 管理存储空间 children: - title: 创建存储空间 @@ -121,7 +121,7 @@ python-sdk: - title: 删除存储空间 url: /python-sdk/删除存储空间.html - title: 更改存储空间属性 - url: /python-sdk/更改存储空间属性.html + url: /python-sdk/更改存储空间属性.html - title: 获取存储空间信息 url: /python-sdk/获取存储空间信息.html - title: 文件上传 @@ -131,36 +131,36 @@ python-sdk: - title: 表单上传 url: /python-sdk/表单上传.html - title: 二进制流上传 - url: /python-sdk/二进制流上传.html + url: /python-sdk/二进制流上传.html - title: 秒传 url: /python-sdk/秒传.html - title: 分片上传和断点续传 - url: /python-sdk/分片上传和断点续传.html + url: /python-sdk/分片上传和断点续传.html - title: 指定存储类型上传 - url: /python-sdk/指定存储类型上传.html + url: /python-sdk/指定存储类型上传.html - title: 文件管理 children: - title: 文件下载 url: /python-sdk/文件下载.html - title: 删除文件 - url: /python-sdk/删除文件.html + url: /python-sdk/删除文件.html - title: 查询文件基本信息 url: /python-sdk/查询文件基本信息.html - title: 解冻归档文件 - url: /python-sdk/解冻归档文件.html + url: /python-sdk/解冻归档文件.html - title: 文件类型转换 url: /python-sdk/文件类型转换.html - title: 比较本地文件和远程文件etag - url: /python-sdk/比较本地文件和远程文件etag.html + url: /python-sdk/比较本地文件和远程文件etag.html - title: 前缀列表查询 url: /python-sdk/前缀列表查询.html - title: 获取目录文件列表 url: /python-sdk/获取目录文件列表.html - title: 拷贝文件 - url: /python-sdk/拷贝文件.html + url: /python-sdk/拷贝文件.html - title: 重命名文件 - url: /python-sdk/重命名文件.html + url: /python-sdk/重命名文件.html c-sdk: - title: C-SDK @@ -170,9 +170,9 @@ c-sdk: - title: 快速使用 url: /c-sdk/快速使用.html - title: 初始化请求 - url: /c-sdk/初始化请求.html + url: /c-sdk/初始化请求.html - title: 存储空间管理 - url: /c-sdk/存储空间管理.html + url: /c-sdk/存储空间管理.html - title: 文件上传 children: - title: 简单上传 @@ -180,8 +180,8 @@ c-sdk: - title: 缓存上传 url: /c-sdk/缓存上传.html - title: 分片上传 - url: /c-sdk/分片上传.html - + url: /c-sdk/分片上传.html + - title: 文件管理 children: - title: 获取文件基本信息 @@ -191,7 +191,7 @@ c-sdk: - title: 分片下载 url: /c-sdk/分片下载.html - title: 删除文件 - url: /c-sdk/删除文件.html + url: /c-sdk/删除文件.html c++-sdk: - title: C++-SDK @@ -234,13 +234,13 @@ java-sdk: - title: 简单上传 url: /java-sdk/简单上传.html - title: 流式上传 - url: /java-sdk/流式上传.html + url: /java-sdk/流式上传.html - title: 分片上传 - url: /java-sdk/分片上传.html + url: /java-sdk/分片上传.html - title: 指定存储类型上传 - url: /java-sdk/指定存储类型上传.html - - - title: 文件管理 + url: /java-sdk/指定存储类型上传.html + + - title: 文件管理 children: - title: 比较本地文件和远程文件etag url: /java-sdk/比较本地文件和远程文件etag.html @@ -255,10 +255,98 @@ java-sdk: - title: 文件下载 url: /java-sdk/文件下载.html - title: 文件删除 - url: /java-sdk/文件删除.html + url: /java-sdk/文件删除.html - title: 文件拷贝 - url: /java-sdk/文件拷贝.html + url: /java-sdk/文件拷贝.html - title: 文件解冻 url: /java-sdk/文件解冻.html - title: 签名URL url: /java-sdk/签名URL.html + + +js-sdk: + - title: JS-SDK + children: + - title: 概述 + url: /js-sdk/概述.html + - title: 安装 + url: /js-sdk/安装.html + - title: 快速使用 + url: /js-sdk/快速使用.html + + - title: 文件上传 + children: + - title: 简单上传 + url: /js-sdk/简单上传.html + - title: 分片上传 + url: /js-sdk/分片上传.html + - title: 指定存储类型上传 + url: /js-sdk/指定存储类型上传.html + + - title: 文件管理 + children: + - title: 存储类型转换 + url: /js-sdk/存储类型转换.html + - title: 获取对象权限信息 + url: /js-sdk/获取对象权限信息.html + - title: 获取对象元数据 + url: /js-sdk/获取对象元数据.html + - title: 获取目录文件列表 + url: /js-sdk/获取目录文件列表.html + - title: 文件下载 + url: /js-sdk/文件下载.html + - title: 删除文件 + url: /js-sdk/删除文件.html + - title: 拷贝文件 + url: /js-sdk/拷贝文件.html + - title: 解冻归档文件 + url: /js-sdk/解冻归档文件.html + + - title: AWS S3 协议支持说明 + url: /js-sdk/S3协议支持说明.html + - title: AWS S3 协议常用工具 + url: /js-sdk/S3协议常用工具.html + + +node.js-sdk: + - title: Node.JS-SDK + children: + - title: 概述 + url: /node.js-sdk/概述.html + - title: 安装 + url: /node.js-sdk/安装.html + - title: 快速使用 + url: /node.js-sdk/快速使用.html + + - title: 文件上传 + children: + - title: 简单上传 + url: /node.js-sdk/简单上传.html + - title: 分片上传 + url: /node.js-sdk/分片上传.html + - title: 指定存储类型上传 + url: /node.js-sdk/指定存储类型上传.html + - title: 文件管理 + children: + - title: 存储类型转换 + url: /node.js-sdk/存储类型转换.html + - title: 获取对象权限信息 + url: /node.js-sdk/获取对象权限信息.html + - title: 获取对象元数据 + url: /node.js-sdk/获取对象元数据.html + - title: 获取目录文件列表 + url: /node.js-sdk/获取目录文件列表.html + - title: 文件下载 + url: /node.js-sdk/文件下载.html + - title: 删除文件 + url: /node.js-sdk/删除文件.html + - title: 拷贝文件 + url: /node.js-sdk/拷贝文件.html + - title: 解冻归档文件 + url: /node.js-sdk/解冻归档文件.html + - title: AWS S3 协议支持说明 + url: /node.js-sdk/S3协议支持说明.html + - title: AWS S3 协议常用工具 + url: /node.js-sdk/S3协议常用工具.html + + diff --git "a/collections/_js-sdk/S3\345\215\217\350\256\256\345\270\270\347\224\250\345\267\245\345\205\267.md" "b/collections/_js-sdk/S3\345\215\217\350\256\256\345\270\270\347\224\250\345\267\245\345\205\267.md" new file mode 100644 index 0000000..61d8328 --- /dev/null +++ "b/collections/_js-sdk/S3\345\215\217\350\256\256\345\270\270\347\224\250\345\267\245\345\205\267.md" @@ -0,0 +1,599 @@ +--- +title: 'AWS S3 协议常用工具' +sidebar: + nav: js-sdk +--- + + +## 文件浏览器工具 + +### 功能说明 + +S3 Browser 是一种易于使用和强大的 Amazon S3 免费客户端。 它提供了一个简单的 Web 服务接口,可用于存储和检索任意数量的数据,无论任何时候从任何地方。 可以通过相关配置,直接操控 US3 对象存储的 Bucket 中的文件,进行上传,下载,删除等操作。 + +### 安装和使用 + +**适用的操作系统:Windows** + +### 安装步骤 + +​ 1.下载安装包 + +下载地址: [http://s3browser.com](http://s3browser.com/) + +​ 2.安装程序 + +进入下载页面,点击 Download S3 Browser,按照提示,进行安装即可。 + +### 使用方法 + +​ 1.增加用户 + +点击左上角 Accounts 按钮,在下拉框中,点击 Add new account + +在 Add new account 页面中,需要填写的项描述如下: + +**Account Name:** 账户名称,用户自定义。 + +**Account Type:** 账户类型,选择 S3 Compatible Storage + +**REST Endpoint:** 固定域名,填写参考支持 AWS S3 协议说明。比如:s3-cn-bj.example.com + +**Signature Version:** 签名版本,选择 Signature V4。 + +**Access Key ID:** Api 公钥,或者 Token。具体获取请参考S3 的 AccessKeyID 和 SecretAccessKey 说明。 +**Secret Access Key:** API 私钥。具体获取请参考S3 的 AccessKeyID 和 SecretAccessKey 说明。 + +**Encrypt Access Keys with a passward:** 请勿勾选。 + +**Use secure transfer(SSL/TSL):** 目前仅中国-北京二,中国-香港,越南-胡志明,韩国-首尔,巴西-圣保罗,美国-洛杉矶,美国-华盛顿地域支持 HTTPS,其他区域请勿勾选。 + +具体配置填写如下: + + + +点击左下角的Advanced S3-compatible storage settings配置签名版本以及URL风格 + + + +修改成功点击Close 关闭当前设置,点击Add new account 保存配置,则成功创建用户。 + +​ 2.对象操作 + +### 控制台功能说明 + + + +特别说明:目前分片大小只支持 8M 具体配置如下: 1.点击上方工具栏 Tools,在下拉列表中选择 Options,选择 General,在弹出页面中,设置 Enable multipart uploads with size (in megabytes) 为 8,如下图所示: + + + +### 常见问题 + +#### 1.上传文件超过78G,报错400.显示分片大小为16MB + +##### 问题原因: + +​ s3分片数限制为1万条,设置分片为8M是,只能满足78G左右以下文件的上传,如果文件大小超过78G,会自动调整分片大小,保证分片数小于1万条。目前us3后端s3协议只支持8M的分片,如果分片大小不对,会返回400错误。 + + +## 网络文件系统 S3FS + +### 功能说明 + +s3fs 工具支持将 Bucket 挂载到本地,像使用本地文件系统一样直接操作对象存储中的对象。 + +### 安装和使用 + +**适用的操作系统 Linux、MacOS** + +**适用s3fs版本:v1.83及以上** + +#### 安装步骤 + +MacOS 环境 + +``` +brew cask install osxfuse +brew install s3fs +``` + +RHEL 和 CentOS 7 或更新版本通过 EPEL: + +``` +sudo yum install epel-release +sudo yum install s3fs-fuse +``` + +Debian 9 和 Ubuntu 16.04 或更新版本 + +``` +sudo apt-get install s3fs +``` + +CentOS 6 及其以下版本 + +需要编译 s3fs ,并且安装该程序 + +#### 获取源码 + +首先,您需要从 上将源码下载到指定目录,以 `/data/s3fs` 为例: + +``` +1. cd /data +2. mkdir s3fs +3. cd s3fs +4. wget https://github.com/s3fs-fuse/s3fs-fuse/archive/v1.83.zip +``` + +#### 安装依赖项 + +CentOS 系统下安装依赖软件: + +``` + sudo yum install automake gcc-c++ git libcurl-devel libxml2-devel + fuse-devel make openssl-devel fuse unzip +``` + +#### 编译和安装 s3fs + +进入安装目录,执行如下命令进行编译和安装: + +``` +1. cd /data/s3fs +2. unzip v1.83.zip +3. cd s3fs-fuse-1.83/ +4. ./autogen.sh +5. ./configure +6. make +7. sudo make install +8. s3fs --version #查看 s3fs版本号 +``` + +可以查看 s3fs 的版本号,到此,s3fs 已经安装成功。 + +备注: +在执行第五步,`./configure` 的过程中,可能会遇到以下的问题。汇总为: + +报错: configure: error: Package requirements (fuse >= 2.8.4 libcurl >= 7.0 libxml-2.0 >= 2.6 ) were not met: + +原因: fuse 版本过低,此时,您需要手动安装 fuse 2.8.4 及以上版本,安装命令示例如下: + +1. yum -y remove fuse-devel #卸载当前版本的 fuse + +2. wget https://.com/libfuse/libfuse/releases/download/fuse_2_9_4/fuse-2.9.4.tar.gz + +3. tar -zxvf fuse-2.9.4.tar.gz + +4. cd fuse-2.9.4 + +5. ./configure + +6. make + +7. make install + +8. export + + PKG_CONFIG_PATH=/usr/lib/[pkgconfig:/usr/lib64/pkgconfig/:/usr/local/lib/pkgconfig](http://pkgconfig/usr/lib64/pkgconfig/:/usr/local/lib/pkgconfig) + +9. modprobe fuse #挂载 fuse 内核模块 + +10. echo "/usr/local/lib" >> /etc/[ld.so](http://ld.so/).conf + +11. ldconfig #更新动态链接库 + +12. pkg-config --modversion fuse #查看 fuse 版本号,当看到 “2.9.4” 时,表示 fuse 2.9.4 安装成功 + +### s3fs 使用方法 + +#### 配置密钥文件 + +在 `${HOME}/` 目录中创建 `.passwd-s3fs` 文件。文件格式为 `[API 公钥:API 秘钥]`。 + +公私钥获取方式具体请参考获取请参考S3 的 AccessKeyID 和 SecretAccessKey 说明。 + +例如: + +``` + [root@10-9-42-233 s3fs-fuse-1.83]# cat ~/.passwd-s3fs + AKdDhQD4Nfyrr9nGPJ+d0iFmJGwQlgBTwxxxxxxxxxxxx:7+LPnkPdPWhX2AJ+p/B1XVFi8bbbbbbbbbbbbbbbbb +``` + +将文件设置读写权限。 + +``` + chmod 600 ${HOME}/.passwd-s3fs +``` + +#### 执行挂载操作 + +操作指令解释: + +- 建立 US3 挂载文件路径 `${LocalMountPath}` + +- 获取已创建的存储空间(Bucket)名称 `${UFileBucketName}` + + 注意:空间名称不带域名后缀,比如 US3 空间名称显示为`test.cn-bj.example.com`,则`${UFileBucketName}=test` + +- 根据 US3 存储空间所在地域,本地服务器是否在 {{channelName}} 内网,参考AWS S3 协议支持说明。 + +- 执行命令。 + +参数说明如下: + +``` +s3fs ${UFileBucketName} ${LocalFilePath} +-o url={UFileS3URl} -o passwd_file=~/.passwd-s3fs +-o dbglevel=info +-o curldbg,use_path_request_style,allow_other +-o retries=1 //错误重试次数 +-o multipart_size="8" //分片上传的大小为 8MB,目前仅支持该值 -o +multireq_max="8" //当上传的文件大于 8MB 是采用分片上传,目前UFile 的 S3 +接入层不允许 PUT 单个文件超过 8MB,所以该值建议必填 +-f //表示前台执行,后台执行则省略 +-o parallel_count="32" //并行操作数,可以提高分片并发操作,建议不要超过 128 +``` + +**示例:** + +s3fs s3fs-test /data/vs3fs -o url=[http://internal.s3-cn-bj.example.com](http://internal.s3-cn-bj.example.com/) -o passwd_file=~/.passwd-s3fs -o dbglevel=info -o curldbg,use_path_request_style,allow_other,nomixupload -o retries=1 -o multipart_size="8" -o multireq_max="8" -o parallel_count="32" + +#### 挂载效果 + +执行 `df -h` 指令,可以看到 s3fs 程序的运行。效果如下: + + + +此时,可以看到 `/data/vs3fs` 目录下的文件和指定 bucket 的文件,保持一致。 也可以通过 tree 执行,查看文件结构。安装指令:`yum install -y tree` 效果如下: + + + +#### 文件上传和下载 + +挂载 US3 存储空间和后,可以像使用本地文件夹一样使用 US3 存储空间。 + +1. 拷贝文件到 `${LocalMountPath}` ,即是上传文件。 +2. 将文件从 `${LocalMountPath}` 拷贝到其他路径,即下载文件。 + +**注意:** + +1. 路径不符合 Linux 文件路径规范的路径,可以在 US3 管理控制台看到,但不会在 Fuse 挂载的 `\${LocalMountPath}` 下显示。 +2. Fuse 使用枚举文件清单会比较缓慢,建议直接使用指定到具体文件的命令,如 vim、cp、rm 指定具体文件。 + +#### 删除文件 + +将文件从 `${LocalMountPath}` 删除掉,则 US3 存储空间中,该文件也被删除掉。 + +#### 卸载US3存储空间 + +``` +sudo umount ${LocalMountPath} +``` + +### 性能数据 + +写入吞吐量40MB/s左右 读取吞吐量能达到166 MB/s(跟并发量相关) + +## goofys + +### 功能说明 + +goofys 工具同 s3fs, 也支持将 Bucket 挂载到本地,像使用本地文件系统一样直接操作对象存储中的对象。 性能方面比 s3fs 更优. + +### 安装与使用 + +**适用的操作系统:Linux,MacOS** + +### 使用步骤 + +下载可执行文件: + +[Mac X86-64](https://github.com/ufilesdk-dev/ufile-fs/releases/download/v0.21.1/ufile-fs-mac.tar.gz) [Linux X86-64](https://github.com/ufilesdk-dev/ufile-fs/releases/download/v0.21.1/ufile-fs-linux.tar.gz) + +使用如下命令解压到指定目录: + +``` +tar -xzvf goofys-0.21.1.tar.gz +``` + +默认在 `$HOME/.aws/credentials` 文件里面配置 bucket 的公私钥,格式如下: + +``` +[default] +aws_access_key_id = TOKEN_*****9206d +aws_secret_access_key = 93614*******b1dc40 +``` + +执行挂载命令 `./goofys --endpoint your_ufile_endpoint your_bucket your_local_mount_dir`, 例如: + +``` +./goofys --endpoint http://internal.s3-cn-bj.example.com/suning2 ./mount_test: +``` + +挂载效果如图: + + + +测试挂载是否成功, 可以拷贝一个本地文件到 mount_test 目录, 看是否上传到 US3。 + +### 其它操作(删除,上传,获取,卸载) + +同 s3fs, 可参考上面的 s3fs 操作 + +### 性能数据 + +4核8G 的 UHost 虚拟机, 上传 500MB 以上的文件, 平均速度可达140MB/s + +## 基于 US3 的 FTP 服务 + +### 功能说明 + +对象存储支持通过 FTP 协议直接操作 Bucket 中的对象和目录,包括上传文件、下载文件、删除文件(不支持进入文件夹)。 + +### 安装和使用 + +**适用的操作系统:Linux** + +### 安装步骤 + +#### 搭建环境 + +使用 s3fs 工具将 Bucket 挂载到本地。具体安装方式步骤参考基于 S3FS、US3 搭建**网络文件系统**的内容。 + +#### 安装依赖项 + +先检查下本地是否有 FTP 服务,执行命令 `rpm -qa | grep vsftpd`,如果显示未安装,则执行以下命令,安装 FTP。 + +运行以下命令安装 vsftpd。 + +``` +yum install -y vsftpd +``` + +#### 开启本地 fpd 服务 + +执行以下命令,开启 ftp 服务。 + +``` +service vsftpd start +``` + +### S3FS 使用方法 + +#### 添加账户 + +1. 运行以下命令创建 ftptest 用户,并且设置指定目录。 + + useradd ${username} -d {SpecifiedDirectory} + + (删除用户命令:sudo userdel -r newuser) + +2. 运行以下命令修改 ftptest 用户密码。 + + passwd ${username} + +#### 客户端使用 + +此时,您可以在外部任何一台机器上连接该服务器,输入您的用户名和密码,来管理 bucket 的文件 + +``` +ftp ${ftp_server_ip} +``` + +## s3cmd + +### 功能说明 + +s3cmd是一个免费的命令行工具,用于使用S3协议上传、检索和管理数据,它最适合熟悉命令行程序的用户,广泛用于批处理脚本和自动备份。 + +### 安装和使用 + +**适用的操作系统:Linux、MacOS、Windows** + +#### 安装步骤 +``` +1.下载安装包 +https://s3tools.org/download ,这里以目前最新版本2.1.0为例 +2.解压安装包 +tar xzvf s3cmd-2.1.0.tar.gz +3.移动路径 +mv s3cmd-2.1.0 /usr/local/s3cmd +4.创建软连接 +ln -s /usr/local/s3cmd/s3cmd /usr/bin/s3cmd (权限不足可以使用sudo) +5.执行配置命令,填写必要信息(直接跳过也可以,可以放在下一步手动填写) +s3cmd --configure +6.填写配置 +vim ~/.s3cfg +打开当前配置,填写以下参数 +access_key = "TOKEN公钥/API公钥" +secret_key = "TOKEN私钥/API私钥" +host_base = "s3协议域名,例如: s3-cn-bj.example.com" +host_bucket = "请求风格,例如: %(bucket)s.s3-cn-bj.example.com" +multipart_chunk_size_mb = 8 "us3 支持的s3协议分片大小为8M,所以这里只能填8" + +``` + + +#### 示例配置项 + +``` +[default] +access_key = "TOKEN_xxxxxxxxx" +access_token = +add_encoding_exts = +add_headers = +bucket_location = US +check_ssl_certificate = True +check_ssl_hostname = True +connection_pooling = True +content_disposition = +content_type = +default_mime_type = binary/octet-stream +delay_updates = False +delete_after = False +delete_after_fetch = False +delete_removed = False +dry_run = False +enable_multipart = True +encrypt = False +expiry_date = +expiry_days = +expiry_prefix = +follow_symlinks = False +force = False +get_continue = False +gpg_passphrase = +guess_mime_type = True +host_base = s3-cn-bj.example.com +host_bucket = %(bucket)s.s3-cn-bj.example.com +human_readable_sizes = False +invalidate_default_index_on_cf = False +invalidate_default_index_root_on_cf = True +invalidate_on_cf = False +kms_key = +limit = -1 +limitrate = 0 +list_md5 = False +log_target_prefix = +long_listing = False +max_delete = -1 +mime_type = +multipart_chunk_size_mb = 8 +multipart_max_chunks = 10000 +preserve_attrs = True +progress_meter = True +proxy_host = +proxy_port = 80 +public_url_use_https = False +put_continue = False +recursive = False +recv_chunk = 65536 +reduced_redundancy = False +requester_pays = False +restore_days = 1 +restore_priority = Standard +secret_key = "xxxxxxxxxxxxxxxxxxx" +send_chunk = 65536 +server_side_encryption = False +signature_v2 = False +signurl_use_https = False +simpledb_host = sdb.amazonaws.com +skip_existing = False +socket_timeout = 300 +stats = False +stop_on_error = False +storage_class = +throttle_max = 100 +upload_id = +urlencoding_mode = normal +use_http_expect = False +use_https = False +use_mime_magic = True +verbosity = WARNING +website_index = index.html +``` +#### 使用方法 + +##### 1.上传文件 +``` +s3cmd put test.txt s3://bucket1 +``` + +##### 2.删除文件 +``` +s3cmd del s3://bucket1/test.txt +``` + +##### 3.下载文件 +``` +s3cmd get s3://bucket1/test.txt +``` + +##### 4.拷贝文件 +``` +s3cmd cp s3://bucket1/test.txt s3://bucket2/test.txt +``` +##### 其他常用操作 +``` +1.上传文件夹 +s3cmd put -r ./dir s3://bucket1/dir1 +2.下载文件夹 +s3cmd get -r s3://bucket1/dir1 ./dir1 +3.删除文件夹 +s3cmd del s3://bucket1/dir1 +4.列取bucket列表 +s3cmd ls +5.列取文件列表 +s3cmd ls s3://bucket1 +6.归档文件取回 +s3cmd restore s3://bucket1 +``` + +## rclone + +### 功能说明 + +rclone是一个命令行程序,用于管理云存储上的文件,支持s3协议 + +### 安装和使用 + +#### 安装步骤 + +``` +curl https://rclone.org/install.sh | sudo bash + +//参考 https://rclone.org/install/ +``` + +#### 配置 + +``` +rclone config +``` + +#### 配置参考 + +``` +[s3] //这里可以填写s3等自定义名,作为命令前缀 +type = s3 +provider = Other +env_auth = false +access_key_id = xxxxxxxx +secret_access_key = xxxxxxxxxxx +endpoint = http://s3-cn-bj.example.com //参考 +location_constraint = cn-bj +acl = private +bucket_acl = private // public/private +chunk_size = 8M //目前只支持8M分片 +``` + + +#### 使用方法 + +说明: 以下命令前缀(配置内的中括号内容)以remote为例,使用过程中需要自行修改 + +##### 1.查看所有bucket + +``` +rclone lsd remote: +``` + +##### 2.列取文件列表 + +``` +rclone ls remote:bucket +``` + +##### 3.上传文件 + +``` +rclone copy ./test.txt remote:bucket +``` + +##### 4.删除文件 + +``` +rclone delete remote:bucket/test.txt +``` + diff --git "a/collections/_js-sdk/S3\345\215\217\350\256\256\346\224\257\346\214\201\350\257\264\346\230\216.md" "b/collections/_js-sdk/S3\345\215\217\350\256\256\346\224\257\346\214\201\350\257\264\346\230\216.md" new file mode 100644 index 0000000..0e611aa --- /dev/null +++ "b/collections/_js-sdk/S3\345\215\217\350\256\256\346\224\257\346\214\201\350\257\264\346\230\216.md" @@ -0,0 +1,176 @@ +--- +title: 'AWS S3 协议支持说明' +sidebar: + nav: js-sdk +--- + + +## 概述 + +S3协议是AWS推出,在对象存储行业成为事实标准,US3产品在自有标准的基础上,增加了针对S3 v4协议标准的兼容支持。 + + + + +### 支持的 API + +US3 目前的 S3 协议模块对标准 S3 协议的支持如下表: + +| **编号** | **API名字** | **备注说明** | +| :------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | +| 1 | [HeadBucket](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadBucket.html) | 检测 Bucket 是否存在以及您是否有权限访问该 Bucket | +| 2 | [ListBuckets](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListBuckets.html) | 获取 Bucket 列表,只能获取公私钥或者 Token 拥有者创建的 Bucket | +| 3 | [GetBucketLocation](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLocation.html) | 返回所在地域名,不建议依赖该 API | +| 4 | [GetBucketAcl](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketAcl.html) | 没有太多意义,主要为了支持 S3 Browser 而实现,响应体中的 `Permission` 字段永远为 `FULL_CONTROL` | +| 5 | [GetBucketVersioning](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketVersioning.html) | 没有太多意义,主要为了支持 S3 Browser 而实现,响应体中的 `Status` 字段永远为空字符串 | +| 6 | [PutBucketLifecycleConfiguration](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketLifecycleConfiguration.html) | 为 Bucket 创建新的生命周期配置或替换现有的生命周期配置规则。注意,这将覆盖现有的所有生命周期配置规则 | +| 7 | [GetBucketLifecycleConfiguration](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLifecycleConfiguration.html) | 获取 Bucket 中设置的生命周期配置规则 | +| 8 | [DeleteBucketLifecycle](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketLifecycle.html) | 删除 Bucket 中设置的所有生命周期配置规则。注意,不支持删除 Bucket 中的指定某个或多个生命周期配置规则 | +| 9 | [GetObjectAcl](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObjectAcl.html) | 获取 Object 的访问权限信息 | +| 10 | [PutObjectAcl](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObjectAcl.html) | 设置 Object 的访问权限信息 | +| 11 | [HeadObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html) | 从对象中检索元数据,但不返回对象本身 | +| 12 | [PutObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html) | 向 Bucket 中放置对象 | +| 13 | [PostObject](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPOST.html) | 使用 HTML 表单将对象添加到指定的存储桶 | +| 14 | [CopyObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html) | 创建已存储在 Bucket 中的对象的副本 | +| 15 | [GetObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html) | 从 Bucket 中检索对象并返回 | +| 16 | [ListObjects](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjects.html)/[ListObjectsV2](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html) | 返回 Bucket 中的部分或全部对象 | +| 17 | [DeleteObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObject.html)/[DeleteObjects](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html) | 从 Bucket 中删除对象 | +| 18 | [CreateMultipartUpload](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateMultipartUpload.html) | 启动分片上传并返回上传 ID | +| 19 | [UploadPart](https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPart.html) | 在分片上传中上传一部分。必须先启动分片上传。 | +| 20 | [UploadPartCopy](https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPartCopy.html) | 通过从现有对象复制数据作为数据源来上传部分,必须先启动分片上传。 | +| 21 | [CompleteMultipartUpload](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html) | 通过组装先前上传的部分来完成分片上传。 | +| 22 | [AbortMultipartUpload](https://docs.aws.amazon.com/AmazonS3/latest/API/API_AbortMultipartUpload.html) | 中止分片上传。分片上传中止后,将无法使用该上传 ID 上传其他部分。 | +| 23 | [ListMultipartUploads](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListMultipartUploads.html) | 获取正在执行的分片上传请求 ID | +| 24 | [ListParts](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListParts.html) | 获取正在执行分片上传的分片信息 | +| 25 | [RestoreObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_RestoreObject.html) | 解冻处于归档状态的文件 | + +注意: + +* PutObject 目前仅支持 1GB 大小文件,如果需要上传大于 1GB 的文件,请采用分片上传的 API + +* PostObject 目前仅支持最大 32MB 文件的上传 + +* CopyObject 目前仅支持最大 100MB 文件的拷贝 + +* UploadPart 目前仅支持 8MB 定长分片大小(最后一个分片允许小于 8MB)。若有不定长分片的需求,请联系技术支持 + +* US3 S3 对 AWS S3 兼容的存储类型及其转换规则参考 [存储类型转换规则](#存储类型转换规则) + +* US3 的 ETag 计算方式与 AWS S3 存在部分差异,建议不依赖该 ETag + +* 目前不支持 S3 API 的 MD5 校验,建议关闭: + + 例如AWS S3 Java SDK: + System.setProperty(SkipMd5CheckStrategy.DISABLEGETOBJECTMD5VALIDATION_PROPERTY,""); + + System.setProperty(SkipMd5CheckStrategy.DISABLEPUTOBJECTMD5VALIDATION_PROPERTY,""); + +* US3 的访问权限(ACL)定义与 AWS S3 存在差异,具体参考 [访问权限定义(ACL)](#访问权限定义(acl)) + +* 目前文件访问权限控制 API(GetObjectAcl、PutObjectAcl)仅在部分地域支持 + +* 目前生命周期配置规则控制 API(PutBucketLifecycleConfiguration、GetBucketLifecycleConfiguration、DeleteBucketLifecycle)仅在部分地域支持 + +* 目前 UploadPartCopy 处于内测阶段。若有使用需求,请联系技术支持 + +* 目前不支持多版本功能(Versioning) + +* 目前不支持标签功能(Tagging) + +* ListObjects请求中的max-keys参数(请求返回对象的最大数量)最大值为5000 + +### 访问权限定义(ACL) + +| US3 ACL | [AWS S3 Canned ACL](https://docs.aws.amazon.com/AmazonS3/latest/userguide/acl-overview.html#canned-acl) | +| ----------------- | ----------------------------------------------------------------------------------------------------------- | +| private | private | +| public-read | public-read | +| public-read-write | public-read-write | +| 不支持 | aws-exec-read
authenticated-read
bucket-owner-read
bucket-owner-full-control
log-delivery-write | + +### 仅支持签名 V4 + +支持 V4 签名的场景: + +1. URL 中携带参数(URL Query 部分的 x-amz-credential 字段); + +2. POST(表单中 x-amz-credential 域); + +3. Header 中携带参数(Authorization字段); + +### S3 的 AccessKeyID 和 SecretAccessKey 说明 + +S3 的 AccessKeyID(或称AccessKey)和 SecretAccessKey(或称SecretKey)对应就是 {{channelName}} 的 API 公钥和私钥,或者是 US3服务提供的 Token 公钥和 Token 私钥; + +**注意:要求无论是 API 公私钥还是 Token 公私钥,要求操作的 bucket,必须满足以下条件:** + +* 创建该 bucket 的账户与 API 公私钥的拥有者必须一致; + +* 创建该 bucket 的账户与创建 Token 的账户必须一致; + +### S3的分片大小说明 + +1. 为了达到更好的传输性能,默认情况下仅支持8M大小的分片。 +2. 部分地域已开通动态分片功能,如果固定8M分片无法满足需求,可联系技术支持开通动态分片。 + +### API支持路径风格和虚拟主机风格 + +**路径风格格式为: `http://\${Endpoint}/\${bucket名字}/\${key名字}`,bucket 名字作为路径使用的一部分。** +例如,AWS S3 Java SDK 在 {{channelName}} 北京地域走外网使用 US3 S3 服务则设置如下: + + + "AWSCredential credentials = new BasicAWSCredentials(ACCESS_KEY, + SECRET_KEY); + ClientConfiguration clientConfig = new ClientConfiguration(); + ... + S3ClientOptions clientOptions = S3ClientOptions.builder().build(); + clientOptions.setPathStyleAccess(true); // 表明使用路径风格API + AmazonS3 conn = new AmazonS3Client(credential, clientConfig); + conn.setS3ClientOptions(clientOptions); + conn.setEndpoint("s3-cn-bj.example.com");" + +**虚拟主机风格: http://${bucket名字}.${Endpoint}/${key名字},类似US3目前使用的URL形式。** + +## 访问域名(Endpoint) +访问域名的一般语法如下: + + **`protocol://s3-.`** + +例如,http://s3-cn-sh2.example.com 是中国上海地区的 example S3 服务的外网访问域名。其中,**s3** 是AWS S3服务的``, **cn-sh2** 是``,**.example.com** 是 ``。 + +对于内网访问域名,可以使用如下格式: + +**`protocol://internal.s3-.`** + +内网的 **service_code** 前需加上 内网访问标识符 **internal.** +即上述示例访问域名改为 http://internal.s3-cn-sh2.example.com + + +**注意:** +目前华北一,香港,胡志明,首尔,圣保罗,洛杉矶,华盛顿地域已经支持https协议,其他地域可支持路径风格https,后续支持虚拟主机风格https (所有地域内网不支持https) + +### 关于 region_code.custom_domain 的说明 +进入控制台的对象存储模块,在单地域空间管理分页下找到以下标签`` +该标签由 `..`组成 + + +## 回调扩展功能支持 + +| **请求形式 API 名字** | **PUT Object** | **POST Object** | **Complete Multipart Upload** | +| ---------------------------------------------- | -------------- | --------------- | ----------------------------- | +| **在 URL 中携带参数** | √ | × | √ | +| **在 Header 中携带参数** | √ | × | √ | +| **在 POST 请求的 body 中使用表单域来携带参数** | × | √ | × | + + √:支持 + ×:不支持 + + + +## 存储类型转换规则 + +| **US3存储类型** | **S3存储类型** | **US3对应S3默认存储类型** | +| --------------- | --------------------------------------------------------- | ------------------------- | +| STANDARD | STANDARD
STANDARD_IA | STANDARD | +| IA | ONEZONE_IA
INTELLIGENT_TIERING
REDUCED_REDUNDANCY | ONEZONE_IA | +| ARCHIVE | GLACIER
DEEP_ARCHIVE | GLACIER | diff --git a/collections/_js-sdk/img/cors.png b/collections/_js-sdk/img/cors.png new file mode 100644 index 0000000000000000000000000000000000000000..7d35479e399c8a41693a922ee82b8886bfe49bd1 GIT binary patch literal 60980 zcmeEuWmH_twl3}#AR%az1P$(P0fJj_hv4q+?(Xgof(3VX*Wm8jIF0i<_v~}dX5ZiM z&l|6M^jOv1RkccHRn7U$Z>|toX%SQ;JR}GR2vjjqp>Gfn&~^|IP_zhe;9uU-%I$$K zkapihzCe_Z5gdTO_#3E*8A?e(dXNiuO;jwNEw#;0vO) zs4BR{IGBGvkVaWk;4bh(hzSWOI71$9R%if<}aWJS`cvC&2#VL-ohnZ#}`N> zS#K!pYzSBi{{KlrJ|V74kaJ#OjStHRjX z{MJ6dpc;QAE`VH>baoq-?+GqeJTW}~TmQdq<&%W@3#vZ))K%6@^%s^3WU7a*tgNiI zORKInF#rl0TF_#zAv25eUrqW?>(+Z=ELWOXD5O%E@eQm{7rR^!d<0?b%bR9$*iDG{ zwzm}~b#XN`Gzh%5jsDRs>|hd~Aanp)oM7Xi1auvLP||gEo0+fF+6k#Xy^(e&`hRZn zSBEgJq1dceucE7a;*#KOAulexF3PiN_b+sO(rN8ccIV^239I}yWdANIl-F^xRL`?x zf4)=DnU>X(p4Bp(hPYW;ntokA><4Ri&jih>sFU-bq5hw87*bG}BPPU2qFQ(ZcSFM> zT52C>N4~*x=>j;?2ww94k@+_i2)Ht>Lz~mW96eo{{%&+ib(Hp+N5RJ2u$U?(1zETU z_TR1fYg%)q`Qi8{pKb9bR6og>o)xNco^Ta)X7rhKrm75$-xwyMY%2H#{9OXTlgNyG zdw<_FOv#cxor}vcI$^Ro$`Bm~kg9}tP(qSPQk%@Qz`->~)!+TE36X}xtryy2qzbNv;-GS#2GJw?HPWj#ME|2LR|qgL zFe;E51Cq;jR2@3NMsAIN+?iMnrW8e^0hgnM6=jziu}%K9Ox{|-mU@r<40t=Q$_MXe zM2{2h$M;(1v7K39-jB56fkJBkIpC7S+xZ)vN?-fX=d6~$#tI>BY!v622h!ruYW1er zCCQfCzhUR%pMFUZstRyVGZg&ic_2CulW1B4xtJR91x)ZQ#ZVo+@ zh-e^%hsm`439ia$G#Kj6_9KPpt`+ja8AI$L25wNug-*G|sa2)UAb`Ej4R@+Yg7D|q z%6&waReMG6suVIw_Ru@i)-7KVEv+9KM|SblS_ol=UZ)L64?EJWz zAX;f#&~-sUzxV0+Q@-}u-s$w&*Sk4Es}a&yLdhSG=jt#nZXGWQ-blQjUVT&qjeEQiG)4@`;cGb+me?rg;ypgKFcRuAZySn z^b+=tEiB7VCHl_g$KI03+dNJLgc?}kG3l{^Sjr3@jH$5e3s;ntcK!30fTo))PgQ^2 zJWucT`Y6dVoR98_X;;@c0Nv$!9^Vr->su1L$7?T({*6yX$4BV~FEO@TyH9nPjC;_I z$GpMgZ76LOlE*=+(}7)LRUt0~ypeQEH`S-7w>c(TO~&^J517ZWxzqR++AZ@i)`EQuhsx@7t}qq=k~nGO!*7Tm}U#LKsuGHgQ<^`siHtE z2_g%V-lp%g5#1)}Ek6DC3DA2c8?qZ9pRdDkQJRqpl>pX8oX7^69Nq{GR`Agt?f1T! zoEwSvT<17fCl7=q;;{yp_Kob_q@LVYhg^8AP3ZM>c2XFCqMM2@hKTz$l)I3K$!LVU zy5F*MBi@>^rs%;Txfj@yu7B(oBkphXu(SHHn`&aKK!0?S5RLw+PqM%6#4ScJRmO6q z4c+tkc_5lM;`oR}(UzfeCEGJEhFefgqsLA>Vhy!F2Mp|T}! ze(%PX&TZFyx}l2&5xBU)r-f0_sZm|$5mRq1(4tsS`ozeyyh0L<+_e+7biTgv>A|)i zooSD{x1qRNN~d6WZ$ul-q8wKnidzQJ-4&h4Xl;3o4+6gK(eR@QYSk{SMgU0&X_MzS z<_1@4x_;ZMTih#r&Nr~g+nvDC%gMSej7N4i2G6Gh^%gQI254v~xg)=d^^XU?YbA>E zL~~k#lYYMU&0iI9qzTB%YUfBdoFN!)uyXqn_0*fR^6Lf!SWj(W{7Dq$efS_$BR zkJ`E|w!7s&RJ*O^-jz+&qLJT#wm-P02}Ir3A>^HJu1=TGjx;EPu@%g}D}wV)T`gPs zLGy0B@)}g4acQ^QXby?1|Z*^L|C zli_dsUd!MF{5IoU&CP3~vbRHp9U>B3rBl8ZeO{QF#p(*bcw;{t+3LJ^GQ>)KpzplB zPs#1P{dTv2fX7DdQt2j^Oc5zP5mLIwORYIEE17!kotVqmgKDao_+)maaNND|lY!s^ zZzmsniedaB^|!R|iIl4EA6}lNQiXW?cBtm0sz=uLBI4xC7?bf^E%rRqy6)^oG7esKfDkt#D~+@%Nu5vwF~S7X)PKeFaX$#NF1opdT+X)6I)9 zhbh0r-KbPr#~u_G(42c_oYROaJtO#imF^3=HWi(tGBJ5CC>{@E_XH>7%}OH~!%eA0 zbxj0Z_V8{_s?c2?AbQVb@@_dlPWSnX6^~oG4C123ri*BO6FK29jM@1GUktO@#}=(k zmPZn)j}8=aG>hAnBWZv%V#y*_1n?^{Wm-of&jO&W-6EgI=$X!pQ8H`U*X%&r`WZTJ zKFo0D${sIc7*#x)8u4e_C2W3Png>=0nZZ+>991jP=5}#=%qO?Pq9Z{Mq%JmXmal%G zwXd_(2XgdY(#!2H2xXk8@q!#VroB_Qo94mxMPh^`7tj0HT&5V>MyH) zE@4VJX3cc6YK*pO%uJxYR8_K)cU7wSVKB~Svz9)F??h0K0>2AiEctR*7d_@s3L@S! zvya5k~2F9d=Ws-r`;sf(uQ8&`PcysOg55!_H*a z7v%MK<%pOO*74*H{#?JK)5%o15}=f(1YBevC&_RGKEU@<&GcU(Ld#X&7oFX>gu6QY zT!7{peR=2_hoxOusQ(YBgya3|9Q{j2J8SQd5}KVpT3q z{>CW=@lHwHQBuUwU1_oy@uwm!D(b%FazjAg6g5+&{w@-Bgd_I)DwyVW-bJ%#vEJQJIe-o#6WZ9<_m#->!)4(#?Sk5u$laDDQY8b&iC#gY9t+b_9$805K~=qcSiu82uW-V z^*E85pQKBPD7Uqm+C7VZJ9l-GODn?ec#Xr3Ew|k&_reHqh>?E&bFxA%N%AzZLVk>% zXHVg8{9lew6>ySC+KG3+H8e5|Y6cP!T0j%b4)%%bqWU+7;2n0_MVTHGixA=kTRDt6ay7pYNwY_)KXnK%3_?HI(RRqWurZ4*M%T#_Dv*-s}NG5al=#PpgQ24tbD{sb7Pjjpvb1*=HVhfWX z867$=L(yzERhW&Za@k7l2VYXmGoMSP$UNLht9R+6Lut`fYlw(|V#i_VM4X0IF60KT zzR{iOt>rgmS(gH=7p!Bnx?=N}s>%&?;N8I`G!)8pQouqYyn^Z`9s0_z6Dp`)@#6j)fwNHI-6%W{Xir z-zb+<^n2odN{t4%&E8=HEg*0~wq6Ta^pk48V8HV+Mo#Sk^ZQo#2ob21r1Dkr6G?fw zm5nF|bq1$S@y7GHn0eiA7ZdQvZ9?tTV~n;1ZHMikceGje$T(3Jzgi=$;$1*LQypYB zC)c?y$zMtRdH}3eT*Fppx5)dO74#cDV*dD(RkoZJ*abeg$5($Z^|#&L8Z zF+GG;G@d6Eh_0uv25U5UbA3z=`Tf1{UZ!2Tbf-pewIGL&L`qN{7n09HZRrQ71V=_-y23S$WBUpl4}HJ7DbnOT zrH5#%{YaOuy4?%nd9ey>fwXU8{sUP81@dlbm`0r`*g#1(`5HXKYsWh^BD+S~)a{Ab zQYVpBXJ~Q1Id<1RN=%Hb|J{<1x5oB_Z1#if>56Hg$DtXl-3R3x>R1l^p!Pao*Bg^m zn;=4My@y|pjiH*ONu%|+v&_jvnPAjapbMEl&9ls_n+WN_pYg!&hN}@dRL~u#IX75p z+r8(;B#VAygD3WdK0Q3UmF)2~)Fo8MpUO-*QJ~2bp2-e<(uDp;VIQHKDijOZLALlb z8N|IO6KAf;{vtna||r$LL2ECQ!{hqh)6jBuj}hGo4v83EC(Dopxg+o!WKZhF;OAaT08( zLOrS^<)j1VL0I8rwY1%O@ukE-aI#V(U3Yx>tsYj}Z4(>O;C`~OK1Qrp9Ua4)yYxFM z$yOGm3YBsg>0|YPp<3SVQthNoqmiSwwq>*JDt2HpMR!^(%j>FFF65I#vlD&K|;aqKD;aKU0rK09U8dRpF->l zQkk1T?VKS=No+r7Oh0-6Y4a?W$c!K5PP}Th?JcoMJC{G<&gjQ5=eUEqIrryxnc^vw zARO>L{hS$PG+wkUtU-}uEAJd9iT4x3+4UmcW8EmpB$-zCQ8BIvAP(LfJlZyr+9i=Y zu`(5`hI72SVrmO~F;_2i^h+1gW zYda;)Ez9PdjEr#dMW5}@-u62@O1?tdTGGAi?zt+h0!Y49eeWYsXR8?0yXOXFAF-PE z7tu6t750$g(S;MuUN*>R_P%guuAR@%fF4GVeLMYs1HiDuC{B-2L0&Mb|Hz%$J)uhJlQElcAx z9rmoW)!C`y?+nLvUoDj=6;N~5-f8T%BCp;kK7cyshKc+SJQo3U?fUPXtnS}>SpIN1mq1nNOIx7CfdlAi65k* zb>?zu=(ekxi8F8@jhiWn^gII1UOHg{^AU64puAY$8_T2e@5;P4RHa0=@bvu;%`H@o zCR@}?1K#5!HsJbMOdN#@F%A;Wh+pWAL^gjL!3~LmWZS(l>h5<&n4_<+M6U_nF9kIlT2=vKeq-?QC0c0Der7_tWN98p(>kah=8!V0j^#4!Zpy8`BX*#L7y^T#*ZJI!X z4UY5rTYr?|^k@!3jQ3!7i;%L^#}{sCX=wx`B&5rZD)HHX#!%!}3kMRZS0Z&>Py-VypKqh zmb9^`_6(e zAWYvYxIbWp6Y+J=^e@g;j;XQn^nt+;xnKH(4FkM|qG$2yhn$j!`JKfT6%{V3o$(fV zMP8e26H9!JIBwf<#SMAcY>~kWjTvT zj_G{k9>6ZD#!_k0Eqm;w2?h0%NOkF;`TmL9jiCzLSw6h$CA1_D&rHXCV^fjfaqs=C z)uNB;WnzwEkPn5{26T(kA>lTS7M2AkI_nZ=rqy)f3We%{AmXh%$%Ip9Y=PP2FR{J7 zS^1NzboKgE!I+EFwDG2jwyUNG6rw0RhqNHhq%D`bl>eSU#Foe~G7Q310cj-ksaI#6 z0cHZxMmBB-zh_3df1y1sC@&I=%YHlp@*#DJRGkbl5H(SB?eq-V&D*Ubtd=W5){~Z0 zZr3ZS-1QqiHtOPUiX|F7Z~mT;RIGs1Bkj&j!O}+AU%*4e>|xtOie}c$hT(&R6qs)X z{X%3f6DNtcfFh}`JMEB*r#{DP0^vub$OnE@ts-If+$89e&NP6c8dnhBp!V^pr~r^f z0R03$6(yrYm{on*l`W?&kF~MwZz~InX14j5KddQN#TH_-*GH2tnTE$Uo*6qbO*-;o z5rIen6tjPL2)(}Puu*xr`2kkrA_=%6z-CYO*iwRI-|!Ikodj9c?gI^}3Q`B%WfB+_ z3)tl(5}mDlhG$`T2-)GX-AalzpwE#vQpE5NW~N&tANy(+JPQ#a91r zX>|v=@T(egbTc_GrTlJy$dEnRr(_bFFC;O?y0Ii7(F$5%vlmdYGam5Ac`8$GZX1=! zxz7N`vwpyTX>rcH;oJvFgcT=wMmH7v;2pBqpK>kgEAVVTSc4X&m~UIKVoAX6tVXIf z{6$!JG{WxY+SM5zKUjND&(6^ixWAh!#L`GYs`N#jOHu9co68BrF4?G-X`GE%Rrz+Y z#QcDt7DwB6vsZZ!E$!%rXlLFv^x?9T5cFVs6jSIraby9JI`%l&1at2&un}3gYj_Di z0C~5!V*_ild3X*JVV?+1=0#@AabxqX!q01y9}uK%+&}6MnJ2g(kF=O}pCb*kJiO~k z+`fSw7|F~((WeY31MJA3zZf1M%qufe5fNE7*tjzBnk`8As^W~?*}y=Ns?2oAR`Vlb z_4NZIE@MiP^`CCanHTuH2Gtx6SukBLwmU`!1$)QdE;#{&!rp1hwHOOvA(*c<0yG~y zgSxqrc#2u9cNS^3>9mkRKtB3myPbjWP>p*1Ug~0&2%mrmX z4pifEt@e=54yp4|d$%7f4&&Jyy$TNIet;K74bL6dI?1_)|B8iiyV@9yf~Pno7{IeX*HV+%devxy!0;D z+(_j2!ofbTE|F9&wD+uHZGdMAxqM>N)~7(k=)%!kDJW2T@4}5bG*2d+-&WzHZ(hIj z0p)RlZSOa*4N3Sge$Qep9g7)dbsVF20Q%zBS5#64F{QS7Th02g>~!`6G14o&5n*hK z&OQvLplDpHV`M>MgJzUQDrU=sX}PK(Y=|Sj_{Zb-PWP8>p_QgO#&*#O>fFKkLG5s_ z$n+i}MAvpgP@aR@-D;gslB4!K&k5D7&);?+X(WGy>W|~=vphP7-`8Y)mM_P$$Jc_! zVK%G|w3WHrPjVhzmf*EwMv%&}2Iv;3d4e zyfE_2WJKo!M@gvW5%_UO-($^Dl=kUl(GfTND`2LN9Q81nS}7$qz~*2d4vX7FHdY3z zW1P=(9L6A3ZiZOQkGIvZ#MnD$fntE~196?zM4=a8_mS534qlVMm=xnR_D9{tAB z>Odk#9^#Na*EMkBp6QXk-PPoski|JJdRka$* zNnqC-h?kv=JjAV!)-|*GIcVS?!(;86V3c}5j%T!c3r< z^e|Ply>96un`V3ob0$Ohbd2V_T7R|1n0UI*K#wG0H@fM0|D0)`Otn%mtZ*os5>)y9 zT05D^E1*%Dutt!{Q)2tp6Y_aVyF7{eZ0WwgH|o^nFt0R0$mbN-M8r_Y^D>+44Rp0%%CZ8Jh`{HI$VYV zBW2Z^OZ^TEhR#lIvKwTOC9G_}FCC69Ghi%$`wPXem||FbG}<*l(nvRJ)0fX2Q8e?v zcksfE>!{2MQ3jH@xW0*JJMS)&Y8ucj`dg1x44)V4zX`fJ@lG7YPu3YtB(1h6+~aDq zBE5K95<(EL+GT1plEB2I<~a?3Mc(bKQpEpdzvJ4dZv+h{b(5 zq@v4~3PwR%D0ep=xR7!;EHocpdOuu2f`-We;?s>z6QvGT4HB^TAoBoUOzJDbi8pVD zTO*GcO|zAXzV*gZP(9U#DnCNt&}nzq*g#tw_kXho7Dy38pFa%%-!r=-O$}4{p0qvp zpv>FcEDJOF=Av2K@K@2JlXe&SnUe$q0H5F-m|5&mRG4)>U#TfdrcG$8AH!0mPKsr6 zhakq!7%lvauq_)?sk8MPWM+9N%{2K5N<#UWlhT(~Z<)srBp{2PDvgU&DV|GXb5hc1 z{Y6+ZXHf^>Q{9~+Tb~J6yc|E&)2U!hre0`{W3-lVL<}$G=Z9{TXB^r^FtKfNo@B=Q zVcJ^Lb9oitA$|WQ@CO)Cw|ikX$#%Exs>ynnJ(pe78LIWNZ^JZE_TrSxr&n_&AmJ0C ze&atGtr!I}lFAB|;fj(pq9euK(uCg)Zy#M4KrZ>xia5QSEsu{Vo#pOR>T1>}dz3Al z_LK8n|Fe7KaM?Ik(O7Rf?68gX111)V`tbG2_#8Yfrv55-^KX>zn+>$3xF4*r0xtwp=!H)Zk1yIQ@VDmUYVxPsj6lM^|M*FMQ zZ`ciL-O+dsm<*p6GFsrv&GZ z0K@)egY5(;MxEYe7p$1Cg;-~CWLAS&%&jxfgdDDTdO;we^Y5@DHY+QdBi%0E)6-ao z*l;eF`f(%CX(o=zv{K^Jr|XM+d_htI7avU>g9ei;4E>oW>#a^o zK$8$ZXU!8z!m9I%hO*x{SF&5q)7q8uVvr29P^&VzOjg{bQeWS1K8b9XPL%74PPu@p z-kxfRz6h{ugA}XCpg!E(1a=6IU)`^49{wGJI?n?-YWX`_)v&q@_>CY$KONM*?Th_i8IG3Lc>U6gp)2OztkE8KL>!mL}vI(Q#Q$Z|t*L<_@W8slpmh`LP@BH(VG zng{RgU}7&*~4bWd$1OuSWqe|`p|^co771J3muoV6 zAdE6tZK8^GnYz0QP5SAlZ}Lryc%Qa2kgg5Lj=`2sne~p)TlnbrS(xwTqZxMZeUV1|Hxm|FnfB;B}nR zf+ZXfTH%q$siT`7*;rij;%}Vl9bp`Way;aKqO4BrT^&Vcuw0~5X?ErmPkIFW9AKeS zxd>vWA~f7haVz;@_G1|g6-^o|e}A1+G`EKh)3=;0Nal6>bOnu1IE3EYGY4T5G^6wm z+40&q6Mgd31$(aAnV)uu@-ksplNc1!ArLxaR&Y8X5AO>)yY5vM-+I;%%Qu(|P zhL2o7|Em-3+bXHBD7lh*d`9O!-FPZR1+w&;n8Et3yxc3?;X2(U6vN^xZ*l>S3Yq!{R{=#L`Q;83@rT_P8ja#$!s)Tml9*TBscgM;f~-$EumrcR^?RaXq2WYn9^7UYbM$T#&o!_`?Qwb=c_V(yR2ow#z1q`EHpSQiiew% z8x}MGN5B_@RR-t=umfv?KqgA{rS**fdB>cqG%!OZc15f4`tf+ZU_~;T_RWK7jKQYNc`b?C?xLs;>8p4*!02Ja*nHb*Q3MT zn49@R5uQxWZp#)apRsj@5t;3zj{cy*N+gfY7JTrNdh+x}#BKr+1}NZ89g=m1vX~aa zL8`Hh5}QJtt$2?a&+TG8)E(FkVmx9l8`J0H3AcePlQ&?JYev8>WVMvQqR%H3=1#1*44J3pCB=`c0k;x-XW38#U9XQZrSb3XNlBXxG7$zso`~ZHr3^#=@gJkp~4B` z1ya8Vlv-;k^QE6;OI(Gw^47AhHVb?b^d^tQQc1*qaUQwyR2W*6Q}ET0#?%&7_G%GMhZrpzKJ0G)+R94sQ_`P-;|Q+NPX}dWk>s}N9cF@Hz3G zRqxUzOkx63k0(ZoyGJnYsu+e6D18~Wj0U&I#*9Ct@L7DGFOfi=-a9ODgl0IJD`4Dy zhT<4YV^m(Wj4z$)fFa-%4U{$pvJ(gL{)$TK6jByUnlg2) zhvS{}u)`^je86143!UpAvLM-inAex1X^!5T&TW&o5O4Rh3wjpXl{Arii9^GTUu9m{bd6|_Io_<7lSd><_^%DWp70h-E|pFb zi#*zljB>l*yei=Dx72vgF%19##G?1!4-DG1kOhM!((!WGh|$G?=5t zgL2UU-}jw1NJtZRWIIgea6@Vkn=<(3oQo`_-7sr54&DiIJChlI#?TQye{D8)=Sb2! zM-|uOtX_O8$}}Y^wb7OuFW=$l7M5s00mbQ*m%cN-U8r|f!!dH{(5Ut10R-5{)o+$m zlT9>%DD9lE!G)YQ4g7gM2`O2cEy@&}T08yh^dXFs?%sot_u(hk;mi;J8^+U&cf(%e zZQTtX51At`N#oQ`kKIaHK~qYTsG6=`L4(s?y>!!oX~lR1^~iUse!nZCy;pSGanO2^D0%z?a)5AD@|&QMw{B#H?1y9o@ggWjXcy&v zhsIv5--R=0)%TXB&1|R_B`=TfhHcx4(mFL;$_x`_Sj9;ezGRr{u661aqUWzJ0Seai zFZ4quRTxWM>C~L?^x?V|>hIW=6Hk+F29H*)m;-OjtlHrt_x>J`7FZD#7=r65H$7hr+VbQ-Ze~9BNVS@A6q4kLT==q8H*L!At?kPh_vt5BSa*zDO zR^{LNqgOeaB1!37UZk^Ds~@CZSU<&?CsC_nK7`my(^2UaLWjH4T;?5&V?pxURf2k9WB&y%1%TR_1!iR?@*e^_5Jea~r{s_*7Y;><&pl4+mg2T`0BXhclEksk$YGA-iY>*$?zMrWRI%u- z2e0%=9n9~ja^qMnUBLznwoY{@J_ang#Vtjs^#s_%e52uvJ?PkolOkiCceFLfLEN{$_wCv_O!Z!{d1_`1-P6Z~ zlqW}|BbNFcb)snk^qU16{f+~P6`0?KgY&uA9!*pC4&yN;45PZqX`1oci`PJLM~KZK zRa-8%fA>1vAkSw?TDago9aHDwKU1b8BgMEJr242gf5NC%hSxc*gix=RaA`=i5hTvr z$(!y6{*ar+#Z;dsYWn1CJ4%od^Za$Op2B2G2-AMRU0-SYd|M?|Shd{*ekxx&#P`m2 zXg^@^`FErJq4!V5nRaQsa-Z(|_BshbO%100JfmX2OeWJPg2TRN$zT@m{*nKLeBVt{ zwHdbVu&=6*h+4`nfBj_kVC7&QOVskz+oKq{D(}S>SwD={#71N#(e}zvLkMg;exAaX zXAawMRTB7CA3Q449@j1KXhcZ|@T|kxG^kRn@=1Ip z#ya2ePbM8{#uue4tmak)V{IJ1$l(skS4cr0y#3bR3?kVg6DFso`}B#`)T{DWD4Z7O zpxft#G3_>DrD>6}YQxVg__ufcogxJ@$8%r>Tn^loIq$Oym9KJ^xsydC=*h{Mq&^pz zz1Lr4Am9S%rz#kVpZJHHSU^UQ%;mNi>*SzsM514BWGH|tQ}GY$@P0j}J+VmaB-bLH zaz`FrrwcGL23^xv95LTc>o9CLUJI=C_EOF_)v5=$~a=|j9&MG{_k9(qi#L9?&g9kIK5qLz278ChIftqEq% zEy|3amyV>pJyu`3$oTJ32d28T7}Mhvj8YStupk%FiJ1=!hkm&3e7svA>ep*I-otT0 zKyQrFi@s>N_kU~6ZROVQ@L|08DD&F80x}#95Gj20e)X<N{X3yC2IuQ`BpnSnnzsPa1p-MZsv}RNjosshD3;X5FRoht zDm~ul-?B&?@BK`5e1a7`y$Ag06uF% zM;K_wN5mrYkHd;Poa`L6`)k=}lCGcbQKfLOR=X#nCy7KsWFh4Brx@k%=OeHWT0v%H z+|99mqj$mh*XGe7f#B@y?0rJ54Hez|H54KX=~F-qJh*nmUt%KmFE%9LM-2SySpa{R zc;do8d(DAU@O9ol7F8g4v!R!52>$OD{YNVJC2?-Y{W$peytv)B-yM_t z%gh8ipI(k(PB?V=1w~okZmTL#D87gE5wLc!>C^T)?KN|;Bese4M~FAb2XA={X;#eGG8u2eOu_B!8i z>T6_{$)m%*1I^+OTTxI&427FJ*uT&d%xl45ctODYRLJLAXZ4e7BaIG!6WID-xYbL= znyVQWXsAu@Vl9&SF}%#@Bc-+30)-+zCs6QHO}b8j{i+m`gi(GaNOs?}rBtDo+stm-U z3fjYcgMylC_Zv?WhHyj%H!FCY()P+wTp!=JRTp+!^()=}EtM`{Rm2;Tmt`YF>-igI zd0B&GiKe|PTHD=xz6E)YR$Dj)GayHdQ(1M7P8;sY0O@Z&wvG}?j1%Z=W7%5k1lst3kkEJ@Y76cFuG|7OXU;0Qk@WgK~eWZVhjj4+vubQ&3H;Eid7 zr>0u+@d_K}nd$=1ic35Ucnkq~ZVzNP{9~NA{eTvlQ4}6}hux3W`|C0C`x8dkHPU*8 zBLdZq&^FJTZ{4Ra)oNgR&Q%6aCXJAlPDvwp5FriY(SV?X)ddi&;iUa zm>SicBtkfe%N@VnBz)RfMMnpl7{vYzYq6^SvgU~BcA3@6{ZJRm|1L8iqL3Rs4*0PF zriwH{Vth38=qY1UYoni2O_1Ytid_>JXFp!@{&JJRj(@fuT|)Q0@vuXgt&v&k$0TMk zUR`Oqk4pdiw3E48<#^1YfAgD|zI3YLd1(rMTq4zsx8u)~1um|HYk-U>X(Vnc4LX*H z$57Vy%cL+S>cW)JVEa(}UHKQ>gNnJ`F!aiG5FdjwoZjS9&9c|J!QqcA2Y`jrqE(N= zAZAmp$M9EcJ161sPAr>^DV7zIkM6?iR@F0p502x2Mh|_MyIuM ze}UM>S#4R37F$BC3yBCKzrGydB7uQSQfThwtY;nA`rB8SZ129N?RIt#nne3?s9Dr*fT!>;quXf*ez>liRSkU`W`%`pcBXo>0 zd*FvyyJ&mX1DQnOgBhSnOB(Y_!pByacFwf3r_dz%2riFN|SF3*{V*?WFCts(+u-7 z0^~^BgS1a~cqv;cC!}Y~ZfD7jv%d~^rbO>y#BIvYrBzC@nveT3h^ZI$!z}oCPMB1F zW+Oi*E%h?jh6BbCQ&DPD=L|$o{0VT=4ZN|vX37XGw1f7vpq0P+db=B}{Zzo)k$BsQ zJ{9irl&HCz-B@MNw;jV1!=Yz-85Szaj`i=e{*PvQ20#>Ans&?eN0f3LQcQI#5X&Gi zDbQY?&46Ejn(x=RAL6Ue{+dt~D-bKKS=b;NNOLP2#CmyD3$v-Q3AgP^wR1eGs z8f9c2=P@K;1#q05yAH}cOv5#@nRj2zcaPD3{-=YnzjYymYeAoBeda{HBgSb7J{gdhr zD&|0=D3%0G(U@k>Ds)f`uf>q^!8aA<|C%&D^f0+J$-`OX_*VuA)3N7jO@?u}`}!#@ zd*d0yFpjgq@w1MRr;Df5nfjy5-a}-0vhV0LAmrOl(6qJra8d0NaSN)HYA?u2iWazO z#Nx>6K;w4YiPtW@0Cjy?4BRR{bux`a9chUn?7`CCovQ$zt3*$yDQae+jy#VTaG>ZJ zVp)Y=CCj|M2Bu5axW@ls@2#TZTDGv!;1(c25~(A(~|D&ro`vg zq(qwNb2Wq=M<(0QWIpu%R0(7Xh)yf_@-!-vI;jTVQyJaWBqHx5I)`ETb%O7r>Hg2B zdDqQ$`?DZq6tPOTCYz)8_Whn@R&fmadOPcnhi$YDVS~n1gM~9zbG0+5aHuKp*S38G znatAEgc`Mts2zM8YZG35@$(vx4Xdm?cGLo}-Uw(Juq(d&&0io9FuIttnx2_V-V~_O zL&-TfU_W+YcO!0fANKp4iq3t>aPL12(jhLC`a3jYy-A=7&KAQxIsCd=G{c|8Xpm@* zF=cguI$F%W^Q4eHA@aJ&X!>D27B|O3+s05Fi1YBw8MZERuJ08Rl!`7-IszO{-}Et3I{`NBYWa#vt2p>v8O-F0#($V&>BYbqrOI zFv3QbQrgGdmt@#zuJIh z=5S=U;c{H*+^^8~_^ZUg3E$-d5hU@p*jK@ErOY55!>YkRl7>Wgvr2azFX|HyKlZI0 zL3Pu;Ms{~~OhY#&fgrv^Q_pR6mVk-+{UYg=z7~s|J!4|m`345l0cEIErWfroA*XD) zs8Gd&!UZi6u{TvEg1~&g+S>5fLg;`jv1v*YsGblfb6BqO{fv_=Zs*SeNT?fzbSo~j zZ}RA&*Pp26;H=B1&z`2qJ){Pqhe5r~Ym9cUgFS+hfl2ia_az z5KR=k#vP-ps{2&Y*ExZ|f@~h1&`J+9j%^%UZ8n`H9HO!66QJN?&@L?L9EyRcwOskM ztev3HpicDIaEqD%I(l8uYLYZAK%lwvT%&lup~Aj1zHNlxYH6;!!=T$OGp7bs7Lgqc-UZM4n#%ucf*CSuLlb2K(o`WxP{$j~a~h?^$L?u*_K@N{_R z97IqXDKdMcy!-6OrA|smsaBbfecr6r%P@!r=s3HRn*Q>5O5YdDYfGB4wS{VXX->$u zzZM$BL?>7~!W%(W933)=uvmvn`m~+Q=oZMIOMflcQbZp=y$~%!tCz|%5N^G<|0w}u z43o2x93f68^c>>?`Y+SR7u`CNsO<*7fe68yC1V<#%7U1BG)pX;$OjH}nc=|%qZRHv zis)uY%75!73MdPaF7r?|mzmok%|)xpWNTBw2*s(B%HY6uF+M^R{4PEx+C09cYFBtU zjO=?R^tbV{nvG1#r^P&5+rg-3huDLfNw#QoVe-xgCQ2!3#Lc&aR;piVKVTE+p-$P>JpfGw z@1!y_U;Ac09k9&W#jOlD_KACaEH{+c@y*FH|D+a{vO@vEYB(v0c?op4n-T9v>^N$KY%7gi-*G*JK=>JvGDfkooD7vox8s9(N<}c}BmmSMdNuBJ! zDq2Hej7O3SIQSP22!Yro0DjMpr@3X4|5Z^32Kd8L-^~2xr2T3l!0T5?-Kva!>~AXa z>(-Y7{9!yqCI22e$~RCj;7GowTlYejF<-i~2D5Nd-U;PG)2<9TZ*!@)go8bFJ zs{U^P{!bWyRUD{;u7Ak`p(tWqeSN$$3^a64-+j3vI6yO@z7EjpRQ;P&GElA_9)_H4 zP^>0d&9`^^g^w}x^(}#k+Jt)XuFZ_!RO#6D-#2Vw9>;a}BbFx^B{v;4efzDouL$2nOY~Zwjmx6>-0tItWRp3(yrAIv2q~W; zS`K4o`=BYuD3^Z*kkS#@oH-D-vj~;kKE0~59$|KsH%+In5we1*A zX=F4Rhjt;{*};5;`SrQ*(N&@(6a@?$V;O_C@`UKn+M6q{Ehz)a>ewp35>4+;y= zqOVRum+u;%EB+>WlmcFLBw!>wUNqCC`sd+aD-^-t*Xqtp>X-V9o&FEQz8(e!wmREV zr<~tp9Za&){)%Z1wj^ixcY@6TZZH6^4>xbe{(FKd7YE}hZ9_uS+kfN%46TI6zk*u1 zZxYoc{{^&N#NclK-^>61x#vC27VDM4LZ<8HWdR2{W_E6{=z;~E2d^C#{W!lpk$C*= ztOYljxWWK>7%e4fxgOPy;04Pi3|9EI>aMc!v$~mFw(N&B9fL&_us4t~aTEUi%=xo# z3#v1bic~}9S0z+O=dLt72>Gd7eQO$bePPW5Em%jV4rjuD) zcmV%_umAml>U+d48!u#+N3<0ThGdNkYdCk^HC&n)lvG3()J+DPkN0Jcf=n6^XmanH z`t~x-(}K~vU|^eXZv;Jv%FJ!Mn3Wam1;?yc0gaC`s}IC^HgPKfj=U%xDG%Pm=p~-d z6`M`aZTB}vB0*G(qG}jyQMS~ecGh_J$%&Pv>svK`EIPVnG#o`8h zXtC5Jn4$r)J(w@Ipze)l3xng1Q=LM<`C#?KlPM(DeZjk+t>PDsxE)_R9PRnJ35!6z8BuMN1Ws9|iFX?^}WNvbBNNiow9AK;$HQVzukR zZ|be&=bOXt59Mi8t%O#YWz#(vju)F5ASNqmR`>V4_f^K129l}p5#J43lzQr}^vE>IXJ~%}$D?$$X!9>NxM6D59(}5360tshRMBp- zW1V&GC0JZqlD`X$vIZdRoW$z-wCH*h$!E30$3)zyTQC`k`*gg3Ft-(klbZup)QWna zp?c-3B~2#tHLwxZ{hv>&!5M*C(io8%uknTEC#mGKG+~t8&R3iOdQUs5b@e}EuLHwKdAWU(j>@z#WL8#)yjIHPp>oH2t$NRiay7?+u z#B6;xIxj=W8kg<|H_slWTL|^kgJ~q!M~H6l3o93+ptS1E(J>_skLL+e~Wuax=Hmk%HD;y7KTit>Pp-)rIId4C#QavD+P^DM@?`f`{8_gPs4qnDyt zV7HmSIzyS&rA-b;Sx zt~}&5^cEHs1?4q!i4P;HscnBTFxYx>H^`P+c(2Il@uA+4N}EqbFuU-%#Kr3Btwn1b zGL5o+%14)s$)>CdXqX&Z8xUto*_(G{Xa+w5UnWL^&KX%>H zk*&o6FKQF;iffUPq7gv>0a4=3?4N`xuybJMK3!t-mRXc{Dqvr*N`^+BX$o3<%B0I$ z_G9r5ts^7Rg zR^0^_^fLhLoO%YY4gCoCsoG&KKy_%%n|{V}iJ2tC#9LEr1bwy9t-vo^AaV6?Xm>SF zt_U92ba!@!4143x?_4m_3Hh#ET@dh4T_3dEhWC*dIoJ)jKwy z1qUD)LR&Ydg5!pnW>_^DPV?LvC6W|J=0Jl149=$Wz+E?t>>HW$g}JSDAgvY^L(6lC z;mp?#IfAZlZmIMH+|*HY!hFYLy30q84SS99pR*|R(9^W*$^20$$+4yw@)PNgbR&;- zbb-_O{BccOjORPvRHA~?1NslsUq{swu0X{789qRHLVTqdoBhROBW&Jh+)C`HT@jU8 z$g3al<-CXl=qOv9+p0?#n0$Di~c}yo%wG2!?iYbg3wKNNd_Dd z99^0_5*L7Brug=3Y4C2m+Q6YXZa`0I29m;Wx^m9-|VImCcM zRx)e*sA_Sd!e{|KU%e&9ZRHLLxS5}~6492-Srv}La?)Ze7MAohq-RO&#`o=HT)?MU zz?}#JozTk;y${{Ua&~rad+N?PIPY+%J7CiVUoYCdvo(sKr6^*5_lTFZP_p%dl?0%= z3>k-2gFYXf^*;J$5Ond>lGblf(6ppk~P+yZcR}+|*}rhb8mw zAoE?p&#rvN9513(u??Y)Z`b<(EmGofwiReMW6uw1(?1K4L~;{gra7u)9%k+GW<@SY z_b<@4>-yT~KN^9^+p{PF)xP!H#rj>C-CO!5bKo+hq@qS(64WlSy|FH{n)5j0>gN?o z-A8Ar^v}iv$f+3(x(-1T#t^APd26}eXoWoH+GZ`v@IH8u>$zT~y#BB()zI=(&>WA= zKNyXv*<6Sy$$fX%I5KGOp~6Q9JrsYL&#R}Yx5}hbXbh!M>)sMWt-(`lKGTHn^9DXs zz9}8Z^CDPRM?(p_FCe+}VA^k0psl{=sX24pR2w+yboefiyHT)7pHmIiafgHA5D6Y} zygc@siuI3K2R&7NE{8xD!^5)F^Znq^qXp)BDbE%mQG2AgPrVyk(ftaRsCC))p(A>A zgscZV6el+yh-7g2zbvbvvO4OlL?&$=?4|Vw58=+ra_N@imZA88sVCKu@yrKD52-Hog@cMTXM0ZtZ(JzXTK2}6 z-(1>!V$jy^vJ9O`n1YuJ{;?g92urbCqrcnLf`#&sDyZNmsHUr`GX;FhSkdmuD^#R+uZdFS*Ut?>~Idhg0HQEgN7Jq2aC z2!L^Wb-Fv>NUr8+2F4@N%V%I`YK_Um6#Q;H3?wG$_da%9UnD2FVIdul zrd?C5MLOQ~dFp1G{9z~N#GTdB;QQIU3wI8IhYq>hl3x}Lh zjliAJvyB(iH$bAG*E5sD8iDhmaF+Q8ekV4Kz%tdxpCv3${%%wTRdiYohce($Qq|HW z;?CAv(R<_+%CZ3%(}b|S(J>4rn??3e$u_(2M)=XCu$EHGabN5*~n`&x!%p zZK|4FJt0%uu_V*Z6N9RlE9KJ7s+G2U!!ZCXxNLzF3Yu7c($u)4g zl#j-0pCH!=kJFw>qY&L=ZtY>5DgEx?8BnIXa9gTB!P@ zdorsPmd0aotuOo>I{dU*Xywj!aWQSVNR;A&60Jdft8VfmOw@yA7iq4+wiFx@(WS<8 z^>8I|c9H}-&o&K$XlZ)Glb0gZr2rNnBwE+ zZyZQT5A)AmeX>~J> zcbvBR#SXGHuW?J+rVifH#IMANZ44V&uBuHKgMwns1{Z}6(sOKEqdek<%iN7`^Oo5Z zDNAdAwA?8A^{Db3=V%k@o{vYjFadOn2+yfvEabA$_6x9Fp1m9HEvhh23LFBSJe6A= z^C_OHxt9ztD?|2z9LYa}pj(-LfxOwl-l+9bw|M;WJ-Bzt?R=rOhJmL!k>E#%u4 z_O1tHPU<@#kpi*d6OUaUJ50sq1_z0Rfp?q!Y3~~%k~DrvXd5r&%yW;NSTid?==;gd ze8K3u>dvOn$@Y3XEax`o=80ECWL5QTP`T1Y6=g)2`_ZlMQqb6tVwg_A1iS>Y-) zzzIAFhWkhiS18(IXBq?F0@niu>a>vfPHG zcIiqzmuyCsih?;iZRHhGv10ISCs){HWd%c6Gb4k60tp0{FWN2fJW~4yKUuWdDTd;+ zY^{zXKHzI5G7YCTQZm#&h&Pk?gj`wG%g+8Vg!oYQIy1yst~M@#OKg2=2RDI@;{}6J zAR!;A(5jnyTYcwA?Sa?AJ*LWWP|qi2_V#d=z=~@FRj*d#b!$9!4kAMrapXWwQ+5uu zW14hWOy0h6qvqJ6M=12=IcRj)t6(s&;f-jYQFX|4$srBu$`*+mx$uT+eeUH9zZq+t z2+`TZown+HlUuF!=)<_=&rHqgfu13(g>AZfCB(Rvm3a%kP7-3n=kSfqW##P-a?tKJ zDpobl;toYA%V}(QOF$e9jltx=3E2uVe3S=UH>k>KjO%vYcn!s0WY?7+1c{ly;t^jh ze}Ax8Ld>OCq#o&Q!|^>eq?0J&o|T+UkgNF(A*(W@`&}0NoC~S0*g zzPBDS7ZkL8c>5Pl)KD+Z$T_2Ia0L=(L*~zX8BPr=!a`EJVt%H^~HTuoUUSY|GJCnI{h$}O{ z<`Po#rdG3+J)Za|XNSV}oK5XxA-6J8dz`CP(2nvT#J{`8^ z;CI`^6>tQE!6L=Smm$eA_v@#0pDK&3`=*X+9O>>mjULxCA1&bQj)Ue_<2eFcq%rE9 z)WkjCxRGPbV>O?ujlUxZ$O~oEzwemv@KyQ;PhNoK(V}J5h`Lxxj5ai#%`np3TIouz zm3yHj=>>M{Zvs{~3tg7r>E9)1FFV+*;iWsVoe;S4_e-jC;;L5{qL=|7r zmry0Q)3ByVD8WenI;`=QFO{YDt$6wQCtDu_mwQ+T<2B6mIf-*_n&AS*qPq#mNBu&8y8; zTPG-0+3bsKin&D&-;x$+V`wE#kLA^?7*S@cvZumpCPvfHz7&t2jbz&T)Uim+OST>< zYL04G3)zx66&0JkyA9Em);Nu2>{8ZC;Nw9~`&y2Qax z<<+^ew=3KJ0;P##Yj~B_Hbo_{yDtwX1a825B`^}3m@xAwz*j9dhAxWU9d3K1^u({x za6P`|HerjZZAD5nAM#x0h0v)XHSKjOI+@Zv^gv4%LP3$RQT)Sv)Ru=-d8Whu-TL!5 zA$R?S1is1u?9JYn|8>TWjJfGl}9G zg)cf2Z+401<#)Ux5%(`dxL8`nIr^63Q!5eM_1T~#&Zm89C*|$?y4ibEVOa-sSmhsb z;V~4?Kp9Q`BZ}bXz{oQK=x;A%<0qgH7um5E)lwFTX<>rq*_Yks>dz%Id}a1RoGwaq z-SVqNz7WgWID=i1Qg%VTZVodM8#s^9ZgzRs3=+L41R(HvIob0^u|?)4?j2W;1(7Bb zJc-=Dri#^lXeN~eTk~Nyz;{>9kLTDm8#$(a%M7{EQD;EahT<VysyhzF0=_`v8RnQbh44JPM3eaB$eg^lRfTRBn+Ynr+m1=BC{u7m>ZoGInErph({Ju^`%$Mo52JIMia*z zf}>b+xkvHL#kEjz)Ql150kb>UPcAEef z_*prAUSeU8b#lS&@(i0R*3&|V>=ZmQN1v55Rg!MTrN4~8NR>nQ$e8EZjssGetSQ0ABA^GA~YUMSjjP7liw#NuyI z(VG6sP9M0C^`9}I2a^D(z=<6tT?PkrqQ3q+1By+hl*RTA;a&%|=ZFf!uB zya#p(A@)ARp~SVynEt8tq+FjC5sMp=S2(Bb4=N)X^zRPR2f?Qd6w6$XJQcLHDSoN# zn7uJP#Dr#i)Ge;y@SvOI7YRLf3X6{wBbHd6cF|9n%aJE4^<>9gF1RIjEmeWLQ;g z|H$6oVg?mfYUyPvimuR|4UGyd*5*l%e*hmx!R4Yxr{|#O>@m63;Hkb@%U-;2CQ1u2 z<>HL}*VL+w?s>PBV1U{+t+0+c*6^bRiCF?mFS?x3Y2!lnG`h} zZ}*5GvBoUVWwV>+_$6hN$Iy;nk7tz=+Jo4m#E4DWAjLl?>bi2V7%=cLG*Yj|bt zn!Y$Cv0)x+IN!<#nx^H7UYA+ABvUggj9Y!N+pg>9WZ}yDk?s#;ayv@}c@Q02wt7E0 z7Cp5zF*7JwRpYXcfp_nQO_%aRw7)>+J9fcn!?-c6+#A)*0G9Ean@lpAjJ2drMl4qO ztV`Kx&N29QJ$cMwdM#?@9X*)-MqYm$m{Dqi6V;+_|C`jjcb|Fqdu4m29@-Vg8`4UA z;sr;-0ra=NfCc9dVY|s4&uL*3^*>$)qzcM3RjoEFVP6+DP3IYONjN%vZssyeyD|wU zEvz%RBr~^gcofDjA)nlswJc1Ygw@cY!?-J}xwd-gl2A)wzv&)~mu}@xSqO5716WQ| zEUQ_AgjX@@%zx~8(ro)`dg!b;i=Z6y5`vUbylA-4)dls1vEzepf>E&e^PT`Av7D(> zU%dl=(Lg*Rg!LNTO4S@OjZP1`2^n5c`!o4Q-i{oHlGlKgFiW-Rz|Jr#O>Rox>t3NH z)O|c;hw;v<<0y%h;g8-_?}_E;0FGG+4PTB~-ZR)${lvsKDz&tVKg7}>$u$#oE5f&V zcC9!3b~2p*F4}y7f_DLgRLmbkPk_u!AKP=K2y#^@CV)BLW^>Y0sGYEyLe4e;IZW|V z3oJ2{9L1riGbgbYSalHd--x32C(-z8%X`6@Ej3>rV)XA1{3LjTXr_D`dsfMUv-=XY zLXFHKq)Syj$jM+-b=b<`RQcU=b7&7DkTOg5kemiCTTU;4-2uMI>UHL+Dyt@>2Kty3UY3fr`nf4HoNA%~E_p zC=Eg-TLXG~>&x2VkD+Wa_9(6!@&3JLyh;qk&t9{+hfSIPa^X(N~ljDP^zRhQCxtvqH`6tic_R z-C4g2`4WCbmZD^E6x=+OQaPo?Yc%uY&RD+iYpcR}WCteekB?W3V{sOUH^B)kY+J`1T3?0mTWQQ(_?3vWkaiz|*B~^#XkHk*-zl z!Gd3+#kZ3#G=hETmr(KoGVgX@)6swel<4s!R`=PMayh@RcEe;r@23TzU<|=YFpQO3 z&GXT{o!R%2`HMzkJ`L$@*(DiZU&9)X0wIo`w%Xa&j8mbujWi_Sh>Uh1s)xx4=kqKM z9) z5#^#8ZRMRVxp&&cnS~p!ABFvKxh*~4Dh2~ovoAll@k}rC&g*p&?|9H>Ue z#i*6OP%-C=#)jtJu4;gd=3XPl1)7IWo%RaAUwSM+cyTunF30gpmss!po5-_3v15nD z&GVZ}Z3iGyvydnc#b@IsaPBPzjm-FRZE!M?@sDm9!T#KhbXpHIW-C5SeEZ96a}8nB z?vxs}YWe99H5EzeJk$2Qa+~Gef%+~eM@V7^Z@m`)4PFL9yv_1*)Kbd=5>)tc*O&0U zHWlj+wlV}j99G4&G}RZc#?6Z}sH36%`8Aaks5BP6<#{h#+I)oqhW!fREPq!@ze(?8 zO--g7sf{(C9K@c3#Go)iSw^_G(|eNi^l34gcZ;Hb*iY!l5Ut z_xHqdG_NF0d##hqpv%M^N6I5{&X?uxtHuOa3&hgu)8i7*6qwFB1yp!M_&yL6moR@E z&jY0M*q06+bybfifK(vU+%V~sJgVuKj`lx>-6@NH1W7)!_1La0;~!jX-Xt>Uw2 zs-He5$IIANN8z#+uVftPqm0I`l@fV%7WCJK$z4`2v&+MlZ;Av8l^~PrcIMQtp^D(8 zyXZE$${Y_<8eGlP4nH|{>e;1&^{|Z#%QwB7*!CsLpX@DjteQF;A+ z9qYFf3uEqtj|0*eMyVE;5j>^g~^bO(K`6$Oy z606@;h9%!kRi`RuyLn(2 z+Dh9ch&Hma@RAu2o#{8wFAafL)9ycmX1WsZ*!Oq|C>diV?*o$1rYE#gde04H3rkCZ z4PK3y2yeV?7RV*0?Za6wxu|db#GZ^2ODMj=m`ch`F!E!a@X@ji+W{c4E*p!h!p-Ml zf>)<5xOe0~WiC31;DSUD@0nvYH-BNuaMD$>(5Q4`OcBA6Ftf8w|5m3wGG&)o4Rj8H3 zHIfQ;GeyZ(ON_z>7DJu8=Pc0)!E2R&UtUSxo{a;GLm#C*l(dCXp?}$jzh8B-o&(7T zMe)(UX8+%>5?n~-?UE#^_8-4L{_EcJ0Q%=({csSc#J@|3uQm_~7#%m%zcq*dR~z6$ z|7+%do%a7q(p+^$29m-4t%l6%VLd#j&1Fs5nCLGu7G)v49w&tCG=ds zq;n%NJnO$W2CXBt?Mt+&Kw{F1_7kaWo^M@aSXoiqme?5Z{iz;(-yn50SGV9Jo8g9~ z{3@K78yw@rAn7sEnRb|{jY++V$gKBlLT>LJB|dwK3qdxZk^iMvUPZ(a6nVfyPONB^ zsY+dRMre+&VeF6yi-gd$WDj8hf8_$u()PaEf;C}JlK3^nfG>6uFKGKd_6?)vwc)_H zdfelv@^;dQ^f*q%zcsc`5)wi8GOAzlxoAPua{%uI8oka7K7FMxl~;IY5S-)*N{5Xt z_77EEB`1A@i%W-q4!}somfI6025X;agQocbom`t#HK&yoL-pVoivwuoHS`~?lSbq~ zKR!JI&|Tu#yket0tsT?YArKY?pwWHl@!3bWz9{{p(QR2^C3kwufJE^(l>B)!@&?>N z*d?_8d57O1fJYJxC1Ph4)_--=UnNgpLr~kMt_>*uQ~iI6fg7n1PI$1G<3AUC`B?F} zUyn-G^7N~c;Xdlc;I)jFgTzfsnuN5ud5~-Be3{Y1hdoZ~qCrYV*l6%(tir_M zk#(jWl1Hjd49IkPb#-P=Mw{{*_7N%}81|1tBLSc2Tu|{MS#fJYBv;20_8nUGhq1fy zSd~YIcTyGK4=e-2N1EzNzYgof3>A#(G~h)R>D+$5A9S)9p|?oei2}xmq8eT*-Qa?z}$y?ohHAo)ReNVITRYb~v+w0uahqgYY0uh9) z#IUaS+~{7I%mWbp;J$i{o4aIA`5UaZ#UdCs7#)MpO000dkTtoU>|8(Dp^$ocycwX| zTxLN~e_zSMY&aTbZ0g5tOQcJFg&4IW3I|G(dCa!vV=$SRl-mx0k9)R)=}+&EJYS}y zPfif*W6s9n=Oej`eVbcoJP5GGtX%&4n0~(P*_|)^Y%c+yO)8$=&#v|ev0Sgm2%>t+|>Q4PyR-$ zeG=!>g)&>$3j+z~Ey>r><#CS0J?M&QT(bP766i; zma$54ZGZIDW~ydwQs?@{hS7Pg?Pi`6XzS50IEq=!-sJWhzEl1j#`=MQt<%HvjtJ9D zE&tYPhJj*D@&0y{dY5DLL(! zbmQD2Wn!%&IX${0X3)?SbK`5N9TCpwxCPH=oB0$gzLizoG780VF*xlQ=?F(tNg;ez zI0MdK$MneY5Qg(Qmfqev28YwL9p{zAfpcR=mPdO;v=abg@QCIrs#1!Qe-qC(H^>Cj zghuezyJ?F>A~{~w-m1DUbBuR8D>C%8Edb!NsJ>J7@)DD)fSQ5PF8iU>T#Z3m3?dzI zuS&aC$ut~I@uO819s%i{WjZRml16-RdzltqW*%Ov&(O*(Z0AC`~Swz+IPC26qZ5k>%X~Vyeb`v{HFe zHW%Kc=fSN;9Y;fz#qXpxlJWEP{h?WP;?L=rcyj3{iI%(XUMwc+dOxoBNE@pTWqE%T z;N}h?+8@rEV6)g%Jf2Pv=NAnWx3-qLa(ph@9Ka^l3w;!yuQtuyY0~I1iaAOB?A*IN?$YOI%kV|s?++5>Pcvw>hb$t001q;;?TfYA z74;8wt?EpXpNdRw+mD6lj}I%MHv5M#d*jmQy%M9US27C#d|J*c=edUNAshSsIEIdh_M_!oEHBFk&mxWRP-k=_fez?! zBtmMC2DZT4*r0bVKc^&~TRs4Dys7P$=9?*5tx8G1f5(v*2K1cviL1b3O6;(WfF?}! z^R+Xoq;VJ2&UZj$X<@0RW`5h;CGCpjuU`cC4j&t|o3QA=o1!%p-dp4ji>aKsRXagZ z;j2!X)_~Ghp5K$i7h`F1NO&EMF$w58cNmXeU0p@>?PlMFoV2VnxS;kT#R%jUk$Jjc zy+7D9c#w#OMNJg+>=cs|Pf+YO8rbDKpT9t!tVpucsjR6k`W<}R@F5fSjXA}Z5Qq$k zPx~9_xToefXzFeKurNDzaQLGJn@8^qKWp@Tz$2|yp^i46Q3{`m{ix^0P}a~q|0tnR zWP6J8fsBBjo;BU5yfk8d9A?d)#PRq^ ziOqM>eQ81b&PM;bf$&=B4(I&NuF|5k0kA$B+i=8>Oz-8p0Zy0zj>1o7Zsp%SZvY>A zCF`#92rOEvlX=#vol;+Fd*vi=99r;BM)x3XM$eqImF}fCrrNU89s&LoIM+%atziqC zuh3a^L4wD~N#AOu9ue9UKaK4OIZ z=$#yTeemoWE4d|=;}loMCa7_4UqOBgHyZ;xm`4-AU;36NS?@jVvlcj;42cSLT;x1g z?;!?22b{8~PPm{$t+>L1PpA21Dux*5!pwc?zU2~#=Jd>nY8h0!qph*hgRay z{@dabp!k-gzvQ*K1fooAkWr}7>xbn#$87WY8m;3ZP2J-X1(ie|1-XVx950>iCWR=6 zOePbyeSU~%iHo68UE%Yh0*kQV;K~Z|DakETO2)Rwo@b_<;DwttP`uzte?}*TVI;L5 zI3^r@@PJ0CApZ5y5~oH>;nS=Ki!L=#8he;eHR*h7MB)7V0in~q*XF|~B$op~gJ%EY zi3z0qZQ~V=WnA6viy-&iMH;|HoX*y&+Ew-b^#_UzU`dR=TZ4EDPd(4l+qgg0U;bm? zk**R!O3VlQQlY_v#>^ls3sNLiub#{zgwHOU!G0NUd? zH>x`re%iNCo=tjxMGvGKLdOBpAQ84&sn(l6Xv*o`Ux?G=aJnxl?-8W(6G2aFyvB=g zF;%T5;CFdG=~;)P891tqRSPzVq4rjZss6&b-IM=-Um&Mf<4b5aUj8|k_ z*0iLWA)Tp1vlnyz^Gh6qoa~)QEOK_tz8BreCxDq$Q=;#(f*BOCn9v+H@0r$Ixl5~^ z=~73W#7lGSHNFja?@c}42O^a!K4=Utm}4sd0}`DoM`z~E-N9nLC{CZzE#`!gpC<2U zs)wEB^5+zg{rw>)2Ja}PS>SQR#|Hc5Hnix_tCJ{L@BYH~k&J-ANBe859-AK+w^Af7 z!- zP8yiam!<8|P`l6sDgK=V&kIKOX6W#M3w1Tqxnsye6hVxin29>u< zUax5D1HJ@urk*)lt+#hvbE`H`qm7>-sd)Z<3<7SpRI9egcgHkkkGv(Sbu(HlY?8RNBrC(ZKZEvdHrD49q0+VM93}3- zy(YwEW#ZY;ij2*y@@tMqWapDZ# z@1rH?7?9;vt{EB9@k|SY)jp>vz`xEA`-MwEc2(!OWaWs@#yr1 z0^`1)!1cRLnFl4pZpLs~X~*>wz)8)m>lWMwNhe~EWVHszS;5WM)NArx)XT1(mP>Js zzM(Vfu)#q}IuK)`_{$X#5XES_FFs&Po1BG*VQ%;q=YWL%522^j+n|Sebt1IBLiY4} zhQCPaM>>AlZCjK<5of}egoyzro}Api40NeT+vn6ZWNT_hR*~Tfhj-8S=iw;nw_ zZLQFqRaodXO-{d}l3VWOw5dImX~+28iS1Ja7pi#s5~}8OCvVkG@W*bXe?&oG8a-V( zwfa$m`>!7Ui!iGb0sFUFvyN~6`wv4puz#xo4kP-tuJQZjsr8E~^9zNG{QXz({jWxt zfd6acf8F)By!>A-U9L-9LH|~hHahXPk&)hHfVTD{o#Qpn);;E$| zr z9>7D~i6EZh6XV8(Du=gm)!-sk)aXr6J(V19pih6qA^bJ?S{@hy$2G?&9M z?Z=PbE3DV7&gZ_S1=g~6?;e~+E!oJd>o|gRY*HD2(~!0)c!9`+87t4XwCVzywU)&L zNx&kFN>TUcI|?er>@rgA;#bEMUebhZIS5P;Ooe}8?Jqj6?mM`w%fnHaVu7;6ldIMj zgN@AIGaIW$zXgo(Mgc!*0;hwd4cUz{`QbNB-H;+YaTf6JGW3u81 z?K;&=!bc={>5<~}Z>-_L|ATV$ItcMp zW1I81tq}KqCd+j>;34C-Pb+tqG}rdte0^5?vW@9xFB5uLUM z6T}03oWlNg{t2bv+XTtmR>W*}PzIy!HK-8O!9cD-dMg3R{tAN94hG4v5khl-JnA=C zcT9W*BtK=*|Cc-Phj@G-6fhcwlGh;pZ7lyN2rd%KDTiVFFJby6E2|LTm*jaBEAo2- zzmUQ+58MqG|7+woPxk-w?(!_nqxt-6NU+5Sxwurr(q02+>!8oh2kLAz(El1jfqQsp zm|ID&$M$)G%1^QcoZG|V;4x#0A_oyeprU&c*8+O65nf;B67L8O(u^=fI0? z@rh4@W{Q8cGibyp|6U9-l{h^$R!9s*Zp^6upJ#QW2+!u(nIi>Mf7uwZml}VpaQ;2d zfN$y~JfAf!y*KnpBgmj9Suo$e?dqS_fJ}@1Q``j5y=G^zo&?!Jq{NSL@4%vO1Ou3s zfV4#S^k_*-D}exD}Qaq`^LEY8zC?dA)f@i^~6QS_YiY_O{>+vkfW?ZeJZ&{9g`)sz?~R+Vl>F zz(Cu6w^4=Dg=R7RCv7(VKMsXIzK6ODI6^#+r;4wG{r#VPecP`%=O$7YwSe_drEh+< ztt)(9K0c9gaUOLdwk`^*tG~BktS57Zh791-jV*jqB8c9YdcfeXU&G`2_jEiw$7NpC z+pqo|6f*^xMCuez5H-1n^{#91|@(Pj~Vv&2P{S z>bF)k2e9d*D-t`R4pIrYt0-jirP#JzXwy(-(d=!T9-nMa1i4T?Gzl>u_+U54^l%=e zPR24nYQua4`seQ26U?!IPTlO^C#k3R@xnG16IqG_$b-kr0Uu+tKR{A5*Zx*t?<3$4 zbH3n86H6d05Wu41jMIL(Rxua>vB0%-^99CIy&-Zd0W5mAPc28(o>93y zLc^4q1Yu*Bwe5{g5`m%mLyoj@w%vEH;Ey(b@J~7wMp_qJvwtBWr(KB2^NH8-Q6xOjAFj08#o-wN?ADl*8Ymq^&z1N+_fxa-vtic7 zs=f3k1W5HBUWr~M5>FeHJTZf>PF73n9k3YPSZ9nyiQ1v8qM{CT%LN5a5lGhd(2ZuX zN;~gj)*32$47&=ya3dG-mq`kZ8gCo*3j_!IXr}no6?>X#m3^rt+>5Hq@yrtP5&K5W zSa3^#PZWn_SO4gEpAr#)s=kjS-Gks&Af1444H=BhPOdYPNj1&%8l-3ACn6=HOXq=n z|4tVrvntqvNwB?AV-@~U@+}tIxu=%r} zqQZ$@K6B?~9|qE07F*2#+oMEz6Y9pQd&!k;bntdEwhYIlv!sQH710W8({;0;xou0Vfh^iqx0vONW<2+dzv5}8htN$JWv6-(soYpl@;GKGG# zGOwqbN?Bi<=J_Tl&()9%1RU7dWG1cP}JQ!8gbl2V*m*@9AX3WeJtRs?m!?%wzTDkohlHIy<5pA9S4W}ex!TfdhMEfJ9>(;fkhrc6> z8q9k=%*2r%UzE*E>9nvB5fI*}5jkeyjH|PI6&3!VS1j+}i8dz4?-i_QP+FI$%3~=m z4l<(58@4u5k|`A8;V~duq*ZD66wBw*+S#ORB&4w+6V8RPNqI@iqzq7pK<=_9T|T%S zX0#|~R}Q zDs?kyGUX5J1~Qq~)>&P!aj_RVCUeN#GL+Rt@{m3{d;F*W8iSd^VANI z#C_{Qw^|Q4sE=q+zmI75a!I#<@%)=Q-(4c-M+Xs%xB2p6E_vGc;7A|qwWx`Su!o`V zB-!?enRit_Hl>|nlG06WkoI!EKt5UTF>R8&V_&5ybj?B8^S!&|y9m#07DhhbGGNQ(PdCEGhu>$A~Am5=m@Mn2a$?Z8!O;R_AHCMxXM=P@Yy=%eMZIAhQvhP!@ofaDJcmp~X`>EKEs#+x2sD zf4Rw*;c5ewqUJccVREI+wRB0Mxi>Wo9oK8;*7MT4w@MH2h()X-D$6A2kZ21WABW(Lsj6!grs8Q znJK?M+;x4nyjR%4%_YwIR_2HxqJXlOp)nl6qidGW>X;sa!JG&%QkTP@HGz?r`2!p8 zbKbM%^m?zVozRs>U;6eAlN}kg^wwJT5mT`IE``2%@j8d&!`r=!uwZ1(c~FZ4%mK^e zv+NxDB}Kxzr^9hWrA(#-<~eIYF&dNjDdiSO19Q+arNHCkQ8JRuVQ~gLfr^A;p|nc~ z%x0t=7Ayc$t&DezaG$Wv_|`%+?wv9b5g)I9E?ZRbogb@@xK~i(kbHRep$ z?e#K&+gUfp#9w4t{%xp7L~q;o`1Q_MJ!Is~br9VP!q@q|SESM$8g0$ZT9I-M1!B1p zl+3vXQ9L|`(-t^8d2(9A#(pft#D3%e<)pdZu#2Df#sFd657=@_>`&wb6=5ax+@C=2 zsJi~Pq`bkCUd4E0H*xCVxv|y%XbjL$fiyXj8u)9FL|_wT+FGL>zrj@!`e{UQ=1Os3 zB~os3;itHS=uROsinlfsD;X3Mp!sSdLV#8-JR* zfS(>!4meAP9h{9CQBvWOH zpD&ZbjnH@nnS0Dw&Q%&}G4m)K3)_A9dQo>ZD1A*_1D3D-R7yPkw^|RHaa=ZRT|9lG z11ZZw#9N$qIEEP$*%<u`sie!w2e(JSQdPXN82o+9A<4E*>ZzS!rwf$qdD@{v-dShP-~fv z@%rlB+7c_z1~90aGWj(h`Zl9`YYn_zka+NQ@42?z%E|gBDTQ5nu^FR!_#7QhRp=+R zmcD!T%KPCkCWtoqNVivaFLB{FJP=jS;QZiXeeq45ED;)r**LfV2qN}Vi5wkFjd&ba zralaW=EBLMNdRFNY^xWZq>{1(2FLNNcVaJqBgD|PdJ>f0K25%T!=NB6+*4H1VcjyC z#M}mq4Y0LSP>M26)KN1YtJfZ4J8%gELsB_Nu%Q+^-*R`gok1QOy=E7Jrewpv{BS(1 z8jQo4Lf1nrFwqbRH#X4fBv5!b7T&W|M8HA?RI7~>*MGvtp$gGwAc8*U`f7C5e{Wfn zgWshBAOFH(-*N6`IQLZShP)D_rB!=3({YOXJDWX@kBO@xg49tAINuSW2AKg(W;ciFhOQqSixbhAnb?KzLkSrZ*Rm{5p8DP! zuv*bK(6Z$ahx1emno6=(Vk#fbb6amhgOz{jaFsCTtD=LDCHKb z_&NVerY16Ka1NcL*x6D^{(16MPH^n3VLM@N?=xPetg76yu?tY#pWNf_(rfD*8c7ug zkudFR+A$%lAVLSsHw{EPl83_0Iyz`(>o~*2haq4xLf(QSV6a=2Vd{}eA&SiFUGlIn zB8^XMfR0KHKBf0PsBE5LS3>YT+slN#2p9C(lq8^-wrtd@|%UhKtv&jlHa?3Rkfhl_WZ?iq0MR>m`lrB~>%QWJblXG-VCU+?E?+!}2gN zcApTHwmW7EXLj+u&Zg;v@RPN`cadku-F_a&4_-p6v$jkF9-yt489{D$b@M@eHLRNB z~ zrAUQcyLp8Tsjui6bG{?r6g&;T0I(b>G1);+Xfl6jYViTjwf3T&;|}ybW8!lBZ4ob! zr&dkkbM@%NMu>E&x7H|UIDt~+2jTb@xTKSO8MziZKP%oUI5Tylt48-HQ-i`{W1M}KU+gp zbML3y!#ypa$48=R9J_CzZ&yy|LkkJhcUA$VTGP6%*l}tLIY?e-P9mg__BmXKq)R29NC z-Z=`)VyS`^gOXMT+fq3{;>m$rfu9WA&;)7r#wj`6_|c=2>rj!=dpq6O7-r4{VuNzx zJTO>@EmOXYYRnxxh1SIIS1uqDzR#c!)ntfnHYjxKOvrYU)hz5@9z^|RNf2t^?tooR zho~XNfT8q&ThfbwIldSd$Osc%uEAg;5VEQKvcALn;b3w1V-VezGsaGLLQDvyDLR%4 ziI^5v966s^MS)QYOBYz3ek2kcaNMq=G?zrMK*bhkNmkLmUgJ0D@3z5gKR(Bg>n1TZ zR8^Iv^z^V!m%BVUA-WN$5(3Akv}ZP#4oR4#%rxM%*IryZhpfJbT}eD$dUf&M`GHsR zhMX^l%zbFtL6Cl;(R+IO|OWTyeM;Z)`AoPO@^w%O|_xh{8 zEwwr$lw8YcT%Jn&ejI9tPk_(jZlULa=thRI`;4E|wZzUfd6UATG_6ey-cf*HL@y&Q zF;prxX0-KG#0|uD!dz=-6R==KG=w}%$js;ABy1Raz>QaeZ*c&Qh$CPF!Gp3H^6=g+ z6}To(v`D>jtqkBDJ<-haQAs=Sa+v*Xsg5s|H-=5}bh|moV0D(-6-p!tWp&>54c?yE zp>q(lBk;XAgpS}*)56G1QrcGpMPDdJ=DoLw@Z6I>~>h~p4j#@UO zjTZwt$|==QFVdt2Cs7;0Y#F1dnYe=*Au6Nh_HE`6%HAn&DPN_6c-1BG1$qd zGp#oM;PzJFwA<;!iwS;0C1zu`*MuvVZxWH+Q&Es5HMc_RoF1dfknuE^lOC!X_I^dG zz7=O&LeLasaA_vl5Z3*)0tMKvm{oBnR=xT3k*M^e4Vm5xfc>&fp44V*_H+w~Ucq4U z0-e+49LAAcvWnQ`K`>4VB^Z|Ky**mZXNxLRJE;C;w<{5ndL#$>daqsCRu+%)!kgIw z#msO7*!5haTvX8njGf;>Qp;`GibWr=4zsIyZyFEp!o-@Qz+8P5xrgKOm@})hBW4wz zM8?+VDDeq)5VqmmO%UG@ay)IWEfD*uU%bjTe(+sq+9lNTwz4VlVOgJE z1n->tcfBhU{0a-nT%#dScM6XG8o%DAp40k#Y;Db_YdI_tZXmb!kXX-?+r#+Y|8BPzP|vJJbqj#{3l-wbWK&?|Is|OW92$p z^VWI&vXYpU|0KFU6d|vSY)*^oJ<|y;hj9+ft`rwwe;JN&0Y7BF2eM znC%Oi8ub%tpXcCvLi;=`{pEtk&pVk5JuRwbu3H~$P7#QUXNLoOu`rnOG)=O;@N}Fw zW%pUTD^Fk$OYFHIxUf`j_)=QJ@AhnX?uE8e3{m6cxxM8kS#$WDJz#X>d8 z>$9t~nQ9y-0H0nrVxj(&di_bDCFs9A1Jsd$^1TXokST}#lCn}8qe|~nE(EJHU$k<3^kA-bLE`D@b!nxpDN|9&i_eF><>yyXd`B~s+Z9t*?$p{UV6;Vn7X68&%KsIVl=~c(1 zc!y8CD{mLWGSZJPhigE#=EVAqP(g>c)GGh(Ez*NGg>hEb zmqNK)|14#b@XO;jEi>;Nu zckZVTKlS#w!$|Z+Uv-O6dr`~$hFc>FN&0{eTr|SMlgR`lY7!h;C z*Frc43#w}YsDFufW1*q(K=XyF{d?-kzq6))!TF4yY61yY`6|j-N{9KK`U5JV<|sjE z6n$^4E;{}A&i=dQz!$+8U(9-4G9>i#C-$ic^59K?FDeGZytF-eyIVtoUS(kHmeS-g z?8*gk?iLu^>on_7OqAkgX1Vr*6SIQfGqUe$_0*x8p}2Ic`=`zGtv|*XaigXOh*y4B za;G4uXp!a#8*z$*2n-|pa41${gQ{aE6g)rNA+Vc@IfJt5zAO`i!e44F0-j5>+i*j$ z2IIggXr-f9eR#jK1*YaETSg;J83@kiynL*?oT$R~sg6@-EKElAE=|B?d!%w3 zbXB0Abwfw98~Ae0YMpuSd+Kw|k1LH#u!h&_3(S80iA|zBP!ZagoaKKG@22ht(!vHn zK=h6sN97OU`@SDFe_(^s6z<7=%`Uw^yshBYtOx`M`f56dVIe*(2@a(&7b5I?3U;+A z!UYqv2*nPmIFbYC?yFL8tD4j2=7_~Pf;Wt|DeTQ_ynEa*TtG%>e7P4+9Ivu<#2xWQ zfgx~MvUhAk#QCSGTVXk`OFIZ0_e2?fxqu(^NOLDg&Prv<=J0o&z#D!w5+|k=DagDs ziOn7-c#SHC96hq5pwa?~>NB#6*vJGieU_n&peztwrbh%kWxK4AdWCa3lc&YR&p=Z3 z@y?mP#J2$KCI`?PCb-`Eb&9jLtgYQqrLy<9)DF#FD zDeRL_gH3&S3;l6mW_N1xW)}XpEV{Mwh*MhNxBSO#OCCt7e2(j>EhjM75ktz{X7swC zqeoa?2smrrYeRS-V0=jLS*?jS+b$b|nuq}-uo5m8=7$Rr=19R7T>9QBOz+=^vgD$K zVnI%@3s;n!^Sh^`!90dZGNJDg&1_ue<$h2<{>46rBmAc+zKxeG0JszxxjxYbMLqgv zd|DaCRc5)rm!#&~_=9OqVz_}{_RaWwPd%APv|bcGmBHCtvJK&Gm{%lCT7Q>iOiwML^kLvw+dqxto~S>b@(kDE-}{Lp)c9OZP5w7|%2 z+88zOtVDUpRTW}QG(cWsM8uH`SEwNp3L}UO4E7SCSBLX>=;HycS{VD5)jSg_C-CCiw6_qBCv^e+hLDwr0|~aP#krdl?c6qM z$!V{8<{l29voCbxsSq)h79AxuZZ-JkEhyEgjhzMpONI~^vC;M%bldPkNcYKHQKg>a zCq+yR-u!JT{VM}4%S>TyB9(;q%OxSvzACzj->MBOSdE6Sm^S>x)f3oo*GTee=&vwh z_RQjI$PK(X+3*c4n*MyGR|G#l;ea74c3tT4i%@-+O&dv!_+r&P^~~pdw$Y)3$+Dk# zZ?nBN;kAotp3}Q-gSJEO^X5^Y1Llfzyn_JiDQV${Z49fareeGo@1$^|=3=Sl`;HXB zRIV}_^xGLIE`$|+xGyM`>D)Tm$WBuw6ZP(il;h%dx`osv(R~Vxfm=H+2I@vW^ zA4Li?>AMP(fhctaFG@x;+SDk0szRJT2Th|GQ`l!qM_g)umKapL^{-;8t8Xlyi+pJ9 zy3SNDDA18~FPXI3;YQgPsWkMLlvGHiQRE6(t{@953Hjjgkx1U5OtVk%F9tVOE}cSC zYrP3g8DjMy;cln$-w$1z1%CCSLs`@LY+jO2GQ990k1z_5RV6|V1(XG;!V&AXx>d0* z6yn&|?~D{#mlVkn-i?e4hi8Uow!X?#HbJ#JLcN*Iy@#5AUE|zGhYe4D>NBy5n0E5L z7GJdISHxp0jESzG$|X$NU+J1UB2Tyh>jFrPsdJ@^6*>u!N47z2{Tm+=@QgFx>S#l(ziB^-N&;a4AcJc5lT zD>~n}+1nJ<fsjy zn^*62*egK5tDW#B?RjPwhjKdHZ(6CX9Qk(RXlyMJa+oKC4i3wLqT78r2*+a08^?1y zxa-#6;NI2Lp}csJCPq5zppDow$RwhdwDLS}J6C4whjvWSe4OuX)Q51e&z?YKzu=hS z?k`NS99ee;+8}`ZqK;KhNy530@1CnzC??W|hFw|h?ED>cEh>YcffJb}~kT8GmoccyFa*BwOS=$$_d18O!Fx zHlJ2Em(cR+YgG!+)l=g_ZmssIF-ot-(p0|+?knLj?3A>IZqH4Q(am1rcHV0A^1Ia- zL1uE8906!E{kj|Tu7wS{A#vT-TC>JN0?ZMjKMN4Fo%o>{nirhYQUJ+UPLEI69~^Dp~Z|%thqHA1%7D{Y)0i5$mV`GbwGJ&8#H)hSDjWqD~_1TRet@LtO z>e?FZVvPej=DnuR8lZFU^w4$aY@mZ^E%xp+Yp#5{qhe`ipl_;_Rhx%xi<5{EmWb9+ z_qgmxDBuMlFEiC08^i17xQr93(W_t;M1aEVPKJ^GS9K!$Sa9P+Z@tDx#3&>GMyHE8 ziJA;J7AyK$1Wj!sAFVo^gYQU@ta6QdNA*i434HSV?<2&0rM( z_3i6d+q1QPRV^&U>)Hsg`yWQqA8Dh-c`HY|()EgZP=Q{0EpqC7 zVnB;@l}_ongSJrjhlQ@^R#H~NF!?wo0uz2BF{-~NK^2QJccX79q>pDiY!a0QUjxO4 za8<41S4KZ^pw~nxxT$teh0;m!6TtI zKt2mLXs5!((?gKNrR5tOaE60prThF*ObwMSFs}|3pExBR_1iaHq#bdskHD`$fXBh{ zL$=GI>q46x=f(RI-Hrz)SIsNFVuiWeYuw#4HFa=6;(7I3B;~NFNcTgz`X_Y6JF4$# zQ6h|LiuafBzXeHo!MxAO2oX;N-_4VY_=7ufgd(IA`DQT>2n;W&Gr0Xr*Nkqc;CW}W zdsBluf@jsD#HH^p%Aa`-C#)apIn}{uW_KOJEA^GnK_+E%4%I*3AyZum)dpmSh{ruW zSZ8~x52y**H_L6!_}^XR_;AeN4;CxlDL%}~X5ht%gvYOGR$fiA2qk`L|6#r9FmJ`o zdI$XwLz84hQ8jQDfaB(WXt8;7VF#4V`GJJ$l6h&@_}rqvJIehm;(s6aE@Ol1*X_d&H?Z z(N3{=>hSEcYgR7FsizEIrttbbtA;1sI5Y%jBx5~>31nK7H;U_`msBp<*=FAPv{M&% zUXpl?aoLT3GEdW&8&PvFn-L1%3ejVrfcm%`_(=PW{qm+x&$OBYh<{^iS@DgcJes=u zhDIR^Uq6>!js>(I^*Vfi+sQW;5cl@@2VkM&HyMBOFi~)WWwy1O_j92tZsyAY9@o*x zy|NRF%}(ol>S8m~yU)**;S|mex3anyZ(H8lz7B*#K$TO?$?pZFKc3xId*61>kTbQ7 zHFXEG%DQh1?k#@baqDV#o_%t3n0vZ(95aAU`}?d`5&4|0CVhrZj9v3N3nJl)!omOj z;8*{vsMsgb7adysQbtk%;)MnlRM}ma6)xl1oS**tGhEReedOlOO8qX)(Y~ z(uk(o1W6Dfpi=+Uw7(ydRS2Oi?vLgl*#A`(tc)-?<0@oq^?!dFDB6aOySsFizR~`t zRj{`pVptKxAJl06lnQ%;Fu`hDaQJ#5{-mU7dBMuzwox*G~=Qu-g8&!PNgd z!v8Fx|6i7bs9_X;*}vB+HN6sBN~Idp&pDeG6&^eWN!ZnwCLlhh|CWY8Q51ANzboib zG^eoH4thY$ad&k!SmCEgm_9V=J@r2#5;Vp-xn@|KZr?g&-+m^!>@p!#X|M*8$`L{0 z(GMJ9;a!Oq`ctpBv}lU@s_%s&Bz>)YR`_;EaVZ!X_0t^O!i1i?-~tygc^pF88_ zpJq%Uv`QThGgmi|**A|Xh&;OLHaq6V#Hd%QT;K0%;WB8!D#3U{_#pV_7a#Qf>0!gC z^Az{Ts|6rqafZpo9(zqVrT5}-+lj$NaZbbDXBQ12I?+^cX?1$ff5Fp#VSsH#cjLlS zd1k;<3Z!91;excKvQmW+C?)J1oauAiqG4`sZ%S-k!`&KJ_`8uO3nPI@7vCA3{H zfse|{tInyRXkAkFL9Dt>pjBumbm`q7xg83*W1xCDh;zYf%@&&Z&baEnQ&lqbp8`1K z;f0R2Eky2H^qu;5wBvPQWN^w-9m}x-#}ZA?Pxs6F4A4>eA1Nz5^>8WL_U1~h5=KQx zn@P8VC|*Ur2PA*P8`^#lJ&y2APNwhqP?P1uj@tAILUa`w$^7VkFn3e zh0^fmyWeK_7Ex`3yESyN7rz&RZ4c_Mibg~(v+;=k&)hCPAXNL#*;MG1FP#&C#rHxa zbb)A8e86Nj!v$2w5l!r6P|Jh5_XT)ns|fX!-chx|GD{nRnP=x<3||6A<;mq8F+`xR7SC}O@NV`uU%`_ByhmwcOx8rj-<-`c38-UA(un^a1<5R0W# z{yk-|nIJ%ZdN{3%{6u!;rIPA%>))nRr~)j>wE80m{{?%ezmV+Py}?#)-9j!iCZ_cN zpv_;)JVy(@SFiSooZ>%JdTKHp%zSTq^q(aWGK;1%&u8b;{)47}$@vUE^{9MQOy(b4 zg1rTqAfvkU-?_N{M`cDjuw`adTTAfoRYt8;0b6ET0kN_FUS+6qvVk1DtUT`jsph{C zQI8UVC#L>mxPq+EaCeLP2NeEed_-$um6<&9@&89<9IWuYpoYaowEyTYgGL~SW`&59 z)PL+Lis&YJD?9$jr>WvO2HANvQQu?=uSqeDtNc1K#*zLM78Fage~p{cnaVZpj_MZL za#>j;`KM4L0;IzOhpY4&#bRz&yN@PAoR+$5F+(i_lEpE;y|;I^dTH@LBDJQQd4ny* zEz(bDjqNYAr~eF>C=P09&1S%l09&9n8AZ7}Bm(8`-gYY3i&Br!KqZk>1Vm`rpz#An z5o6-9(zR-Bpj18Rm$>((NRejwpz_T4@aeoTX4iPnd@M9hdv6OYyK1Q-z>a37-M@WT zfusJ5CRBl|Q>EpLkD%#qcc9he;LE{`fmOltBRjlZ^NBRtKU*=^=0Ne!l^XSik5B%( zB7&_w@iQBJn8nq#d^O$|JWouT?juMkriV=lb?`w{(1AX;yX*Ke^(^>D1b^ zj$7$a>r|TGK|!V+K>urd4WBF^sGaOSGuNXRgxNFA`p-;@dZ00g?sE&ix3-zbUXHr< zD#E(dY_*ARnZk^V1}ZwVF^>vkUk);sMRK|h3#WyZ-$c*xtr^%~kO=n6`1w)Bnnb9= zO*aMWWzb>65mHRV*?4%aq;pY-CvN!W)I}C2iB@ z3UPqKKbHE1t+l(X)}CsW1s<7YkIJ(b8KYNB?NgJ~9&On}TY$uSo`04q1#^ye%e$@y z75Jj`kc`fpo*?1!JJ0f04u!T!GMvrkEeyVKb7kR;)+_VsBs##5jKj1ZCi`&eqslFR z?UHZhN{gWA3y<5U8Fx(ALq&=MDu;B)B1@E8*-Jy>*FQA}l!{wts~vcnz*k%WB}#m^ zdb3uA(-zF(d-vIh6#3hx8aTCaz^qK#(N8|B{}QXk98px-CqO_Y(Z09EScaYG{ZEO__Ip9rQ^Wb)!rvlj_JNfY1K;@R$KvHSvySA50HVu?=^>$Qs zF)_&%I;FY+5#{fiinhk*(>Qo_r^!+N0g;@1HPn?#o0Os4%Hy5-E6F8P% zD-XLSm7oQx*T0IWY?St=rZGLxo$knYkHrI3e+(J3P_iFolxMeQ0(014(34jdm+L)nu6EcO_e!c4rvB8*Q2s zR_ARZL#`b$@6yf&2F>t++qd|@r%QZ3w@tfi4?USQp@#-NxTn3?+J^P*Cg%3ZJ$&T< z2reAL%4=NnsyfH~cZJ$zSYri+PQ`c48d}x{l)w@Uin6zAyuGu~HSG>35UCco-0s=rl8O_#Te{nxQjGzBeGZ!1WWM#@F= z7Oo(b08xqDwO!4{B*OYD4RXsVqtA?-R_f#Qv39+hh&{z&z3l6b*ZSl@)UUu!?fOaW zGF6*Qmk;H7ZMz>#jJH4y0Ftmr7swp+{}yai*Yv>PjNgcPo{YloAp@SydzrnowV1ITTNzC-7U?@rvtL zTzhbnmEdUL{`0;q!q7)4)quL z;N|kNH~*E;J>)ys#=T$OG}mq}0G&pdo74S69!@9e>*VSMoCU}* zXLxU|!F}!(j|Ycs*=I|&bH>VVUA-0}=8LHGFPF_UJ?{DxHtABet@OgKA@ArVQ^}xX zr||y>CS1@@q7PtD^|JcfbHde?`al?2SWEG7a|p%Fw4dqk-wLL-wxhxiK^}{o9@@5k zkACh~jUr7a23{eLdzG3?E6-Q-J_}Ry%goq|3r%bGWIo$dgSIQDj*AzqWC128kL!%r zP$^2DdcjI&z*X;4 z(Z#1~l*8+uddWw?$pdt?j80(p6c+BRE^XoDHz>sIv*9xAzI%^iXZHr}{?KHk&G-4_ zY1#577LUDQL5hHL<#H_ijyXutT_cTKv%bY!gLz8n0uX3vVAqIxu ztq<;_2G;e~@+%4to2ifMd+$C|Jm0>X0VlCDESE!>mv1#=fQ%&3K+WpXucW^Np5HpaB8v^&6-E0>#U~?%(?*Rs)bJI$68%3>q>@%GySo z1>i<_=Mph%szboV0IV-KB5$NKo}S0+_KtQx_oqxvy(1$fZNwPon_ImY2WSNZbY-4T znI8348&Vz;B@eX6unmGNs#lImJRam3vd?(42fjf^tU5NrGxr5maHm_oPB2{&S;V--$@~DH25t zi20p7w3RWj|8!PRhm){hG!aLlRG~8bv1||`RO5HlUh5dQVmbo8ZfgTK?QzObf@C&S zw5UJG2^}5Jki>HkkH+JofZeiN!rdg93HF38iKL-FeET3qmYaWjs)M`m{at#qh+=x z6i7RA=$>GxaSLNVJgSbPss&JiES@j8iF`%|C6BJy8J9)Km#78=J}A{mhzOco_%=a+ zQL-9yeM-J~{r>(Fpz4!~pNhU;%|^irZIc#a3k!CZ9A7OmWraem3@!Yv$^HGQLpz+C z<=NEkUe53hQbzgF|Br)}w_47oqN84eWhV-C9NQB|VgvcpKnP?JW>yzAaPPmmuCNf~ z)-x{&u2W>z_a6dY7tzM5BSf}&B6zi|NdWHavyaVcrBmphp4*P=sa7xRwK!x?&?Vxa zH9s7gRux*L_sSi4ElTX!eeByq&nW{w46z9$+3rC`Up#(J)~R|f7R%0T;6U`)DSojc zTmRDlue{?&YvpD3>znf=b(|o|Ro`Sq^|kxa9KY-D%DS8NzQ#l2)p*-=CVnVR=2c#3 zuuXt?&P;4>=EQiRi^5<`2k|v*nxnH2|E0(3x)B4seqH;YrHhODoNmz3%nq5}Lz)^> z+AXQ>ivym@R-D1rkgPR*-pGS=62B(A(@W{WM}sqsY^(D@KJ$%_pUrp5)UUrTbXZS! za%$klu3$;3d-+e4|yysUAt`P1(>J^vb7eNz7jwKQ0|NjlNA z%hn&ce$bb_#_Hm=hxXI?Jk|RRj0$M1>C9L03P3~s+YZ-kuERzo1yRj+z?+B~DoI?8X)?l(z1QnUiy;v-_Y%?Vl>rz>%Ln$7>0X&?U98W zBJuW&?U^sgqx0GHi4*xAIQ~>H5;=lwcwa^Fn5$~%3Us)#^>5pQE-Yg&I|x((Q`nkE zJ6-0mwua5mLl6{E?PwssXbT+^gY|_`3;DBi8U&8ZfjIV+%jMTs+QTv{f<;~^{y=m0 zUfw6$(WbPSJ?Mn`QMgF;-N}?!%>@TZnwKiYeLy?)xV^yzRle~H_H;mlLw45VZ@okC zd|IaeRLy(Gre7EH;U5S{;wUDnwMk;}U*x!3t(h$RkK?>(9Qx>0u~z4uIy+iEEVno6 zlyErv>4CTDMA!$|Ik7e)A>Y4x)wTMeaNLPL(<;T_^gwGbMJ>yHz^h6A#`rY#y(?eQ zahvYC2#c^y6cvA1#>-X0{xoBTxdY$nrg?OjJ2d%dh9u52l?>C3Cc0zJb@16iToA#cK0pq|hE|T50_NqLOP{da~&_d99RYfvif0|7@QL?e7?ol+&n9 zf7ZhB&$3T~5@H(3hpO7jU{7OfWvRX9f9sdj?n8B(UR8R{N(0m4EU&CbNSi`U+wm3E zvp+P_eRFW)e1uK*`Z(aB*$x1izwX4zvS!w(dPRSE%kJOT1aig@7(JnSZg=pF z=t+mva72@fGVS})=r;cVPqyp@7Bclp2yyahH*V7Ho?1ahV&e?|XqT6^&~mYcs+n)= z_Obu%4u6;LdHF&t&@Q0jR*^GR-&T*OW(Zcb&?NC#wnO&WTCBk4=|Y;n|9X?*n*9jXO)14) zlj#vrczd7y<5{8qfsx@&OiRGibpQ2ypTW$R3n{Uu?5$P1wv$5~(IM7szI*YuC#kd2 z)Rs+BpqlUueuy618%B~s>f-Jq*ThHiKpvvl-cDfCUGoEG7jz;;DtX=x3ZrG$eBv6m z2~cV5>HH4akZ8X)e{>b=KgnkHA)zC?@Hy^0pAx%R(*gi8KG;4xZyI@?4&6^&Ztuw! z;>VF9n7MPT6sNpTUn+pUeeFqup^Di)rtFSte6#i@}b3Bk>eW4Rx z{B8$PtRsK*4eH|ee3xEv1P$sqfIM5K1c0sE4%XP}gM$3qc9-{-Tnui!ea3R7fH9ic z4uGdk5BI%le}}H8A&-F|?&HrFZ_XQKJNGyPMve^G?qWOEy(UN(?2Cj$Z3%)^FeQvR z{pQ6U+fB4tB#swrd2B^ zCuN*2s}#ESVQ<&pI&sNNpQ!cqDADjl&!2szxBNPzzzrvqo@D}LR?Di_TnM=Utw0L4 z*0#Va;u!i>o>yjji!FNroXh#giYwyZs)v6a zh2Jb*DKNU4$XW3DhWS^fV|xQotDLJ`0n%%m$@}v3e$oCi{0GP@fc5fX|Ka<}z0peN z4$T<-I|zH+JR~Kc1gQ5+8ZxMnd^EpYF5IV$!|CY((gk2tkz?QpYd4jdXWtChWIBrt z89=`a``oFHHeGjKcK@g=TJ<$oQdUZFDe}4By!DE<$?$e*(04iH{7Vah zwYCiE<@eQ6Fb{>>8VceICT0%Uukx?r>^u9i+Lp0-JdWeTb7eeB70pN~H0$-j^9$l7 zcfUz&fDVn^5^;CZE%a5~Cp&wG_u-^9H5$x@)9yMx36knFu`P82O(|w^+-JpxWtn#+ zyu-5!vJ6l+ew-w_{F_UeBrfnE;NpA$p4*2+@ne^lN6%-(rhZVJA)78bjmlb3-KJ~u z4+Z{%AH|vOOaD(B*BRB+mPQ3Zm{1)94+084MFb*UK$?IGL-iRzAk={LPLL)=&=4^Y z1Ri^#`86NS2w_yhW5G2AtO5{;67d>={w5?tFEW2q%pF`=M zfRyqhikILfpFE$tF;_>`S?sqw-6?tgD_q>Q^BHyYYd2(nVKTC!fsXCXEzeZJi2Nkv zch?vbUhRIE_jz6F^77JQyTSeq*iw;KiF_2gLJs$Q8Y%S{p(|?`2inbfi$%|69vlN_ zQx@Xu@8u471e+B-JLIC$8Dfx3J6Z=LNEV_$8i{`q`aN6B9sg)RmIYJI_Yf4;q2)Yr zwR2BT^XOiJVSaMoG649#8RPyYa_?wP~WL9?CGkICY4Bq%}?ro@d5wS9qNgk6!;jc^%G zN?%_EHcu;V^f7yhwBv{*HOfVX$o!N>7RDfsgp2AG}MKEIa@FslA)(9oQbG9-%%CE zShfNacEMg}~tLJ?_4nfBWnSlqC}sH#(>@T2TU5= zdQac8Q>yMKm1zyQY`b5)P6s^@N)jALG>HA{oH!*B6Ht)l7 zprb2m+}#+F;pmv+nz16LLuAWBK)CdaNe!^hs~4G&5uIhzLh>N1L3Xx)g38Ke!Yvlh zg>H>y&H><_Z>0Bez|C|4`3;=3dp;+gqg&PZwZhW`QFlKsM)c}kh&V>qO`CS!!AgjJZRzCcF0y-f zaR8S9d^a$A3P(s;+#(0jJ+_cGl{HDfq@z8fz$a#6q{txs;AhHf)R+y&W#PjS-s~QWC#g%$C zIq`y6&WaF+z&lB)_FnABpFk%k)9;~PTOEeF&mCMtt{gFase~TYH<0*;&h$XTAAY(I zKdAT+P;7sxHcy56^FC#q^O?J!mR3v6oE)vCZFNm{H)2K+KLJmC z4Lx_W1>GY5KWV61X8=0Mnj04yDK|Kg@KcWSqDS9Rr}zxDW*og0?Gtv(Fs;e0MQbif zQ<^i7Q+;Xa4tSuSCbFa2Mhu=A?=IDrxuXE7iF29wn)@_dH@C6wqyi?(%yPouL165pWLF!aCRoD# z&FZbbf@c4cN2d=b{|u!nPBK0K|HDJ?l@AW|y#!XYq`X)cKTXTb>Sye;9=nbPE>=zG zSNecrF|zlI*Go;6jw~w;Q~V9H`t6Y^v!Vf`#ZZbdJWEJNKC!> zPW_N*Z%y$@i2`q~v$MVy&1pbWTGiqlVGalS3>bx%@IA4xZI8X)G(m{1g#YA)U(?3)4Lms%QCy1y9e(YOd%ix50Jdrgz8Pq9y88NS~A={&N zu`2gBa|bGhHIa22sVmTB7pY@c8poKn%{BFXsT|vf>%lp7RHR_OD?8@Po{~Q!c@U5oBYT5S^00PN!9>Q zfNzjUS0Pwn{=lvw)*xFCrKtTUSg0`=f5|G-VOo~4VAVIuVXOyr5@OYK>_UCB>4^lAR4e4BA0Hb<33{8b9%bEZqT!w=;N3LseYn43Hwsn zxcn7daxHMNuhCZoA7~8MmVtN2^DIIf0d6`-s8PN_KOxOwHYpL#@M~ghK=dMcBCaB} zxDT@SWl@#zm-U%E^Z849N;ovv?1qjyT2lD@-)`w=gdFujlf~x58E-Uhn5jz;*SqKA zIp8i?UKo&z*0sH~%)-=wK%sDP$+gInaA9_(|=~nubGh(4vn1+5y_S;s#>=QG98&nL1Gucb?q%4LNnGuaJ8Wf{tO(wTr z0mQ5f3lGjptt}=AQ`f}tD;I89XH*N92!DC!c%tk6VFmg8kGwKD@8U_wXl;g}8lOqh zzNGP?k$s)z2u<|;4}eB)hZoXQjO=4PE(?(SC5(6~E|ySux)yL$nRHtz23`dIhubN5>3?0@ga z8?S0)MP!C#Ma~%!Q@)v@3UcBIusES%qYe|?fZkI&ZsO26cQ{=3HY?8MJ4CUf9FAa|7!LIhL7->z}QM? zI)H#+qW*P&nq|*?4nY7!QbbV274%dW;uo6uY^Cn%8opn9yGP9?DxM#NtgX;5BHtWP zzwh8KE-uiYKM3ci?-zHgt7qqDS#|+0?~dfd=f7`nZ#$Ao7gTkvsQt+fiEnEKSYa8l_K~X{!!uI_4%PS=+CD zWN9>&MA>|f<^OZ1wgQ`vg=Q>2`<;SG8x-7NMpfojrpL3NmH+qs|LXo#u;+Nv%}eV) zm+}zhz$8K$+#3F;du5b@&8v%H#?CMgKwT|J|Fld5*Q($X9BC) z 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 s$qZg;tyysR*ULEP$;u{!yd*DWG!v8&L0#XrZUG 上传分片 -> 完成分片(中止分片)**三个阶段。 + + +### 参数说明 +- `Bucket`: 文件上传后所在的存储空间 +- `Key`: 文件上传后在存储空间里的名称 +- `UploadId`: 用于关联所有分片的唯一标识符 +- `PartNumber`: 当前上传的分片编号 +- `Body`: 要上传的文件分片的内容 +- `ContentMD5`: 指定分片数据的 MD5 校验值 + +### 示例 +> 执行该示例前请确保配置文件的正确性
以下代码段需要在上下文中运行 + +
+{% highlight javascript linenos %} +function MultipartUpload() { + const [bucketName, setBucketName] = useState(""); + const [key, setKey] = useState(""); + const [selectedFile, setSelectedFile] = useState(null); + const [uploadStatus, setUploadStatus] = useState(""); + + const handleFileInput = (event) => { + setSelectedFile(event.target.files[0]); + }; + const calculateMD5 = async (blob) => { + const arrayBuffer = await blob.arrayBuffer(); / + const wordArray = CryptoJS.lib.WordArray.create(arrayBuffer); + return CryptoJS.MD5(wordArray).toString(CryptoJS.enc.Base64); + }; + const sliceUpload = async () => { + if (!selectedFile) { + setUploadStatus("请选择文件上传"); + return; + } + const partSize = 8 * 1024 * 1024; // 8MB + const numParts = Math.ceil(selectedFile.size / partSize); + let uploadId; + try { + + // 创建分片上传任务 + const createMultipartUploadResponse = await s3.send( + new CreateMultipartUploadCommand({ + Bucket: bucketName, + Key: key, + }) + ); + uploadId = createMultipartUploadResponse.UploadId; + const uploadPromises = []; + setUploadStatus("开始创建分片请求..."); + + for (let partNumber = 1; partNumber <= numParts; partNumber++) { + const start = (partNumber - 1) * partSize; + const end = Math.min(start + partSize, selectedFile.size); + const filePart = selectedFile.slice(start, end); + const md5Hash = await calculateMD5(filePart); + const uploadPartCommand = new UploadPartCommand({ + Bucket: bucketName, + Key: key, + UploadId: uploadId, + PartNumber: partNumber, + Body: filePart, + ContentMD5: md5Hash, + }); + uploadPromises.push( + s3.send(uploadPartCommand).then((uploadPartResponse) => ({ + ETag: uploadPartResponse.ETag, + PartNumber: partNumber, + })) + ); + } + + const uploadedParts = await Promise.all(uploadPromises); + + uploadedParts.forEach((part) => { + if (!part.ETag) { + throw new Error(`Part ${part.PartNumber} 上传失败`); + } + }); + + // 完成分片上传 + await s3.send( + new CompleteMultipartUploadCommand({ + Bucket: bucketName, + Key: key, + UploadId: uploadId, + MultipartUpload: { + Parts: uploadedParts, + }, + }) + ); + setUploadStatus(`成功上传 ${key} 到 ${bucketName}`); + + } catch (error) { + console.error("上传文件失败:", error); + setUploadStatus("上传文件失败"); + if (uploadId) { + + // 如果上传失败,中止上传 + await s3.send( + new AbortMultipartUploadCommand({ + Bucket: bucketName, + Key: key, + UploadId: uploadId, + }) + ); + } + } + }; + +{% endhighlight %} +
\ No newline at end of file diff --git "a/collections/_js-sdk/\345\210\240\351\231\244\346\226\207\344\273\266.md" "b/collections/_js-sdk/\345\210\240\351\231\244\346\226\207\344\273\266.md" new file mode 100644 index 0000000..a31c10d --- /dev/null +++ "b/collections/_js-sdk/\345\210\240\351\231\244\346\226\207\344\273\266.md" @@ -0,0 +1,73 @@ +--- +title: '删除文件' +sidebar: + nav: js-sdk +--- +本SDK提供`DeleteObjectsCommand`类和`HeadObjectCommand`类实现文件删除。完整代码详见 [Github](https://github.com/aws/aws-sdk-js-v3/tree/main/clients/client-s3/src/commands) 。 +`DeleteObjectsCommand`调用的 S3 API 为 DeleteObjects, 具体参见[DeleteObjects API 文档](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html)。 +`HeadObjectCommand`调用的 S3 API 为 HeadObject, 具体参见[HeadObject API 文档](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html)。 + + + +### 参数说明 +- `Bucket`: 文件所在的存储空间 +- `Key`: 文件在存储空间内的名称 + + + +### 示例 +> 执行该示例前请确保配置文件的正确性
以下代码段需要在上下文中运行 + +
+{% highlight javascript linenos %} +function DeleteObjects() { + const [bucketName, setBucketName] = useState(""); + const [objectKeys, setObjectKeys] = useState(""); + const [deleteStatus, setDeleteStatus] = useState(""); + + const checkObjectExists = async (key) => { + try { + await s3.send(new HeadObjectCommand({ Bucket: bucketName, Key: key })); + return true; + } catch (err) { + if (err.name === 'NotFound') { + return false; + } else { + throw err; + } + } + }; + + const handleDelete = async () => { + const keysArray = objectKeys.split(",").map(key => key.trim()); + + try { + const existingKeys = []; + for (const key of keysArray) { + const exists = await checkObjectExists(key); + if (exists) { + existingKeys.push({ Key: key }); + } + } + if (existingKeys.length === 0) { + setDeleteStatus("没有找到可删除的对象"); + return; + } + const deleteParams = { + Bucket: bucketName, + Delete: { + Objects: existingKeys, + }, + }; + + const command = new DeleteObjectsCommand(deleteParams); + const response = await s3.send(command); + setDeleteStatus(`成功删除 ${response.Deleted.length} 个对象. 删除对象: ${response.Deleted.map(d => d.Key).join(", ")}`); + } catch (err) { + console.error("删除文件失败:", err); + setDeleteStatus("删除对象失败"); + } + }; + +{% endhighlight %} +
diff --git "a/collections/_js-sdk/\345\255\230\345\202\250\347\261\273\345\236\213\350\275\254\346\215\242.md" "b/collections/_js-sdk/\345\255\230\345\202\250\347\261\273\345\236\213\350\275\254\346\215\242.md" new file mode 100644 index 0000000..37daced --- /dev/null +++ "b/collections/_js-sdk/\345\255\230\345\202\250\347\261\273\345\236\213\350\275\254\346\215\242.md" @@ -0,0 +1,68 @@ +--- +title: '存储类型转换' +sidebar: + nav: js-sdk +--- +本SDK提供`CopyObjectCommand`类实现存储类型转换,允许在不移动或重新上传文件的情况下更新对象的元数据。完整代码详见 [Github](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/src/commands/CopyObjectCommand.ts) 。 +`CopyObjectCommand`调用的 S3 API 为 CopyObject, 具体参见[CopyObject API 文档](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html)。 + + + +### 参数说明 +- `Bucket`: 文件所在的存储空间 +- `CopySource`: 存储空间内的文件路径 +- `Key`: 文件在存储空间内的名称 +- `StorageClass`: S3 存储类型 +- `MetadataDirective`: 复制原有元数据("COPY")或替换为新的元数据("REPLACE") + +### 存储类型转换规则 + +| US3存储类型 | S3存储类型 | US3对应S3默认存储类型 | +| ----------------- | ---------------------------------------------- | -------------------- | +| STANDARD | STANDARD
STANDARD_IA | STANDARD | +| IA | ONEZONE_IA
INTELLIGENT_TIERING
REDUCED_REDUNDANCY | ONEZONE_IA | +| ARCHIVE | GLACIER
DEEP_ARCHIVE | GLACIER | + + + + +### 示例 +> 执行该示例前请确保配置文件的正确性
以下代码段需要在上下文中运行 + +
+{% highlight javascript linenos %} +function UpdateStorageClass() { + const [bucketName, setBucketName] = useState(""); + const [keyName, setKeyName] = useState(""); + const [storageClass, setStorageClass] = useState(""); + const [updateStatus, setUpdateStatus] = useState(""); + + const handleUpdate = async () => { + if (!bucketName || !keyName || !storageClass) { + setUpdateStatus("请填写所有字段"); + return; + } + + const copySource = `${bucketName}/${keyName}`; + const params = { + Bucket: bucketName, + CopySource: copySource, + Key: keyName, + StorageClass: storageClass, + MetadataDirective: 'COPY' + }; + + try { + const command = new CopyObjectCommand(params); + const response = await s3.send(command); + console.log("存储类型更新成功:", response); + setUpdateStatus("存储类型更新成功!"); + } catch (err) { + console.error("存储类型更新失败:", err); + setUpdateStatus("存储类型更新失败"); + + } + }; + +{% endhighlight %} +
diff --git "a/collections/_js-sdk/\345\256\211\350\243\205.md" "b/collections/_js-sdk/\345\256\211\350\243\205.md" new file mode 100644 index 0000000..997395e --- /dev/null +++ "b/collections/_js-sdk/\345\256\211\350\243\205.md" @@ -0,0 +1,28 @@ +--- +title: '安装' +sidebar: + nav: js-sdk +--- + +## 环境准备 +AWS SDK for JavaScript v3,推荐使用 ES6+ 版本的 JavaScript。在 Node.js 环境中,版本为 16.x 或更高。浏览器需支持基本的 HTML5 特性(支持 IE10 以上浏览器) + +## 下载SDK源码 + +* [通过Github下载](https://github.com/aws/aws-sdk-js-v3) + +## 安装SDK示例 + +* [SDK示例下载](https://github.com/ufilesdk-dev/S3-JS-SDK) + +### 本地安装 + +
+{% highlight bash linenos %} +$ git clone https://github.com/ufilesdk-dev/S3-JS-SDK.git +$ cd S3-JS-SDK +$ nvm install 18 # Mac可执行 $ brew install nvm 安装nvm +$ nvm use 18 +$ npm install +{% endhighlight %} +
diff --git "a/collections/_js-sdk/\345\277\253\351\200\237\344\275\277\347\224\250.md" "b/collections/_js-sdk/\345\277\253\351\200\237\344\275\277\347\224\250.md" new file mode 100644 index 0000000..4e18f32 --- /dev/null +++ "b/collections/_js-sdk/\345\277\253\351\200\237\344\275\277\347\224\250.md" @@ -0,0 +1,77 @@ +--- +title: '快速使用' +sidebar: + nav: js-sdk +--- + + +## 快速运行 SDK 示例 +### 1. 准备工作 +1. 登录[对象存储控制台](https://console.ucloud.cn/ufile/ufile),创建存储空间。获取存储空间名称和存储空间域名。 +2. 登录[账号管理控制台](https://console.ucloud.cn/uaccount/api_manage),获取您的项目公钥和私钥。 +3. 配置CORS 规则,来源Origin可以按需配置,Allow-Header 需配成*,Expose-Headers 需要 ETag、Content-Length 以及其他 js 需要读取的 header 字段,如下图所示。 +![image-cors](img/cors.png) + +### 2. 配置s3客户端文件 + 确保已完成跨域设置,进入src/utils文件夹,修改s3Client.js 文件 + + +
+{% highlight javascript linenos %} +import { S3Client } from "@aws-sdk/client-s3"; + +const s3 = new S3Client({ + endpoint: "", // http://s3-cn-sh2.ufileos.com + region: "", // cn-sh2 + signatureVersion: 'v4', + credentials: { + accessKeyId: "", + secretAccessKey: "", + }, + forcePathStyle: true, // 路径风格 +}); + +export default s3; + +{% endhighlight %} +
+ +* 参数说明 + +| 参数名 | 参数描述 | 类型 | 是否必填 | +| ----------------- |--------------------------------------| -------- | -------- | +| accessKeyId | UCloud 的 API 公钥或者是 US3服务提供的 Token 公钥 | String | 是 | +| secretAccessKey | UCloud 的 API 私钥或者是 US3服务提供的 Token 私钥 | String | 是 | +| endpoint | 访问域名,具体可参考 AWS S3 协议支持说明 | String | 是 | +| region | 存储空间所在地域 | String | 是 | +| signatureVersion | AWS签名版本,默认使用签名版本 4 来验证请求 | String | 否 | +| forcePathStyle | 使用路径风格或虚拟主机风格,具体参考 AWS S3 协议支持说明 | Boolean | 否 | + +### 3. 运行示例 + +
+{% highlight bash linenos %} +$ npm start # 启动示例 +{% endhighlight %} +
+ + ![image-demo](img/demo.png) + 以普通上传为例,必须的输入参数为配置文件对应地域下的Bucket Name,以及需要保存在该Bucket 下的Key Name (即文件名) ,存储类型默认为标准存储。若要指定存储类型,参照**S3协议支持说明**,确认完输入信息后点击上传文件。 + 如果浏览器控制台出现了CORS 相关的错误信息,请检查是否正确完成跨域配置。 + +### 注意 +* PutObject 目前仅支持 5GB 大小文件,如果需要上传大于 5GB 的文件,请采用分片上传的 API +* PostObject 目前仅支持最大 32MB 文件的上传 +* CopyObject 目前仅支持最大 5G文件的拷贝 +* UploadPart 目前仅支持 8MB 定长分片大小(最后一个分片允许小于 8MB)。若有不定长分片的需求,请联系技术支持 +* US3 S3 对 AWS S3 兼容的存储类型及其转换规则参考 存储类型转换规则 +* US3 的 ETag 计算方式与 AWS S3 存在部分差异,建议不依赖该 ETag +* 目前不支持 S3 API 的 MD5 校验,建议关闭 +* US3 的访问权限(ACL)定义与 AWS S3 存在差异,具体参考 访问权限定义(ACL) +* 目前不支持多版本功能(Versioning) +* 目前不支持标签功能(Tagging) +* ListObjects请求中的max-keys参数(请求返回对象的最大数量)最大值为5000 +* 更详细的内容请查看 AWS S3 协议支持说明 + + + diff --git "a/collections/_js-sdk/\346\213\267\350\264\235\346\226\207\344\273\266.md" "b/collections/_js-sdk/\346\213\267\350\264\235\346\226\207\344\273\266.md" new file mode 100644 index 0000000..b5fb797 --- /dev/null +++ "b/collections/_js-sdk/\346\213\267\350\264\235\346\226\207\344\273\266.md" @@ -0,0 +1,50 @@ +--- +title: '拷贝文件' +sidebar: + nav: js-sdk +--- +本SDK提供`CopyObjectCommand`类用于拷贝文件,完整代码详见 [Github](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/src/commands/CopyObjectCommand.ts) 。 +`CopyObjectCommand`调用的 S3 API 为 CopyObject, 具体参见[CopyObject API 文档](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html)。 + + + +### 参数说明 +- `sourceBucket`: 源文件所在的存储空间 +- `sourceKey`: 源文件所在存储空间里的名称 +- `destinationBucket`: 目标存储空间 +- `destinationKey`: 文件在目标存储空间里的名称 + + +### 示例 +> 执行该示例前请确保配置文件的正确性
以下代码段需要在上下文中运行 + +
+{% highlight javascript linenos %} +function CopyFile() { + const [sourceBucket, setSourceBucket] = useState(""); + const [sourceKey, setSourceKey] = useState(""); + const [destinationBucket, setDestinationBucket] = useState(""); + const [destinationKey, setDestinationKey] = useState(""); + const [copyStatus, setCopyStatus] = useState(""); + + const handleCopy = async () => { + const copySource = `${sourceBucket}/${sourceKey}`; + const params = { + CopySource: copySource, + Bucket: destinationBucket, + Key: destinationKey, + }; + + try { + const command = new CopyObjectCommand(params); + const response = await s3.send(command); + console.log("复制文件成功:", response); + setCopyStatus("复制文件成功!"); + } catch (err) { + console.error("复制文件失败:", err); + setCopyStatus("复制文件失败"); + } + }; + +{% endhighlight %} +
diff --git "a/collections/_js-sdk/\346\214\207\345\256\232\345\255\230\345\202\250\347\261\273\345\236\213\344\270\212\344\274\240.md" "b/collections/_js-sdk/\346\214\207\345\256\232\345\255\230\345\202\250\347\261\273\345\236\213\344\270\212\344\274\240.md" new file mode 100644 index 0000000..99fa1cd --- /dev/null +++ "b/collections/_js-sdk/\346\214\207\345\256\232\345\255\230\345\202\250\347\261\273\345\236\213\344\270\212\344\274\240.md" @@ -0,0 +1,65 @@ +--- +title: '指定存储类型上传' +sidebar: + nav: js-sdk +--- +本SDK提供`PutObjectCommand`类用于指定存储类型上传操作,大文件(5G以上)请使用分片上传,完整代码详见 [Github](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/src/commands/PutObjectCommand.ts) 。 +`PutObjectCommand`调用的 S3 API 为 PutObject, 具体参见[PutObject API 文档](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html)。 + +### 参数说明 +- `Bucket`: 文件上传后所在的存储空间 +- `Key`: 文件上传后在存储空间里的名称 +- `Body`: 待上传的文件全路径 +- `StorageClass`: S3 存储类型 + +### 存储类型转换规则 + +| US3存储类型 | S3存储类型 | US3对应S3默认存储类型 | +| ----------------- | ---------------------------------------------- | -------------------- | +| STANDARD | STANDARD
STANDARD_IA | STANDARD | +| IA | ONEZONE_IA
INTELLIGENT_TIERING
REDUCED_REDUNDANCY | ONEZONE_IA | +| ARCHIVE | GLACIER
DEEP_ARCHIVE | GLACIER | + + +### 示例 +> 执行该示例前请确保配置文件的正确性
以下代码段需要在上下文中运行 + +
+{% highlight javascript linenos %} +function UploadFile() { + const [selectedFile, setSelectedFile] = useState(null); + const [bucketName, setBucketName] = useState(""); + const [keyName, setKeyName] = useState(""); + const [storageClass, setStorageClass] = useState(""); + const [uploadStatus, setUploadStatus] = useState(""); + + const handleFileInput = (event) => { + setSelectedFile(event.target.files[0]); + }; + + const handleUpload = async () => { + if (!selectedFile || !bucketName || !keyName) { + setUploadStatus("请输入桶名,文件,和文件名"); + return; } + const params = { + Bucket: bucketName, + Key: keyName, + Body: selectedFile,}; + if (storageClass) { + params.StorageClass = storageClass; + } + try { + const command = new PutObjectCommand(params); + const response = await s3.send(command); + console.log("文件上传成功:", response); + setUploadStatus("文件上传成功!"); + } catch (err) { + console.error("文件上传失败:", err); + setUploadStatus("文件上传失败"); + + } + }; +} + +{% endhighlight %} +
diff --git "a/collections/_js-sdk/\346\226\207\344\273\266\344\270\213\350\275\275.md" "b/collections/_js-sdk/\346\226\207\344\273\266\344\270\213\350\275\275.md" new file mode 100644 index 0000000..431fb64 --- /dev/null +++ "b/collections/_js-sdk/\346\226\207\344\273\266\344\270\213\350\275\275.md" @@ -0,0 +1,58 @@ +--- +title: '文件下载' +sidebar: + nav: js-sdk +--- +本SDK提供`GetObjectCommand`类实现文件下载。完整代码详见 [Github](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/src/commands/GetObjectCommand.ts) 。 +`GetObjectCommand`调用的 S3 API 为 GetObject, 具体参见[GetObject API 文档](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html)。 + + + +### 参数说明 +- `Bucket`: 文件所在的存储空间 +- `Key`: 文件在存储空间内的名称 + + + +### 示例 +> 执行该示例前请确保配置文件的正确性
以下代码段需要在上下文中运行 + +
+{% highlight javascript linenos %} +function DownloadFile() { + const [bucketName, setBucketName] = useState(""); + const [keyName, setKeyName] = useState(""); + const [downloadStatus, setDownloadStatus] = useState(""); + + const handleDownload = async () => { + if (!bucketName || !keyName) { + setDownloadStatus("请输入桶名和文件名"); + return; } + const params = { + Bucket: bucketName, + Key: keyName + }; + try { + const command = new GetObjectCommand(params); + const response = await s3.send(command); + const blob = await streamToBlob(response.Body); + console.log("下载文件大小:", blob.size); + const url = window.URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.setAttribute('download', keyName); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + setDownloadStatus("下载文件成功!"); + + } catch (err) { + console.error("下载文件失败:", err); + setDownloadStatus("下载文件失败"); + + } }; +} + + +{% endhighlight %} +
diff --git "a/collections/_js-sdk/\346\246\202\350\277\260.md" "b/collections/_js-sdk/\346\246\202\350\277\260.md" new file mode 100644 index 0000000..6942560 --- /dev/null +++ "b/collections/_js-sdk/\346\246\202\350\277\260.md" @@ -0,0 +1,28 @@ +--- +title: '概述' +sidebar: + nav: js-sdk +--- + +## US3基本概念 + +在对象存储系统中,存储空间(Bucket)是文件(File)的组织管理单位,文件(File)是存储空间的逻辑存储单元。对于每个账号,该账号里存放的每个文件都有唯一的一对存储空间(Bucket)与键(Key)作为标识。我们可以把 Bucket 理解成一类文件的集合,Key 理解成文件名。由于每个 Bucket 需要配置和权限不同,每个账户里面会有多个 Bucket。在 US3 里面,Bucket 主要分为公有和私有两种,公有 Bucket 里面的文件可以对任何人开放,私有 Bucket 需要配置对应访问签名才能访问。 + + +## 示例程序 +[SDK示例下载](https://github.com/ufilesdk-dev/S3-JS-SDK) + +components文件夹下提供了示例程序: + +| 示例文件 | 示例内容 | +| -------- | -------- | +| [UploadFile.js](https://github.com/summerboy2134/US3-JS-SDK/blob/main/src/components/UploadFile.js) | 简单上传、指定存储类型上传 | +| [CopyFile.js](https://github.com/summerboy2134/US3-JS-SDK/blob/main/src/components/CopyFile.js) | 拷贝文件 | +| [UpdateStorageClass.js](https://github.com/summerboy2134/US3-JS-SDK/blob/main/src/components/UpdateStorageClass.js) | 存储类型转换 | +| [RestoreObject.js](https://github.com/summerboy2134/US3-JS-SDK/blob/main/src/components/RestoreObject.js) | 解冻归档文件 | +| [DownloadFile.js](https://github.com/summerboy2134/US3-JS-SDK/blob/main/src/components/DownloadFile.js) | 文件下载 | +| [GetObjectAcl.js](https://github.com/summerboy2134/US3-JS-SDK/blob/main/src/components/GetObjectAcl.js) | 获取对象权限信息 | +| [GetObjectAttr.js](https://github.com/summerboy2134/US3-JS-SDK/blob/main/src/components/GetObjectAttr.js) | 获取对象元数据 | +| [ListObjects.js](https://github.com/summerboy2134/US3-JS-SDK/blob/main/src/components/ListObjects.js) | 获取目录文件列表 | +| [MultipartUpload.js](https://github.com/summerboy2134/US3-JS-SDK/blob/main/src/components/MultipartUpload.js) | 分片上传 | +| [DeleteObjects.js](https://github.com/summerboy2134/US3-JS-SDK/blob/main/src/components/DeleteObjects.js) | 删除文件 | diff --git "a/collections/_js-sdk/\347\256\200\345\215\225\344\270\212\344\274\240.md" "b/collections/_js-sdk/\347\256\200\345\215\225\344\270\212\344\274\240.md" new file mode 100644 index 0000000..d01e042 --- /dev/null +++ "b/collections/_js-sdk/\347\256\200\345\215\225\344\270\212\344\274\240.md" @@ -0,0 +1,57 @@ +--- +title: '简单上传' +sidebar: + nav: js-sdk +--- +本SDK提供`PutObjectCommand`类用于上传操作,大文件(5G以上)请使用分片上传,完整代码详见 [Github](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/src/commands/PutObjectCommand.ts) 。 +`PutObjectCommand`调用的 S3 API 为 PutObject, 具体参见[PutObject API 文档](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html)。 + +### 参数说明 +- `Bucket`: 文件上传后所在的存储空间 +- `Key`: 文件上传后在存储空间里的名称 +- `Body`: 待上传的文件全路径 + + + +### 示例 +> 执行该示例前请确保配置文件的正确性
以下代码段需要在上下文中运行 + +
+{% highlight javascript linenos %} +function UploadFile() { + const [selectedFile, setSelectedFile] = useState(null); + const [bucketName, setBucketName] = useState(""); + const [keyName, setKeyName] = useState(""); + const [storageClass, setStorageClass] = useState(""); + const [uploadStatus, setUploadStatus] = useState(""); + + const handleFileInput = (event) => { + setSelectedFile(event.target.files[0]); + }; + + const handleUpload = async () => { + if (!selectedFile || !bucketName || !keyName) { + setUploadStatus("请输入桶名,文件,和文件名"); + return; } + const params = { + Bucket: bucketName, + Key: keyName, + Body: selectedFile,}; + if (storageClass) { + params.StorageClass = storageClass; + } + try { + const command = new PutObjectCommand(params); + const response = await s3.send(command); + console.log("文件上传成功:", response); + setUploadStatus("文件上传成功!"); + } catch (err) { + console.error("文件上传失败:", err); + setUploadStatus("文件上传失败"); + + } + }; +} + +{% endhighlight %} +
diff --git "a/collections/_js-sdk/\350\216\267\345\217\226\345\257\271\350\261\241\345\205\203\346\225\260\346\215\256.md" "b/collections/_js-sdk/\350\216\267\345\217\226\345\257\271\350\261\241\345\205\203\346\225\260\346\215\256.md" new file mode 100644 index 0000000..41cd94a --- /dev/null +++ "b/collections/_js-sdk/\350\216\267\345\217\226\345\257\271\350\261\241\345\205\203\346\225\260\346\215\256.md" @@ -0,0 +1,51 @@ +--- +title: '获取对象元数据' +sidebar: + nav: js-sdk +--- +本SDK提供`HeadObjectCommand`类用于获取对象元数据。完整代码详见 [Github](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/src/commands/HeadObjectCommand.ts) 。 +`HeadObjectCommand`调用的 S3 API 为 HeadObject, 具体参见[HeadObject API 文档](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html)。 + + + +### 参数说明 +- `Bucket`: 文件所在的存储空间 +- `Key`: 文件在存储空间内的名称 + + + + +### 示例 +> 执行该示例前请确保配置文件的正确性
以下代码段需要在上下文中运行 + +
+{% highlight javascript linenos %} +function GetObjectAttr () { + const [bucketName, setBucketName] = useState(""); + const [keyName, setKeyName] = useState(""); + const [attributes, setAttributes] = useState(null); + const [status, setStatus] = useState(""); + const handleGetAttributes = async () => { + const params = { + Bucket: bucketName, + Key: keyName, + }; + try { + const command = new HeadObjectCommand(params); + const response = await s3.send(command); + setAttributes({ + ContentLength: response.ContentLength, + LastModified: response.LastModified, + ETag: response.ETag, + ContentType: response.ContentType, + }); + setStatus("获取对象元数据信息成功!"); + } catch (err) { + console.error("获取对象元数据信息失败:", err); + setStatus("获取对象元数据信息失败"); + + } + }; + +{% endhighlight %} +
diff --git "a/collections/_js-sdk/\350\216\267\345\217\226\345\257\271\350\261\241\346\235\203\351\231\220\344\277\241\346\201\257.md" "b/collections/_js-sdk/\350\216\267\345\217\226\345\257\271\350\261\241\346\235\203\351\231\220\344\277\241\346\201\257.md" new file mode 100644 index 0000000..f56120c --- /dev/null +++ "b/collections/_js-sdk/\350\216\267\345\217\226\345\257\271\350\261\241\346\235\203\351\231\220\344\277\241\346\201\257.md" @@ -0,0 +1,57 @@ +--- +title: '获取对象权限信息' +sidebar: + nav: js-sdk +--- +本SDK提供`GetObjectAclCommand`类用于获取对象权限信息。完整代码详见 [Github](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/src/commands/GetObjectAclCommand.ts) 。 +`GetObjectAclCommand`调用的 S3 API 为 GetObjectAcl, 具体参见[GetObjectAcl API 文档](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObjectAcl.html)。 + + + +### 参数说明 +- `Bucket`: 文件所在的存储空间 +- `Key`: 文件在存储空间内的名称 + + +### 访问权限定义(ACL) + +| US3 ACL | [AWS S3 Canned ACL](https://docs.aws.amazon.com/AmazonS3/latest/userguide/acl-overview.html#canned-acl) | +| ----------------- | -------------------------------------------- | +| private | private | +| public-read | public-read | +| public-read-write | public-read-write | +| 不支持 | aws-exec-read
authenticated-read
bucket-owner-read
bucket-owner-full-control
log-delivery-write | + + + +### 示例 +> 执行该示例前请确保配置文件的正确性
以下代码段需要在上下文中运行 + +
+{% highlight javascript linenos %} +function GetObjectAcl() { + const [bucketName, setBucketName] = useState(""); + const [keyName, setKeyName] = useState(""); + const [aclInfo, setAclInfo] = useState(null); + const [status, setStatus] = useState(""); + + const handleGetAcl = async () => { + const params = { + Bucket: bucketName, + Key: keyName, + }; + + try { + const command = new GetObjectAclCommand(params); + const response = await s3.send(command); + setAclInfo(response.Grants); + setStatus("获取控制访问权限信息成功!"); + + } catch (err) { + console.error("获取控制访问权限信息失败:", err); + setStatus("获取控制访问权限信息失败"); + } + }; + +{% endhighlight %} +
diff --git "a/collections/_js-sdk/\350\216\267\345\217\226\347\233\256\345\275\225\346\226\207\344\273\266\345\210\227\350\241\250.md" "b/collections/_js-sdk/\350\216\267\345\217\226\347\233\256\345\275\225\346\226\207\344\273\266\345\210\227\350\241\250.md" new file mode 100644 index 0000000..ac9135e --- /dev/null +++ "b/collections/_js-sdk/\350\216\267\345\217\226\347\233\256\345\275\225\346\226\207\344\273\266\345\210\227\350\241\250.md" @@ -0,0 +1,76 @@ +--- +title: '获取目录文件列表' +sidebar: + nav: js-sdk +--- +本SDK提供`ListObjectsV2Command`类用于获取目录文件列表。完整代码详见 [Github](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/src/commands/ListObjectsV2Command.ts) 。 +`ListObjectsV2Command`调用的 S3 API 为 ListObjectsV2, 具体参见[ListObjectsV2 API 文档](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html)。 + + + +### 参数说明 +- `Bucket`: 文件所在的存储空间 +- `MaxKeys`: 请求返回的最大键数 + + + + +### 示例 +> 执行该示例前请确保配置文件的正确性
以下代码段需要在上下文中运行 + +
+{% highlight javascript linenos %} +function ListObjects() { + const [bucketName, setBucketName] = useState(""); + const [maxKeys, setMaxKeys] = useState(10); // 默认查找10个对象 + const [objects, setObjects] = useState([]); + const [status, setStatus] = useState(""); + + const handleListObjects = async () => { + if (!bucketName) { + setStatus("请提供有效的桶名称"); + return; + } + + try { + const command = new ListObjectsV2Command({ + Bucket: bucketName, + MaxKeys: maxKeys > 0 ? maxKeys : 10, + }); + + let isTruncated = true; + let continuationToken; + let allObjects = []; + + while (isTruncated && allObjects.length < maxKeys) { + const params = { ...command.input }; + if (continuationToken) { + params.ContinuationToken = continuationToken; + } + + const response = await s3.send(new ListObjectsV2Command(params)); + if (response.Contents) { + allObjects = [...allObjects, ...response.Contents]; + } + + + if (allObjects.length >= maxKeys) { + allObjects = allObjects.slice(0, maxKeys); + isTruncated = false; + } else { + isTruncated = response.IsTruncated; + continuationToken = response.NextContinuationToken; + } + } + + setObjects(allObjects); + setStatus(`成功查找到 ${allObjects.length} 个对象`); + + } catch (err) { + console.error("查找对象错误:", err); + setStatus("查找对象错误"); + } + }; + +{% endhighlight %} +
diff --git "a/collections/_js-sdk/\350\247\243\345\206\273\345\275\222\346\241\243\346\226\207\344\273\266.md" "b/collections/_js-sdk/\350\247\243\345\206\273\345\275\222\346\241\243\346\226\207\344\273\266.md" new file mode 100644 index 0000000..2b41a7d --- /dev/null +++ "b/collections/_js-sdk/\350\247\243\345\206\273\345\275\222\346\241\243\346\226\207\344\273\266.md" @@ -0,0 +1,80 @@ +--- +title: '解冻归档文件' +sidebar: + nav: js-sdk +--- +本SDK提供`RestoreObjectCommand`类和`HeadObjectCommand`类用于解冻归档文件。完整代码详见 [Github](https://github.com/aws/aws-sdk-js-v3/tree/main/clients/client-s3/src/commands) 。 +`RestoreObjectCommand`调用的 S3 API 为 RestoreObject, 具体参见[RestoreObject API 文档](https://docs.aws.amazon.com/AmazonS3/latest/API/API_RestoreObject.html)。 +`HeadObjectCommand`调用的 S3 API 为 HeadObject, 具体参见[HeadObject API 文档](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html)。 + + + +### 参数说明 +- `Bucket`: 文件所在的存储空间 +- `Key`: 文件在存储空间内的名称 +- `RestoreRequest`: 文件恢复后可访问的天数 + + + +### 示例 +> 执行该示例前请确保配置文件的正确性
以下代码段需要在上下文中运行 + +
+{% highlight javascript linenos %} +function RestoreObject() { + const [bucketName, setBucketName] = useState(""); + const [keyName, setKeyName] = useState(""); + const [restoreStatus, setRestoreStatus] = useState(""); + + const handleRestore = async () => { + setRestoreStatus("发送解冻请求..."); + const restoreRequest = { + Days: 3, + }; + + const params = { + Bucket: bucketName, + Key: keyName, + RestoreRequest: restoreRequest, + }; + + try { + const restoreCommand = new RestoreObjectCommand(params); + const restoreResponse = await s3.send(restoreCommand); + console.log("解冻请求发送成功:", restoreResponse); + setRestoreStatus("解冻请求发送成功!"); + setTimeout(() => { + checkRestorationStatus(bucketName, keyName); + }, 3000); + + } catch (err) { + console.error("解冻对象出错:", err); + setRestoreStatus("解冻对象出错"); + } + }; + const checkRestorationStatus = async (bucketName, keyName) => { + try { + const headParams = { + Bucket: bucketName, + Key: keyName, + }; + + const headCommand = new HeadObjectCommand(headParams); + const headResponse = await s3.send(headCommand); + + if (headResponse.Restore) { + const match = headResponse.Restore.match(/ongoing-request="(\w+)"/); + const isInProgress = match && match[1] === "true"; + const status = isInProgress ? "in-progress" : "finished"; + console.log(`解冻状态: ${status}`); + setRestoreStatus(`解冻状态: ${status}`); + } else { + setRestoreStatus("未找到解冻请求或解冻已完成"); + } + } catch (err) { + console.error("检查解冻状态出错:", err); + setRestoreStatus("检查解冻状态出错"); + } + +{% endhighlight %} +
diff --git "a/collections/_node.js-sdk/S3\345\215\217\350\256\256\345\270\270\347\224\250\345\267\245\345\205\267.md" "b/collections/_node.js-sdk/S3\345\215\217\350\256\256\345\270\270\347\224\250\345\267\245\345\205\267.md" new file mode 100644 index 0000000..08dff90 --- /dev/null +++ "b/collections/_node.js-sdk/S3\345\215\217\350\256\256\345\270\270\347\224\250\345\267\245\345\205\267.md" @@ -0,0 +1,599 @@ +--- +title: 'AWS S3 协议常用工具' +sidebar: + nav: node.js-sdk +--- + + +## 文件浏览器工具 + +### 功能说明 + +S3 Browser 是一种易于使用和强大的 Amazon S3 免费客户端。 它提供了一个简单的 Web 服务接口,可用于存储和检索任意数量的数据,无论任何时候从任何地方。 可以通过相关配置,直接操控 US3 对象存储的 Bucket 中的文件,进行上传,下载,删除等操作。 + +### 安装和使用 + +**适用的操作系统:Windows** + +### 安装步骤 + +​ 1.下载安装包 + +下载地址: [http://s3browser.com](http://s3browser.com/) + +​ 2.安装程序 + +进入下载页面,点击 Download S3 Browser,按照提示,进行安装即可。 + +### 使用方法 + +​ 1.增加用户 + +点击左上角 Accounts 按钮,在下拉框中,点击 Add new account + +在 Add new account 页面中,需要填写的项描述如下: + +**Account Name:** 账户名称,用户自定义。 + +**Account Type:** 账户类型,选择 S3 Compatible Storage + +**REST Endpoint:** 固定域名,填写参考支持 AWS S3 协议说明。比如:s3-cn-bj.example.com + +**Signature Version:** 签名版本,选择 Signature V4。 + +**Access Key ID:** Api 公钥,或者 Token。具体获取请参考S3 的 AccessKeyID 和 SecretAccessKey 说明。 +**Secret Access Key:** API 私钥。具体获取请参考S3 的 AccessKeyID 和 SecretAccessKey 说明。 + +**Encrypt Access Keys with a passward:** 请勿勾选。 + +**Use secure transfer(SSL/TSL):** 目前仅中国-北京二,中国-香港,越南-胡志明,韩国-首尔,巴西-圣保罗,美国-洛杉矶,美国-华盛顿地域支持 HTTPS,其他区域请勿勾选。 + +具体配置填写如下: + + + +点击左下角的Advanced S3-compatible storage settings配置签名版本以及URL风格 + + + +修改成功点击Close 关闭当前设置,点击Add new account 保存配置,则成功创建用户。 + +​ 2.对象操作 + +### 控制台功能说明 + + + +特别说明:目前分片大小只支持 8M 具体配置如下: 1.点击上方工具栏 Tools,在下拉列表中选择 Options,选择 General,在弹出页面中,设置 Enable multipart uploads with size (in megabytes) 为 8,如下图所示: + + + +### 常见问题 + +#### 1.上传文件超过78G,报错400.显示分片大小为16MB + +##### 问题原因: + +​ s3分片数限制为1万条,设置分片为8M是,只能满足78G左右以下文件的上传,如果文件大小超过78G,会自动调整分片大小,保证分片数小于1万条。目前us3后端s3协议只支持8M的分片,如果分片大小不对,会返回400错误。 + + +## 网络文件系统 S3FS + +### 功能说明 + +s3fs 工具支持将 Bucket 挂载到本地,像使用本地文件系统一样直接操作对象存储中的对象。 + +### 安装和使用 + +**适用的操作系统 Linux、MacOS** + +**适用s3fs版本:v1.83及以上** + +#### 安装步骤 + +MacOS 环境 + +``` +brew cask install osxfuse +brew install s3fs +``` + +RHEL 和 CentOS 7 或更新版本通过 EPEL: + +``` +sudo yum install epel-release +sudo yum install s3fs-fuse +``` + +Debian 9 和 Ubuntu 16.04 或更新版本 + +``` +sudo apt-get install s3fs +``` + +CentOS 6 及其以下版本 + +需要编译 s3fs ,并且安装该程序 + +#### 获取源码 + +首先,您需要从 上将源码下载到指定目录,以 `/data/s3fs` 为例: + +``` +1. cd /data +2. mkdir s3fs +3. cd s3fs +4. wget https://github.com/s3fs-fuse/s3fs-fuse/archive/v1.83.zip +``` + +#### 安装依赖项 + +CentOS 系统下安装依赖软件: + +``` + sudo yum install automake gcc-c++ git libcurl-devel libxml2-devel + fuse-devel make openssl-devel fuse unzip +``` + +#### 编译和安装 s3fs + +进入安装目录,执行如下命令进行编译和安装: + +``` +1. cd /data/s3fs +2. unzip v1.83.zip +3. cd s3fs-fuse-1.83/ +4. ./autogen.sh +5. ./configure +6. make +7. sudo make install +8. s3fs --version #查看 s3fs版本号 +``` + +可以查看 s3fs 的版本号,到此,s3fs 已经安装成功。 + +备注: +在执行第五步,`./configure` 的过程中,可能会遇到以下的问题。汇总为: + +报错: configure: error: Package requirements (fuse >= 2.8.4 libcurl >= 7.0 libxml-2.0 >= 2.6 ) were not met: + +原因: fuse 版本过低,此时,您需要手动安装 fuse 2.8.4 及以上版本,安装命令示例如下: + +1. yum -y remove fuse-devel #卸载当前版本的 fuse + +2. wget https://.com/libfuse/libfuse/releases/download/fuse_2_9_4/fuse-2.9.4.tar.gz + +3. tar -zxvf fuse-2.9.4.tar.gz + +4. cd fuse-2.9.4 + +5. ./configure + +6. make + +7. make install + +8. export + + PKG_CONFIG_PATH=/usr/lib/[pkgconfig:/usr/lib64/pkgconfig/:/usr/local/lib/pkgconfig](http://pkgconfig/usr/lib64/pkgconfig/:/usr/local/lib/pkgconfig) + +9. modprobe fuse #挂载 fuse 内核模块 + +10. echo "/usr/local/lib" >> /etc/[ld.so](http://ld.so/).conf + +11. ldconfig #更新动态链接库 + +12. pkg-config --modversion fuse #查看 fuse 版本号,当看到 “2.9.4” 时,表示 fuse 2.9.4 安装成功 + +### s3fs 使用方法 + +#### 配置密钥文件 + +在 `${HOME}/` 目录中创建 `.passwd-s3fs` 文件。文件格式为 `[API 公钥:API 秘钥]`。 + +公私钥获取方式具体请参考获取请参考S3 的 AccessKeyID 和 SecretAccessKey 说明。 + +例如: + +``` + [root@10-9-42-233 s3fs-fuse-1.83]# cat ~/.passwd-s3fs + AKdDhQD4Nfyrr9nGPJ+d0iFmJGwQlgBTwxxxxxxxxxxxx:7+LPnkPdPWhX2AJ+p/B1XVFi8bbbbbbbbbbbbbbbbb +``` + +将文件设置读写权限。 + +``` + chmod 600 ${HOME}/.passwd-s3fs +``` + +#### 执行挂载操作 + +操作指令解释: + +- 建立 US3 挂载文件路径 `${LocalMountPath}` + +- 获取已创建的存储空间(Bucket)名称 `${UFileBucketName}` + + 注意:空间名称不带域名后缀,比如 US3 空间名称显示为`test.cn-bj.example.com`,则`${UFileBucketName}=test` + +- 根据 US3 存储空间所在地域,本地服务器是否在 {{channelName}} 内网,参考AWS S3 协议支持说明。 + +- 执行命令。 + +参数说明如下: + +``` +s3fs ${UFileBucketName} ${LocalFilePath} +-o url={UFileS3URl} -o passwd_file=~/.passwd-s3fs +-o dbglevel=info +-o curldbg,use_path_request_style,allow_other +-o retries=1 //错误重试次数 +-o multipart_size="8" //分片上传的大小为 8MB,目前仅支持该值 -o +multireq_max="8" //当上传的文件大于 8MB 是采用分片上传,目前UFile 的 S3 +接入层不允许 PUT 单个文件超过 8MB,所以该值建议必填 +-f //表示前台执行,后台执行则省略 +-o parallel_count="32" //并行操作数,可以提高分片并发操作,建议不要超过 128 +``` + +**示例:** + +s3fs s3fs-test /data/vs3fs -o url=[http://internal.s3-cn-bj.example.com](http://internal.s3-cn-bj.example.com/) -o passwd_file=~/.passwd-s3fs -o dbglevel=info -o curldbg,use_path_request_style,allow_other,nomixupload -o retries=1 -o multipart_size="8" -o multireq_max="8" -o parallel_count="32" + +#### 挂载效果 + +执行 `df -h` 指令,可以看到 s3fs 程序的运行。效果如下: + + + +此时,可以看到 `/data/vs3fs` 目录下的文件和指定 bucket 的文件,保持一致。 也可以通过 tree 执行,查看文件结构。安装指令:`yum install -y tree` 效果如下: + + + +#### 文件上传和下载 + +挂载 US3 存储空间和后,可以像使用本地文件夹一样使用 US3 存储空间。 + +1. 拷贝文件到 `${LocalMountPath}` ,即是上传文件。 +2. 将文件从 `${LocalMountPath}` 拷贝到其他路径,即下载文件。 + +**注意:** + +1. 路径不符合 Linux 文件路径规范的路径,可以在 US3 管理控制台看到,但不会在 Fuse 挂载的 `\${LocalMountPath}` 下显示。 +2. Fuse 使用枚举文件清单会比较缓慢,建议直接使用指定到具体文件的命令,如 vim、cp、rm 指定具体文件。 + +#### 删除文件 + +将文件从 `${LocalMountPath}` 删除掉,则 US3 存储空间中,该文件也被删除掉。 + +#### 卸载US3存储空间 + +``` +sudo umount ${LocalMountPath} +``` + +### 性能数据 + +写入吞吐量40MB/s左右 读取吞吐量能达到166 MB/s(跟并发量相关) + +## goofys + +### 功能说明 + +goofys 工具同 s3fs, 也支持将 Bucket 挂载到本地,像使用本地文件系统一样直接操作对象存储中的对象。 性能方面比 s3fs 更优. + +### 安装与使用 + +**适用的操作系统:Linux,MacOS** + +### 使用步骤 + +下载可执行文件: + +[Mac X86-64](https://github.com/ufilesdk-dev/ufile-fs/releases/download/v0.21.1/ufile-fs-mac.tar.gz) [Linux X86-64](https://github.com/ufilesdk-dev/ufile-fs/releases/download/v0.21.1/ufile-fs-linux.tar.gz) + +使用如下命令解压到指定目录: + +``` +tar -xzvf goofys-0.21.1.tar.gz +``` + +默认在 `$HOME/.aws/credentials` 文件里面配置 bucket 的公私钥,格式如下: + +``` +[default] +aws_access_key_id = TOKEN_*****9206d +aws_secret_access_key = 93614*******b1dc40 +``` + +执行挂载命令 `./goofys --endpoint your_ufile_endpoint your_bucket your_local_mount_dir`, 例如: + +``` +./goofys --endpoint http://internal.s3-cn-bj.example.com/suning2 ./mount_test: +``` + +挂载效果如图: + + + +测试挂载是否成功, 可以拷贝一个本地文件到 mount_test 目录, 看是否上传到 US3。 + +### 其它操作(删除,上传,获取,卸载) + +同 s3fs, 可参考上面的 s3fs 操作 + +### 性能数据 + +4核8G 的 UHost 虚拟机, 上传 500MB 以上的文件, 平均速度可达140MB/s + +## 基于 US3 的 FTP 服务 + +### 功能说明 + +对象存储支持通过 FTP 协议直接操作 Bucket 中的对象和目录,包括上传文件、下载文件、删除文件(不支持进入文件夹)。 + +### 安装和使用 + +**适用的操作系统:Linux** + +### 安装步骤 + +#### 搭建环境 + +使用 s3fs 工具将 Bucket 挂载到本地。具体安装方式步骤参考基于 S3FS、US3 搭建**网络文件系统**的内容。 + +#### 安装依赖项 + +先检查下本地是否有 FTP 服务,执行命令 `rpm -qa | grep vsftpd`,如果显示未安装,则执行以下命令,安装 FTP。 + +运行以下命令安装 vsftpd。 + +``` +yum install -y vsftpd +``` + +#### 开启本地 fpd 服务 + +执行以下命令,开启 ftp 服务。 + +``` +service vsftpd start +``` + +### S3FS 使用方法 + +#### 添加账户 + +1. 运行以下命令创建 ftptest 用户,并且设置指定目录。 + + useradd ${username} -d {SpecifiedDirectory} + + (删除用户命令:sudo userdel -r newuser) + +2. 运行以下命令修改 ftptest 用户密码。 + + passwd ${username} + +#### 客户端使用 + +此时,您可以在外部任何一台机器上连接该服务器,输入您的用户名和密码,来管理 bucket 的文件 + +``` +ftp ${ftp_server_ip} +``` + +## s3cmd + +### 功能说明 + +s3cmd是一个免费的命令行工具,用于使用S3协议上传、检索和管理数据,它最适合熟悉命令行程序的用户,广泛用于批处理脚本和自动备份。 + +### 安装和使用 + +**适用的操作系统:Linux、MacOS、Windows** + +#### 安装步骤 +``` +1.下载安装包 +https://s3tools.org/download ,这里以目前最新版本2.1.0为例 +2.解压安装包 +tar xzvf s3cmd-2.1.0.tar.gz +3.移动路径 +mv s3cmd-2.1.0 /usr/local/s3cmd +4.创建软连接 +ln -s /usr/local/s3cmd/s3cmd /usr/bin/s3cmd (权限不足可以使用sudo) +5.执行配置命令,填写必要信息(直接跳过也可以,可以放在下一步手动填写) +s3cmd --configure +6.填写配置 +vim ~/.s3cfg +打开当前配置,填写以下参数 +access_key = "TOKEN公钥/API公钥" +secret_key = "TOKEN私钥/API私钥" +host_base = "s3协议域名,例如: s3-cn-bj.example.com" +host_bucket = "请求风格,例如: %(bucket)s.s3-cn-bj.example.com" +multipart_chunk_size_mb = 8 "us3 支持的s3协议分片大小为8M,所以这里只能填8" + +``` + + +#### 示例配置项 + +``` +[default] +access_key = "TOKEN_xxxxxxxxx" +access_token = +add_encoding_exts = +add_headers = +bucket_location = US +check_ssl_certificate = True +check_ssl_hostname = True +connection_pooling = True +content_disposition = +content_type = +default_mime_type = binary/octet-stream +delay_updates = False +delete_after = False +delete_after_fetch = False +delete_removed = False +dry_run = False +enable_multipart = True +encrypt = False +expiry_date = +expiry_days = +expiry_prefix = +follow_symlinks = False +force = False +get_continue = False +gpg_passphrase = +guess_mime_type = True +host_base = s3-cn-bj.example.com +host_bucket = %(bucket)s.s3-cn-bj.example.com +human_readable_sizes = False +invalidate_default_index_on_cf = False +invalidate_default_index_root_on_cf = True +invalidate_on_cf = False +kms_key = +limit = -1 +limitrate = 0 +list_md5 = False +log_target_prefix = +long_listing = False +max_delete = -1 +mime_type = +multipart_chunk_size_mb = 8 +multipart_max_chunks = 10000 +preserve_attrs = True +progress_meter = True +proxy_host = +proxy_port = 80 +public_url_use_https = False +put_continue = False +recursive = False +recv_chunk = 65536 +reduced_redundancy = False +requester_pays = False +restore_days = 1 +restore_priority = Standard +secret_key = "xxxxxxxxxxxxxxxxxxx" +send_chunk = 65536 +server_side_encryption = False +signature_v2 = False +signurl_use_https = False +simpledb_host = sdb.amazonaws.com +skip_existing = False +socket_timeout = 300 +stats = False +stop_on_error = False +storage_class = +throttle_max = 100 +upload_id = +urlencoding_mode = normal +use_http_expect = False +use_https = False +use_mime_magic = True +verbosity = WARNING +website_index = index.html +``` +#### 使用方法 + +##### 1.上传文件 +``` +s3cmd put test.txt s3://bucket1 +``` + +##### 2.删除文件 +``` +s3cmd del s3://bucket1/test.txt +``` + +##### 3.下载文件 +``` +s3cmd get s3://bucket1/test.txt +``` + +##### 4.拷贝文件 +``` +s3cmd cp s3://bucket1/test.txt s3://bucket2/test.txt +``` +##### 其他常用操作 +``` +1.上传文件夹 +s3cmd put -r ./dir s3://bucket1/dir1 +2.下载文件夹 +s3cmd get -r s3://bucket1/dir1 ./dir1 +3.删除文件夹 +s3cmd del s3://bucket1/dir1 +4.列取bucket列表 +s3cmd ls +5.列取文件列表 +s3cmd ls s3://bucket1 +6.归档文件取回 +s3cmd restore s3://bucket1 +``` + +## rclone + +### 功能说明 + +rclone是一个命令行程序,用于管理云存储上的文件,支持s3协议 + +### 安装和使用 + +#### 安装步骤 + +``` +curl https://rclone.org/install.sh | sudo bash + +//参考 https://rclone.org/install/ +``` + +#### 配置 + +``` +rclone config +``` + +#### 配置参考 + +``` +[s3] //这里可以填写s3等自定义名,作为命令前缀 +type = s3 +provider = Other +env_auth = false +access_key_id = xxxxxxxx +secret_access_key = xxxxxxxxxxx +endpoint = http://s3-cn-bj.example.com //参考 +location_constraint = cn-bj +acl = private +bucket_acl = private // public/private +chunk_size = 8M //目前只支持8M分片 +``` + + +#### 使用方法 + +说明: 以下命令前缀(配置内的中括号内容)以remote为例,使用过程中需要自行修改 + +##### 1.查看所有bucket + +``` +rclone lsd remote: +``` + +##### 2.列取文件列表 + +``` +rclone ls remote:bucket +``` + +##### 3.上传文件 + +``` +rclone copy ./test.txt remote:bucket +``` + +##### 4.删除文件 + +``` +rclone delete remote:bucket/test.txt +``` + diff --git "a/collections/_node.js-sdk/S3\345\215\217\350\256\256\346\224\257\346\214\201\350\257\264\346\230\216.md" "b/collections/_node.js-sdk/S3\345\215\217\350\256\256\346\224\257\346\214\201\350\257\264\346\230\216.md" new file mode 100644 index 0000000..d45a109 --- /dev/null +++ "b/collections/_node.js-sdk/S3\345\215\217\350\256\256\346\224\257\346\214\201\350\257\264\346\230\216.md" @@ -0,0 +1,176 @@ +--- +title: 'AWS S3 协议支持说明' +sidebar: + nav: node.js-sdk +--- + + +## 概述 + +S3协议是AWS推出,在对象存储行业成为事实标准,US3产品在自有标准的基础上,增加了针对S3 v4协议标准的兼容支持。 + + + + +### 支持的 API + +US3 目前的 S3 协议模块对标准 S3 协议的支持如下表: + +| **编号** | **API名字** | **备注说明** | +| :------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | +| 1 | [HeadBucket](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadBucket.html) | 检测 Bucket 是否存在以及您是否有权限访问该 Bucket | +| 2 | [ListBuckets](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListBuckets.html) | 获取 Bucket 列表,只能获取公私钥或者 Token 拥有者创建的 Bucket | +| 3 | [GetBucketLocation](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLocation.html) | 返回所在地域名,不建议依赖该 API | +| 4 | [GetBucketAcl](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketAcl.html) | 没有太多意义,主要为了支持 S3 Browser 而实现,响应体中的 `Permission` 字段永远为 `FULL_CONTROL` | +| 5 | [GetBucketVersioning](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketVersioning.html) | 没有太多意义,主要为了支持 S3 Browser 而实现,响应体中的 `Status` 字段永远为空字符串 | +| 6 | [PutBucketLifecycleConfiguration](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketLifecycleConfiguration.html) | 为 Bucket 创建新的生命周期配置或替换现有的生命周期配置规则。注意,这将覆盖现有的所有生命周期配置规则 | +| 7 | [GetBucketLifecycleConfiguration](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLifecycleConfiguration.html) | 获取 Bucket 中设置的生命周期配置规则 | +| 8 | [DeleteBucketLifecycle](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketLifecycle.html) | 删除 Bucket 中设置的所有生命周期配置规则。注意,不支持删除 Bucket 中的指定某个或多个生命周期配置规则 | +| 9 | [GetObjectAcl](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObjectAcl.html) | 获取 Object 的访问权限信息 | +| 10 | [PutObjectAcl](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObjectAcl.html) | 设置 Object 的访问权限信息 | +| 11 | [HeadObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html) | 从对象中检索元数据,但不返回对象本身 | +| 12 | [PutObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html) | 向 Bucket 中放置对象 | +| 13 | [PostObject](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPOST.html) | 使用 HTML 表单将对象添加到指定的存储桶 | +| 14 | [CopyObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html) | 创建已存储在 Bucket 中的对象的副本 | +| 15 | [GetObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html) | 从 Bucket 中检索对象并返回 | +| 16 | [ListObjects](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjects.html)/[ListObjectsV2](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html) | 返回 Bucket 中的部分或全部对象 | +| 17 | [DeleteObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObject.html)/[DeleteObjects](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html) | 从 Bucket 中删除对象 | +| 18 | [CreateMultipartUpload](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateMultipartUpload.html) | 启动分片上传并返回上传 ID | +| 19 | [UploadPart](https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPart.html) | 在分片上传中上传一部分。必须先启动分片上传。 | +| 20 | [UploadPartCopy](https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPartCopy.html) | 通过从现有对象复制数据作为数据源来上传部分,必须先启动分片上传。 | +| 21 | [CompleteMultipartUpload](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html) | 通过组装先前上传的部分来完成分片上传。 | +| 22 | [AbortMultipartUpload](https://docs.aws.amazon.com/AmazonS3/latest/API/API_AbortMultipartUpload.html) | 中止分片上传。分片上传中止后,将无法使用该上传 ID 上传其他部分。 | +| 23 | [ListMultipartUploads](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListMultipartUploads.html) | 获取正在执行的分片上传请求 ID | +| 24 | [ListParts](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListParts.html) | 获取正在执行分片上传的分片信息 | +| 25 | [RestoreObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_RestoreObject.html) | 解冻处于归档状态的文件 | + +注意: + +* PutObject 目前仅支持 1GB 大小文件,如果需要上传大于 1GB 的文件,请采用分片上传的 API + +* PostObject 目前仅支持最大 32MB 文件的上传 + +* CopyObject 目前仅支持最大 100MB 文件的拷贝 + +* UploadPart 目前仅支持 8MB 定长分片大小(最后一个分片允许小于 8MB)。若有不定长分片的需求,请联系技术支持 + +* US3 S3 对 AWS S3 兼容的存储类型及其转换规则参考 [存储类型转换规则](#存储类型转换规则) + +* US3 的 ETag 计算方式与 AWS S3 存在部分差异,建议不依赖该 ETag + +* 目前不支持 S3 API 的 MD5 校验,建议关闭: + + 例如AWS S3 Java SDK: + System.setProperty(SkipMd5CheckStrategy.DISABLEGETOBJECTMD5VALIDATION_PROPERTY,""); + + System.setProperty(SkipMd5CheckStrategy.DISABLEPUTOBJECTMD5VALIDATION_PROPERTY,""); + +* US3 的访问权限(ACL)定义与 AWS S3 存在差异,具体参考 [访问权限定义(ACL)](#访问权限定义(acl)) + +* 目前文件访问权限控制 API(GetObjectAcl、PutObjectAcl)仅在部分地域支持 + +* 目前生命周期配置规则控制 API(PutBucketLifecycleConfiguration、GetBucketLifecycleConfiguration、DeleteBucketLifecycle)仅在部分地域支持 + +* 目前 UploadPartCopy 处于内测阶段。若有使用需求,请联系技术支持 + +* 目前不支持多版本功能(Versioning) + +* 目前不支持标签功能(Tagging) + +* ListObjects请求中的max-keys参数(请求返回对象的最大数量)最大值为5000 + +### 访问权限定义(ACL) + +| US3 ACL | [AWS S3 Canned ACL](https://docs.aws.amazon.com/AmazonS3/latest/userguide/acl-overview.html#canned-acl) | +| ----------------- | ----------------------------------------------------------------------------------------------------------- | +| private | private | +| public-read | public-read | +| public-read-write | public-read-write | +| 不支持 | aws-exec-read
authenticated-read
bucket-owner-read
bucket-owner-full-control
log-delivery-write | + +### 仅支持签名 V4 + +支持 V4 签名的场景: + +1. URL 中携带参数(URL Query 部分的 x-amz-credential 字段); + +2. POST(表单中 x-amz-credential 域); + +3. Header 中携带参数(Authorization字段); + +### S3 的 AccessKeyID 和 SecretAccessKey 说明 + +S3 的 AccessKeyID(或称AccessKey)和 SecretAccessKey(或称SecretKey)对应就是 {{channelName}} 的 API 公钥和私钥,或者是 US3服务提供的 Token 公钥和 Token 私钥; + +**注意:要求无论是 API 公私钥还是 Token 公私钥,要求操作的 bucket,必须满足以下条件:** + +* 创建该 bucket 的账户与 API 公私钥的拥有者必须一致; + +* 创建该 bucket 的账户与创建 Token 的账户必须一致; + +### S3的分片大小说明 + +1. 为了达到更好的传输性能,默认情况下仅支持8M大小的分片。 +2. 部分地域已开通动态分片功能,如果固定8M分片无法满足需求,可联系技术支持开通动态分片。 + +### API支持路径风格和虚拟主机风格 + +**路径风格格式为: `http://\${Endpoint}/\${bucket名字}/\${key名字}`,bucket 名字作为路径使用的一部分。** +例如,AWS S3 Java SDK 在 {{channelName}} 北京地域走外网使用 US3 S3 服务则设置如下: + + + "AWSCredential credentials = new BasicAWSCredentials(ACCESS_KEY, + SECRET_KEY); + ClientConfiguration clientConfig = new ClientConfiguration(); + ... + S3ClientOptions clientOptions = S3ClientOptions.builder().build(); + clientOptions.setPathStyleAccess(true); // 表明使用路径风格API + AmazonS3 conn = new AmazonS3Client(credential, clientConfig); + conn.setS3ClientOptions(clientOptions); + conn.setEndpoint("s3-cn-bj.example.com");" + +**虚拟主机风格: http://${bucket名字}.${Endpoint}/${key名字},类似US3目前使用的URL形式。** + +## 访问域名(Endpoint) +访问域名的一般语法如下: + + **`protocol://s3-.`** + +例如,http://s3-cn-sh2.example.com 是中国上海地区的 example S3 服务的外网访问域名。其中,**s3** 是AWS S3服务的``, **cn-sh2** 是``,**.example.com** 是 ``。 + +对于内网访问域名,可以使用如下格式: + +**`protocol://internal.s3-.`** + +内网的 **service_code** 前需加上 内网访问标识符 **internal.** +即上述示例访问域名改为 http://internal.s3-cn-sh2.example.com + + +**注意:** +目前华北一,香港,胡志明,首尔,圣保罗,洛杉矶,华盛顿地域已经支持https协议,其他地域可支持路径风格https,后续支持虚拟主机风格https (所有地域内网不支持https) + +### 关于 region_code.custom_domain 的说明 +进入控制台的对象存储模块,在单地域空间管理分页下找到以下标签`` +该标签由 `..`组成 + + +## 回调扩展功能支持 + +| **请求形式 API 名字** | **PUT Object** | **POST Object** | **Complete Multipart Upload** | +| ---------------------------------------------- | -------------- | --------------- | ----------------------------- | +| **在 URL 中携带参数** | √ | × | √ | +| **在 Header 中携带参数** | √ | × | √ | +| **在 POST 请求的 body 中使用表单域来携带参数** | × | √ | × | + + √:支持 + ×:不支持 + + + +## 存储类型转换规则 + +| **US3存储类型** | **S3存储类型** | **US3对应S3默认存储类型** | +| --------------- | --------------------------------------------------------- | ------------------------- | +| STANDARD | STANDARD
STANDARD_IA | STANDARD | +| IA | ONEZONE_IA
INTELLIGENT_TIERING
REDUCED_REDUNDANCY | ONEZONE_IA | +| ARCHIVE | GLACIER
DEEP_ARCHIVE | GLACIER | diff --git a/collections/_node.js-sdk/img/cors.png b/collections/_node.js-sdk/img/cors.png new file mode 100644 index 0000000000000000000000000000000000000000..7d35479e399c8a41693a922ee82b8886bfe49bd1 GIT binary patch literal 60980 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
G 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 上传分片 -> 完成分片(中止分片)**三个阶段。 + + +### 参数说明 +- `Bucket`: 文件上传后所在的存储空间 +- `Key`: 文件上传后在存储空间里的名称 +- `UploadId`: 用于关联所有分片的唯一标识符 +- `PartNumber`: 当前上传的分片编号 +- `Body`: 要上传的文件分片的内容 +- `ContentMD5`: 指定分片数据的 MD5 校验值 + +### 示例 +> 执行该示例前请确保配置文件的正确性
以下代码段需要在上下文中运行 + +
+{% highlight javascript linenos %} +const { + CreateMultipartUploadCommand, + UploadPartCommand, + CompleteMultipartUploadCommand, + AbortMultipartUploadCommand +} = require("@aws-sdk/client-s3"); +const s3 = require('./s3client'); +const fs = require('fs'); +const crypto = require('crypto'); + +// 计算 MD5 +const calculateMD5 = (data) => { + return crypto.createHash('md5').update(data).digest('base64'); +}; + +const sliceUpload = async (bucketName, key, filePath) => { + const fileBuffer = fs.readFileSync(filePath); + const partSize = 8 * 1024 * 1024; // 固定8MB + const numParts = Math.ceil(fileBuffer.length / partSize); + + let uploadId; + + try { + + // 创建分片上传任务 + const createMultipartUploadResponse = await s3.send( + new CreateMultipartUploadCommand({ + Bucket: bucketName, + Key: key, + }) + ); + + uploadId = createMultipartUploadResponse.UploadId; + const uploadPromises = []; + + // 分片上传 + for (let partNumber = 1; partNumber <= numParts; partNumber++) { + const start = (partNumber - 1) * partSize; + const end = Math.min(start + partSize, fileBuffer.length); + const partBuffer = fileBuffer.subarray(start, end); + const md5Hash = calculateMD5(partBuffer); + + const uploadPartCommand = new UploadPartCommand({ + Bucket: bucketName, + Key: key, + UploadId: uploadId, + PartNumber: partNumber, + Body: partBuffer, + ContentMD5: md5Hash, + }); + + uploadPromises.push( + s3.send(uploadPartCommand).then((uploadPartResponse) => ({ + ETag: uploadPartResponse.ETag, + PartNumber: partNumber, + })) + ); + } + const uploadedParts = await Promise.all(uploadPromises); + + // 验证每个分片是否正确上传 + uploadedParts.forEach((part) => { + if (!part.ETag) { + throw new Error(`Part ${part.PartNumber} failed to upload.`); + } + }); + + // 完成分片上传 + await s3.send( + new CompleteMultipartUploadCommand({ + Bucket: bucketName, + Key: key, + UploadId: uploadId, + MultipartUpload: { + Parts: uploadedParts, + }, + }) + ); + + console.log(`Successfully uploaded ${key} to ${bucketName}`); + } catch (error) { + console.error("Error uploading file:", error); + + if (uploadId) { + + // 如果上传失败,可以选择在此处中止上传 + await s3.send( + new AbortMultipartUploadCommand({ + Bucket: bucketName, + Key: key, + UploadId: uploadId, + }) + ); + } + } +}; +{% endhighlight %} +
+ +> 在 `Example/` 目录中运行以下命令执行该示例 +
+{% highlight bash linenos %} +$ node SliceUpload.js +{% endhighlight %} +
diff --git "a/collections/_node.js-sdk/\345\210\240\351\231\244\346\226\207\344\273\266.md" "b/collections/_node.js-sdk/\345\210\240\351\231\244\346\226\207\344\273\266.md" new file mode 100644 index 0000000..fa872ca --- /dev/null +++ "b/collections/_node.js-sdk/\345\210\240\351\231\244\346\226\207\344\273\266.md" @@ -0,0 +1,53 @@ +--- +title: '删除文件' +sidebar: + nav: node.js-sdk +--- +本SDK提供`DeleteObjectsCommand`类实现文件删除。完整代码详见 [Github](https://github.com/aws/aws-sdk-js-v3/tree/main/clients/client-s3/src/commands/DeleteObjectsCommand.ts) 。 +`DeleteObjectsCommand`调用的 S3 API 为 DeleteObjects, 具体参见[DeleteObjects API 文档](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html)。 + + + + +### 参数说明 +- `Bucket`: 文件所在的存储空间 +- `Key`: 文件在存储空间内的名称 + + + +### 示例 +> 执行该示例前请确保配置文件的正确性
以下代码段需要在上下文中运行 + +
+{% highlight javascript linenos %} +const s3 = require('./s3client'); +const { DeleteObjectsCommand } = require("@aws-sdk/client-s3"); + +async function deleteS3Objects(bucketName, objectKeys) { + try { + const deleteParams = { + Bucket: bucketName, + Delete: { + Objects: objectKeys.map(key => ({ Key: key })) + } + }; + + const command = new DeleteObjectsCommand(deleteParams); + const response = await s3.send(command); + console.log(`Successfully deleted ${response.Deleted.length} objects from S3 bucket. Deleted objects:`); + console.log(response.Deleted.map(d => ` • ${d.Key}`).join("\n")); + } catch (err) { + console.error("Error deleting objects:", err); + } +} + +{% endhighlight %} +
+ +> 在 `Example/` 目录中运行以下命令执行该示例 + +
+{% highlight bash linenos %} +$ node DeleteObject.js ... +{% endhighlight %} +
diff --git "a/collections/_node.js-sdk/\345\255\230\345\202\250\347\261\273\345\236\213\350\275\254\346\215\242.md" "b/collections/_node.js-sdk/\345\255\230\345\202\250\347\261\273\345\236\213\350\275\254\346\215\242.md" new file mode 100644 index 0000000..bb4a1f8 --- /dev/null +++ "b/collections/_node.js-sdk/\345\255\230\345\202\250\347\261\273\345\236\213\350\275\254\346\215\242.md" @@ -0,0 +1,65 @@ +--- +title: '存储类型转换' +sidebar: + nav: node.js-sdk +--- +本SDK提供`CopyObjectCommand`类实现存储类型转换,允许在不移动或重新上传文件的情况下更新对象的元数据。完整代码详见 [Github](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/src/commands/CopyObjectCommand.ts) 。 +`CopyObjectCommand`调用的 S3 API 为 CopyObject, 具体参见[CopyObject API 文档](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html)。 + + + +### 参数说明 +- `Bucket`: 文件所在的存储空间 +- `CopySource`: 存储空间内的文件路径 +- `Key`: 文件在存储空间内的名称 +- `StorageClass`: S3 存储类型 +- `MetadataDirective`: 复制原有元数据("COPY")或替换为新的元数据("REPLACE") + +### 存储类型转换规则 + +| US3存储类型 | S3存储类型 | US3对应S3默认存储类型 | +| ----------------- | ---------------------------------------------- | -------------------- | +| STANDARD | STANDARD
STANDARD_IA | STANDARD | +| IA | ONEZONE_IA
INTELLIGENT_TIERING
REDUCED_REDUNDANCY | ONEZONE_IA | +| ARCHIVE | GLACIER
DEEP_ARCHIVE | GLACIER | + + + + +### 示例 +> 执行该示例前请确保配置文件的正确性
以下代码段需要在上下文中运行 + +
+{% highlight javascript linenos %} +const s3 = require('./s3Client'); +const { CopyObjectCommand } = require("@aws-sdk/client-s3"); +async function updateStorageClass(bucketName, keyName, storageClass) { + try { + const copySource = `${bucketName}/${keyName}`; + const params = { + Bucket: bucketName, + CopySource: copySource, + Key: keyName, + StorageClass: storageClass, + MetadataDirective: 'COPY' + }; + const command = new CopyObjectCommand(params); + const response = await s3.send(command); + console.log("Storage class updated successfully", response); + return response; + } catch (err) { + console.error("Error updating storage class", err); + throw err; + } +} + +{% endhighlight %} +
+ +> 在 `Example/` 目录中运行以下命令执行该示例 + +
+{% highlight bash linenos %} +$ node PutObjectUpdate.js +{% endhighlight %} +
diff --git "a/collections/_node.js-sdk/\345\256\211\350\243\205.md" "b/collections/_node.js-sdk/\345\256\211\350\243\205.md" new file mode 100644 index 0000000..82f20f5 --- /dev/null +++ "b/collections/_node.js-sdk/\345\256\211\350\243\205.md" @@ -0,0 +1,26 @@ +--- +title: '安装' +sidebar: + nav: node.js-sdk +--- +## 环境准备 +AWS SDK for JavaScript v3,推荐使用 ES6+ 版本的 JavaScript。在 Node.js 环境中,版本为 16.x 或更高。浏览器需支持基本的 HTML5 特性(支持 IE10 以上浏览器) + +## 下载SDK源码 + +* [通过Github下载](https://github.com/aws/aws-sdk-js-v3) + +## 安装SDK示例 +* [SDK示例下载](https://github.com/ufilesdk-dev/S3-Node.JS-SDK) + +### 本地安装 + +
+{% highlight bash linenos %} +$ git clone https://github.com/ufilesdk-dev/S3-Node.JS-SDK.git +$ cd S3-Node.JS-SDK +$ nvm install 18 # Mac可执行 $ brew install nvm 安装nvm +$ nvm use 18 +$ npm install +{% endhighlight %} +
diff --git "a/collections/_node.js-sdk/\345\277\253\351\200\237\344\275\277\347\224\250.md" "b/collections/_node.js-sdk/\345\277\253\351\200\237\344\275\277\347\224\250.md" new file mode 100644 index 0000000..5b704e5 --- /dev/null +++ "b/collections/_node.js-sdk/\345\277\253\351\200\237\344\275\277\347\224\250.md" @@ -0,0 +1,80 @@ +--- +title: '快速使用' +sidebar: + nav: node.js-sdk +--- + +## 快速运行 SDK 示例 +### 1. 准备工作 +1. 登录[对象存储控制台](https://console.ucloud.cn/ufile/ufile),创建存储空间。获取存储空间名称和存储空间域名。 +2. 登录[账号管理控制台](https://console.ucloud.cn/uaccount/api_manage),获取您的项目公钥和私钥。 +3. 配置CORS 规则,来源Origin可以按需配置,Allow-Header 需配成*,Expose-Headers 需要 ETag、Content-Length 以及其他 js 需要读取的 header 字段,如下图所示。 +![image-cors](img/cors.png) + +### 2. 配置s3客户端文件 + 确保已完成跨域设置,修改s3client.js 文件 + + +
+{% highlight javascript linenos %} +const { S3Client } = require("@aws-sdk/client-s3"); + +const credentials = { + accessKeyId: "", + secretAccessKey: "" +}; + +const s3 = new S3Client({ + endpoint: "", // http://s3-cn-sh2.ufileos.com + region: "", // cn-sh2 + signatureVersion: 'v4', + forcePathStyle: true, + credentials: credentials, +}); +module.exports = s3; +{% endhighlight %} +
+ + +* 参数说明 + +| 参数名 | 参数描述 | 类型 | 是否必填 | +| ----------------- |--------------------------------------| -------- | -------- | +| accessKeyId | UCloud 的 API 公钥或者是 US3服务提供的 Token 公钥 | String | 是 | +| secretAccessKey | UCloud 的 API 私钥或者是 US3服务提供的 Token 私钥 | String | 是 | +| endpoint | 访问域名,具体可参考 AWS S3 协议支持说明 | String | 是 | +| region | 存储空间所在地域 | String | 是 | +| signatureVersion | AWS签名版本,默认使用签名版本 4 来验证请求 | String | 否 | +| forcePathStyle | 使用路径风格或虚拟主机风格,具体参考 AWS S3 协议支持说明 | Boolean | 否 | + +### 3. 运行示例 +首先确保已完成SDK示例的本地安装。 +以文件上传为例,必须的输入参数为配置文件对应地域下的`Bucket Name`,以及需要保存在该Bucket 下的`Key Name` (即文件名) ,存储类型默认为标准存储。若要指定存储类型,参照**S3协议支持说明**,确认完输入信息后点击上传文件。 + +
+{% highlight bash linenos %} +$ cd Examples/ +$ node PutObject.js [storageClass] + + +{% endhighlight %} +
+ +如果浏览器控制台出现了CORS 相关的错误信息,请检查是否正确完成跨域配置。 + +### 注意 +* PutObject 目前仅支持 5GB 大小文件,如果需要上传大于 5GB 的文件,请采用分片上传的 API +* PostObject 目前仅支持最大 32MB 文件的上传 +* CopyObject 目前仅支持最大 5G文件的拷贝 +* UploadPart 目前仅支持 8MB 定长分片大小(最后一个分片允许小于 8MB)。若有不定长分片的需求,请联系技术支持 +* US3 S3 对 AWS S3 兼容的存储类型及其转换规则参考 存储类型转换规则 +* US3 的 ETag 计算方式与 AWS S3 存在部分差异,建议不依赖该 ETag +* 目前不支持 S3 API 的 MD5 校验,建议关闭 +* US3 的访问权限(ACL)定义与 AWS S3 存在差异,具体参考 访问权限定义(ACL) +* 目前不支持多版本功能(Versioning) +* 目前不支持标签功能(Tagging) +* ListObjects请求中的max-keys参数(请求返回对象的最大数量)最大值为5000 +* 更详细的内容请查看 AWS S3 协议支持说明 + + + diff --git "a/collections/_node.js-sdk/\346\213\267\350\264\235\346\226\207\344\273\266.md" "b/collections/_node.js-sdk/\346\213\267\350\264\235\346\226\207\344\273\266.md" new file mode 100644 index 0000000..b064801 --- /dev/null +++ "b/collections/_node.js-sdk/\346\213\267\350\264\235\346\226\207\344\273\266.md" @@ -0,0 +1,53 @@ +--- +title: '拷贝文件' +sidebar: + nav: node.js-sdk +--- +本SDK提供`CopyObjectCommand`类用于拷贝文件,完整代码详见 [Github](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/src/commands/CopyObjectCommand.ts) 。 +`CopyObjectCommand`调用的 S3 API 为 CopyObject, 具体参见[CopyObject API 文档](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html)。 + + + +### 参数说明 +- `sourceBucket`: 源文件所在的存储空间 +- `sourceKey`: 源文件所在存储空间里的名称 +- `destinationBucket`: 目标存储空间 +- `destinationKey`: 文件在目标存储空间里的名称 + + +### 示例 +> 执行该示例前请确保配置文件的正确性
以下代码段需要在上下文中运行 + +
+{% highlight javascript linenos %} +const s3 = require('./s3Client'); +const { PutObjectCommand, CopyObjectCommand } = require("@aws-sdk/client-s3"); + +async function copyFile(sourceBucket, sourceKey, destinationBucket, destinationKey) { + try { + const copySource = `${sourceBucket}/${sourceKey}`; + const params = { + CopySource: copySource, + Bucket: destinationBucket, + Key: destinationKey + }; + const command = new CopyObjectCommand(params); + const response = await s3.send(command); + console.log("File copied successfully", response); + return response; + } catch (err) { + console.error("Error copying file", err); + throw err; + } +} + + +{% endhighlight %} +
+ +> 在 `Example/` 目录中运行以下命令执行该示例 +
+{% highlight bash linenos %} +$ node PutObjectCopy.js +{% endhighlight %} +
\ No newline at end of file diff --git "a/collections/_node.js-sdk/\346\214\207\345\256\232\345\255\230\345\202\250\347\261\273\345\236\213\344\270\212\344\274\240.md" "b/collections/_node.js-sdk/\346\214\207\345\256\232\345\255\230\345\202\250\347\261\273\345\236\213\344\270\212\344\274\240.md" new file mode 100644 index 0000000..e1ba230 --- /dev/null +++ "b/collections/_node.js-sdk/\346\214\207\345\256\232\345\255\230\345\202\250\347\261\273\345\236\213\344\270\212\344\274\240.md" @@ -0,0 +1,62 @@ +--- +title: '指定存储类型上传' +sidebar: + nav: node.js-sdk +--- +本SDK提供`PutObjectCommand`类用于指定存储类型上传操作,大文件(5G以上)请使用分片上传,完整代码详见 [Github](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/src/commands/PutObjectCommand.ts) 。 +`PutObjectCommand`调用的 S3 API 为 PutObject, 具体参见[PutObject API 文档](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html)。 + +### 参数说明 +- `Bucket`: 文件上传后所在的存储空间 +- `Key`: 文件上传后在存储空间里的名称 +- `Body`: 待上传的文件全路径 +- `StorageClass`: S3 存储类型 + +### 存储类型转换规则 + +| US3存储类型 | S3存储类型 | US3对应S3默认存储类型 | +| ----------------- | ---------------------------------------------- | -------------------- | +| STANDARD | STANDARD
STANDARD_IA | STANDARD | +| IA | ONEZONE_IA
INTELLIGENT_TIERING
REDUCED_REDUNDANCY | ONEZONE_IA | +| ARCHIVE | GLACIER
DEEP_ARCHIVE | GLACIER | + + + + + +### 示例 +> 执行该示例前请确保配置文件的正确性
以下代码段需要在上下文中运行 + +
+{% highlight javascript linenos %} +const s3 = require('./s3Client'); +const { PutObjectCommand } = require("@aws-sdk/client-s3"); +const fs = require('fs'); + +async function uploadFile(bucketName, keyName, filePath) { + try { + const fileContent = fs.readFileSync(filePath); + const params = { + Bucket: bucketName, + Key: keyName, + Body: fileContent + }; + + const command = new PutObjectCommand(params); + const response = await s3.send(command); + console.log("File uploaded successfully", response); + return response; + } catch (err) { + console.error("Error uploading file", err); + throw err; + } +} + +{% endhighlight %} +
+> 在 `Example/` 目录中运行以下命令执行该示例 +
+{% highlight bash linenos %} +$ node PutObject.js +{% endhighlight %} +
diff --git "a/collections/_node.js-sdk/\346\226\207\344\273\266\344\270\213\350\275\275.md" "b/collections/_node.js-sdk/\346\226\207\344\273\266\344\270\213\350\275\275.md" new file mode 100644 index 0000000..9575ef5 --- /dev/null +++ "b/collections/_node.js-sdk/\346\226\207\344\273\266\344\270\213\350\275\275.md" @@ -0,0 +1,66 @@ +--- +title: '文件下载' +sidebar: + nav: node.js-sdk +--- +本SDK提供`GetObjectCommand`类实现文件下载。完整代码详见 [Github](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/src/commands/GetObjectCommand.ts) 。 +`GetObjectCommand`调用的 S3 API 为 GetObject, 具体参见[GetObject API 文档](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html)。 + + + +### 参数说明 +- `Bucket`: 文件所在的存储空间 +- `Key`: 文件在存储空间内的名称 + + + +### 示例 +> 执行该示例前请确保配置文件的正确性
以下代码段需要在上下文中运行 + +
+{% highlight javascript linenos %} +const s3 = require('./s3Client'); +const { GetObjectCommand } = require("@aws-sdk/client-s3"); +const fs = require('fs'); +const path = require('path'); + +async function downloadFile(bucketName, keyName, downloadPath) { + try { + const params = { + Bucket: bucketName, + Key: keyName + }; + const command = new GetObjectCommand(params); + const response = await s3.send(command); + const stream = response.Body; + + const filePath = path.resolve(downloadPath, keyName); + const writeStream = fs.createWriteStream(filePath); + + stream.pipe(writeStream); + + return new Promise((resolve, reject) => { + writeStream.on('finish', () => { + console.log("File downloaded successfully to", filePath); + resolve(filePath); + }); + writeStream.on('error', (err) => { + console.error("Error downloading file", err); + reject(err); + }); + }); + } catch (err) { + console.error("Error downloading file", err); + throw err; + } +} + +{% endhighlight %} +
+ +> 在 `Example/` 目录中运行以下命令执行该示例 +
+{% highlight bash linenos %} +$ node GetObject.js +{% endhighlight %} +
diff --git "a/collections/_node.js-sdk/\346\246\202\350\277\260.md" "b/collections/_node.js-sdk/\346\246\202\350\277\260.md" new file mode 100644 index 0000000..ee27a27 --- /dev/null +++ "b/collections/_node.js-sdk/\346\246\202\350\277\260.md" @@ -0,0 +1,29 @@ +--- +title: '概述' +sidebar: + nav: node.js-sdk +--- + +## US3基本概念 + +在对象存储系统中,存储空间(Bucket)是文件(File)的组织管理单位,文件(File)是存储空间的逻辑存储单元。对于每个账号,该账号里存放的每个文件都有唯一的一对存储空间(Bucket)与键(Key)作为标识。我们可以把 Bucket 理解成一类文件的集合,Key 理解成文件名。由于每个 Bucket 需要配置和权限不同,每个账户里面会有多个 Bucket。在 US3 里面,Bucket 主要分为公有和私有两种,公有 Bucket 里面的文件可以对任何人开放,私有 Bucket 需要配置对应访问签名才能访问。 + + + +## 示例程序 +[SDK示例下载](https://github.com/ufilesdk-dev/S3-Node.JS-SDK) + +Examples文件夹下提供了示例程序: + +| 示例文件 | 示例内容 | +| -------- | -------- | +| [PutObject.js](https://github.com/summerboy2134/US3-Node.JS-SDK/blob/main/Examples/PutObject.js) | 简单上传、指定存储类型上传 | +| [PutObjectCopy.js](https://github.com/summerboy2134/US3-Node.JS-SDK/blob/main/Examples/PutObjectCopy.js) | 拷贝文件 | +| [PutObjectUpdate.js](https://github.com/summerboy2134/US3-Node.JS-SDK/blob/main/Examples/PutObjectUpdate.js) | 存储类型转换 | +| [RestoreObject.js](https://github.com/summerboy2134/US3-Node.JS-SDK/blob/main/Examples/RestoreObject.js) | 解冻归档文件 | +| [GetObject.js](https://github.com/summerboy2134/US3-Node.JS-SDK/blob/main/Examples/GetObject.js) | 文件下载 | +| [GetObjectAcl.js](https://github.com/summerboy2134/US3-Node.JS-SDK/blob/main/Examples/GetObjectAcl.js) | 获取对象权限信息 | +| [GetObjectAttr.js](https://github.com/summerboy2134/US3-Node.JS-SDK/blob/main/Examples/GetObjectAttr.js) | 获取对象元数据 | +| [ListObjects.js](https://github.com/summerboy2134/US3-Node.JS-SDK/blob/main/Examples/ListObjects.js) | 获取目录文件列表 | +| [SliceUpload.js](https://github.com/summerboy2134/US3-Node.JS-SDK/blob/main/Examples/SliceUpload.js) | 分片上传 | +| [DeleteObject.js](https://github.com/summerboy2134/US3-Node.JS-SDK/blob/main/Examples/DeleteObject.js) | 删除文件 | diff --git "a/collections/_node.js-sdk/\347\256\200\345\215\225\344\270\212\344\274\240.md" "b/collections/_node.js-sdk/\347\256\200\345\215\225\344\270\212\344\274\240.md" new file mode 100644 index 0000000..c0a0158 --- /dev/null +++ "b/collections/_node.js-sdk/\347\256\200\345\215\225\344\270\212\344\274\240.md" @@ -0,0 +1,52 @@ +--- +title: '简单上传' +sidebar: + nav: node.js-sdk +--- +本SDK提供`PutObjectCommand`类用于上传操作,大文件(5G以上)请使用分片上传,完整代码详见 [Github](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/src/commands/PutObjectCommand.ts) 。 +`PutObjectCommand`调用的 S3 API 为 PutObject, 具体参见[PutObject API 文档](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html)。 + +### 参数说明 +- `Bucket`: 文件上传后所在的存储空间 +- `Key`: 文件上传后在存储空间里的名称 +- `Body`: 待上传的文件全路径 + + + +### 示例 +> 执行该示例前请确保配置文件的正确性
以下代码段需要在上下文中运行 + + +
+{% highlight javascript linenos %} +const s3 = require('./s3Client'); +const { PutObjectCommand } = require("@aws-sdk/client-s3"); +const fs = require('fs'); + +async function uploadFile(bucketName, keyName, filePath) { + try { + const fileContent = fs.readFileSync(filePath); + const params = { + Bucket: bucketName, + Key: keyName, + Body: fileContent + }; + const command = new PutObjectCommand(params); + const response = await s3.send(command); + console.log("File uploaded successfully", response); + return response; + } catch (err) { + console.error("Error uploading file", err); + throw err; + } +} + +{% endhighlight %} +
+ +> 在 `Example/` 目录中运行以下命令执行该示例 +
+{% highlight bash linenos %} +$ node PutObject.js +{% endhighlight %} +
\ No newline at end of file diff --git "a/collections/_node.js-sdk/\350\216\267\345\217\226\345\257\271\350\261\241\345\205\203\346\225\260\346\215\256.md" "b/collections/_node.js-sdk/\350\216\267\345\217\226\345\257\271\350\261\241\345\205\203\346\225\260\346\215\256.md" new file mode 100644 index 0000000..ffdc2d4 --- /dev/null +++ "b/collections/_node.js-sdk/\350\216\267\345\217\226\345\257\271\350\261\241\345\205\203\346\225\260\346\215\256.md" @@ -0,0 +1,54 @@ +--- +title: '获取对象元数据' +sidebar: + nav: node.js-sdk +--- +本SDK提供`HeadObjectCommand`类用于获取对象元数据。完整代码详见 [Github](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/src/commands/HeadObjectCommand.ts) 。 +`HeadObjectCommand`调用的 S3 API 为 HeadObject, 具体参见[HeadObject API 文档](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html)。 + + + +### 参数说明 +- `Bucket`: 文件所在的存储空间 +- `Key`: 文件在存储空间内的名称 + + + + +### 示例 +> 执行该示例前请确保配置文件的正确性
以下代码段需要在上下文中运行 + +
+{% highlight javascript linenos %} +const s3 = require('./s3client'); +const { HeadObjectCommand } = require("@aws-sdk/client-s3"); + +async function getObjectAttributes(bucketName, keyName) { + try { + const params = { + Bucket: bucketName, + Key: keyName + }; + + const command = new HeadObjectCommand(params); + const response = await s3.send(command); + console.log("Object attributes:"); + console.log(` - Size: ${response.ContentLength} bytes`); + console.log(` - Last Modified: ${response.LastModified}`); + console.log(` - ETag: ${response.ETag}`); + console.log(` - Content Type: ${response.ContentType}`); + // Add more attributes as needed + } catch (err) { + console.error("Error getting object attributes:", err); + } +} + +{% endhighlight %} +
+ +> 在 `Example/` 目录中运行以下命令执行该示例 +
+{% highlight bash linenos %} +$ node GetObjectAttr.js +{% endhighlight %} +
diff --git "a/collections/_node.js-sdk/\350\216\267\345\217\226\345\257\271\350\261\241\346\235\203\351\231\220\344\277\241\346\201\257.md" "b/collections/_node.js-sdk/\350\216\267\345\217\226\345\257\271\350\261\241\346\235\203\351\231\220\344\277\241\346\201\257.md" new file mode 100644 index 0000000..87190fb --- /dev/null +++ "b/collections/_node.js-sdk/\350\216\267\345\217\226\345\257\271\350\261\241\346\235\203\351\231\220\344\277\241\346\201\257.md" @@ -0,0 +1,58 @@ +--- +title: '获取对象权限信息' +sidebar: + nav: node.js-sdk +--- +本SDK提供`GetObjectAclCommand`类用于获取对象权限信息。完整代码详见 [Github](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/src/commands/GetObjectAclCommand.ts) 。 +`GetObjectAclCommand`调用的 S3 API 为 GetObjectAcl, 具体参见[GetObjectAcl API 文档](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObjectAcl.html)。 + + + +### 参数说明 +- `Bucket`: 文件所在的存储空间 +- `Key`: 文件在存储空间内的名称 + +### 访问权限定义(ACL) + + +| US3 ACL | [AWS S3 Canned ACL](https://docs.aws.amazon.com/AmazonS3/latest/userguide/acl-overview.html#canned-acl) | +| ----------------- | -------------------------------------------- | +| private | private | +| public-read | public-read | +| public-read-write | public-read-write | +| 不支持 | aws-exec-read
authenticated-read
bucket-owner-read
bucket-owner-full-control
log-delivery-write | + + + +### 示例 +> 执行该示例前请确保配置文件的正确性
以下代码段需要在上下文中运行 + +
+{% highlight javascript linenos %} +const s3 = require('./s3client'); +const { GetObjectAclCommand } = require("@aws-sdk/client-s3"); + +async function getObjectAcl(bucketName, keyName) { + try { + const params = { + Bucket: bucketName, + Key: keyName + }; + + const command = new GetObjectAclCommand(params); + const response = await s3.send(command); + console.log("Object ACL:", response); + } catch (err) { + console.error("Error getting object ACL:", err); + } +} + + +{% endhighlight %} +
+> 在 `Example/` 目录中运行以下命令执行该示例 +
+{% highlight bash linenos %} +$ node GetObjectAcl.js +{% endhighlight %} +
diff --git "a/collections/_node.js-sdk/\350\216\267\345\217\226\347\233\256\345\275\225\346\226\207\344\273\266\345\210\227\350\241\250.md" "b/collections/_node.js-sdk/\350\216\267\345\217\226\347\233\256\345\275\225\346\226\207\344\273\266\345\210\227\350\241\250.md" new file mode 100644 index 0000000..a33eb47 --- /dev/null +++ "b/collections/_node.js-sdk/\350\216\267\345\217\226\347\233\256\345\275\225\346\226\207\344\273\266\345\210\227\350\241\250.md" @@ -0,0 +1,66 @@ +--- +title: '获取目录文件列表' +sidebar: + nav: node.js-sdk +--- +本SDK提供`ListObjectsV2Command`类用于获取目录文件列表。完整代码详见 [Github](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/src/commands/ListObjectsV2Command.ts) 。 +`ListObjectsV2Command`调用的 S3 API 为 ListObjectsV2, 具体参见[ListObjectsV2 API 文档](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html)。 + + + +### 参数说明 +- `Bucket`: 文件所在的存储空间 +- `MaxKeys`: 请求返回的最大键数 + + + + +### 示例 +> 执行该示例前请确保配置文件的正确性
以下代码段需要在上下文中运行 + +
+{% highlight javascript linenos %} +const s3 = require('./s3client'); +const { ListObjectsV2Command } = require("@aws-sdk/client-s3"); + +async function listObjects(bucketName, maxKeys = 1000) { + try { + const command = new ListObjectsV2Command({ + Bucket: bucketName, + MaxKeys: maxKeys + }); + + let isTruncated = true; + let contents = ""; + let continuationToken; + + console.log("Your bucket contains the following objects:\n"); + + while (isTruncated) { + const params = { ...command.input }; + if (continuationToken) { + params.ContinuationToken = continuationToken; + } + + const response = await s3.send(new ListObjectsV2Command(params)); + const contentsList = response.Contents.map((c) => ` • ${c.Key}`).join("\n"); + contents += contentsList + "\n"; + isTruncated = response.IsTruncated; + continuationToken = response.NextContinuationToken; + } + + console.log(contents); + } catch (err) { + console.error("Error listing objects:", err); + } +} +{% endhighlight %} +
+ +> 在 `Example/` 目录中运行以下命令执行该示例 + +
+{% highlight bash linenos %} +$ node ListObjects.js [maxKeys] +{% endhighlight %} +
diff --git "a/collections/_node.js-sdk/\350\247\243\345\206\273\345\275\222\346\241\243\346\226\207\344\273\266.md" "b/collections/_node.js-sdk/\350\247\243\345\206\273\345\275\222\346\241\243\346\226\207\344\273\266.md" new file mode 100644 index 0000000..1d57213 --- /dev/null +++ "b/collections/_node.js-sdk/\350\247\243\345\206\273\345\275\222\346\241\243\346\226\207\344\273\266.md" @@ -0,0 +1,76 @@ +--- +title: '解冻归档文件' +sidebar: + nav: node.js-sdk +--- +本SDK提供`RestoreObjectCommand`类和`HeadObjectCommand`类用于解冻归档文件。完整代码详见 [Github](https://github.com/aws/aws-sdk-js-v3/tree/main/clients/client-s3/src/commands) 。 +`RestoreObjectCommand`调用的 S3 API 为 RestoreObject, 具体参见[RestoreObject API 文档](https://docs.aws.amazon.com/AmazonS3/latest/API/API_RestoreObject.html)。 +`HeadObjectCommand`调用的 S3 API 为 HeadObject, 具体参见[HeadObject API 文档](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html)。 + + + +### 参数说明 +- `Bucket`: 文件所在的存储空间 +- `Key`: 文件在存储空间内的名称 +- `RestoreRequest`: 文件恢复后可访问的天数 + + + +### 示例 +> 执行该示例前请确保配置文件的正确性
以下代码段需要在上下文中运行 + +
+{% highlight javascript linenos %} +const s3 = require('./s3client'); +const { RestoreObjectCommand, HeadObjectCommand } = require("@aws-sdk/client-s3"); + +async function restoreS3Object(bucketName, keyName) { + try { + const restoreRequest = { + Days: 3, + }; + + const params = { + Bucket: bucketName, + Key: keyName, + RestoreRequest: restoreRequest + }; + + const command = new RestoreObjectCommand(params); + const response = await s3.send(command); + console.log("Restore request sent successfully:", response); + + + await checkRestorationStatus(bucketName, keyName); + } catch (err) { + console.error("Error restoring object:", err); + } +} + +async function checkRestorationStatus(bucketName, keyName) { + try { + const params = { + Bucket: bucketName, + Key: keyName + }; + + const command = new HeadObjectCommand(params); + const response = await s3.send(command); + + const restStatus = response.Restore ? "in-progress" : "finished or failed"; + console.log(`Restoration status: ${restStatus}`); + } catch (err) { + console.error("Error checking restoration status:", err); + } +} + +{% endhighlight %} +
+ +> 在 `Example/` 目录中运行以下命令执行该示例 + +
+{% highlight bash linenos %} +$ node RestoreObject.js +{% endhighlight %} +
diff --git a/index.html b/index.html index b18065f..c14dec9 100644 --- a/index.html +++ b/index.html @@ -9,4 +9,7 @@

C++ SDK

Java SDK

-

C SDK