Skip to content

Commit

Permalink
upload url part 1
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed May 23, 2024
1 parent 984ec67 commit 109b525
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 21 deletions.
52 changes: 48 additions & 4 deletions zh-CN/advanced/resource.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 资源文件
# 资源链接

## 文件上传 <badge type="warning">实验性</badge> {#api-upload-create}

Expand All @@ -24,15 +24,59 @@ binary-data

其中,`Content-Disposition` 中的 `name` 字段表示文件标识符 (必需且不能重复),`filename` 字段表示文件名 (可选);`Content-Type` 表示文件类型 (必需)。

返回值是一个字典类型,其中的每个键分别对应于请求体中的文件标识符,值是一个 URL 字符串,可以在消息编码中使用。下面是上述示例的返回值
返回值是一个字典类型,其中的每个键分别对应于请求体中的文件标识符,值是一个 URL 字符串,可以在消息编码中使用。下面是一个示例的返回值

```json
{
"foo": "upload://temp/z0q9lgqb07a/3j6emd92zgw-image1.png",
"bar": "upload://temp/z0q9lgqb07a/reacpmeq2lc-image2.gif"
"foo": "upload://temp/z0q9lgqb/3j6emd92-image1.png",
"bar": "upload://temp/z0q9lgqb/reacpmeq-image2.gif"
}
```

在实现此 API 时,如果平台已经支持了文件上传功能,可以直接使用平台提供的上传 API,返回平台的 URL 即可。如果平台不支持文件上传功能,应当回退到 SDK 提供的默认实现。

SDK 可以基于本地文件系统实现上传功能。上传到本地文件系统中的文件 URL 通过 `upload://` 协议进一步代理,且有一定的有效期。各实现可以根据自身情况调整有效期,推荐值为 5 分钟。

## 内部链接

`upload://` 称为内部链接协议,用于代理一些无法直接通过公网访问的资源。

### 适用场景

上一节中已经提到,在不支持文件上传的平台上调用 `upload.create`,你将获得内部链接。除此以外,还有一些内部链接的适用场景。

::: tip
**场景:通过平台 API 请求资源**

某些平台使用 ID 标识资源文件 (例如 Lark)。当你接收到来自平台的消息时,拿到的是资源 ID 而非链接。此时你需要调用平台 API,将资源 ID 转换为链接,才能构造合法的消息元素。

为了避免在不必要的场合损失性能,更推荐的方式是直接将资源 ID 封装进内部链接,并立即构造消息元素。等到真正需要请求资源时再调用平台的 API。
:::

::: tip
**场景:资源链接不宜直接公开**

对于另一些平台,尽管其提供的资源链接是可用的,但这个链接中会明文包含机器人令牌,并非可以公开使用的链接 (例如 Telegram)。因此,对于这些平台中的资源,我们也不能直接使用其链接,同样需要将其封装进内部链接。此时内部链接就是单纯的代理。
:::

### 不同方案对比

与内部链接相比,另一些实践则是不推荐的。

::: warning
**不推荐:`data:` URL**

一种不推荐的方案是直接下载资源,并转换为 `data:` 链接放入消息元素中。之所以不推荐使用,是因为这种方案有两大致命缺点:

- 这些图片本来可以按需加载,但现在却被强制下载到本地,造成额外的带宽消耗;
- 编码为 `data:` 会导致消息体积大幅增加,极大影响消息处理的性能。
:::

::: warning
**不推荐:本地代理**

另一种方案是由 SDK 额外提供一个用于访问资源的路由 (比如下文介绍的代理路由),并将资源链接转换为能访问到该路由的 URL。这种方案在 Satori 引入内部链接之前是最佳实践,但现在已经不推荐使用了。相比内部链接,这种方案有两个缺点:

- 这样生成的链接与 Satori 服务器自身的地址耦合,一旦 Satori 服务器更换域名或者端口,过去的链接将全部失效,不利于迁移和跨接等复杂场景;
- 如果需要扩展其他需要用到资源的逻辑 (比如下载图片到本地),就会导致通过网络自己请求自己,而这些数据原本可以在内存或硬盘中传输,这显然引入了额外的性能损耗。
:::
17 changes: 0 additions & 17 deletions zh-CN/protocol/message.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,23 +73,6 @@ Satori 中的消息使用消息元素 (Message Element) 进行编码。消息元

使用成对的 `<!--``-->` 插入一段注释。注释中的部分不会被渲染。

## 资源反向代理

一些平台会使用 ID 标识资源文件 (例如 Lark)。当你接收到来自平台的消息时,拿到的是资源 ID 而非资源链接。此时你需要将资源 ID 转换为资源链接,才能构造合法的资源元素。

::: tip
Telegram 是另一种特殊情况。尽管其提供的资源链接是可用的,但这个链接中会明文包含机器人令牌,并非可以公开使用的链接。因此 Telegram 和其他类似平台也适用于这一节的内容。
:::

::: tip
一种**不推荐**的做法是直接下载资源,并转存为 `data:` 链接放入消息中。之所以不推荐,是因为这种做法有两大致命缺点:

1. 这些图片本来可以按需加载,但现在却被强制下载到本地,造成额外的带宽消耗。
2. 编码为 `data:` 会导致消息体积大幅增加,极大影响消息处理的性能。
:::

对于这种情况,我们建议使用实现资源反向代理。SDK 额外提供一个用于访问资源的路由 (类似 `/v1/assets/xxx`),将资源 ID 映射到资源链接,并编码到消息元素中。这样一来,上面提到的两个问题也就都解决了。

## 标准元素

关于 Satori 内置的消息元素,请参考 [标准元素](./elements.md)

0 comments on commit 109b525

Please sign in to comment.