---
docStatus: current
---

# Hosted Service 对象存储与签名上传

绑定工作区 Object Storage Profile，通过短期签名 URL 上传文件，并用 HEAD 和 GET 验证结果。

业务后端可以把附件、图片和导入文件保存在工作区的 Object Storage Profile 中。Hosted Service 从绑定获取对象 API 地址和 Profile Token，文件内容通过短期签名 URL 直接写入对象存储。

## 绑定工作区 Profile

先在目标项目所属的工作区查询 Object Storage Profile。有可用的 Profile 就复用，列表为空或没有合适的 Profile 时再创建。可以在控制台的「工作区资源 → 对象存储」操作，也可以通过 CLI 查询和创建。

先找到当前工作区的 `workspace-object-storage` 服务，从结果中取得 `businessId`。把下面的占位符替换为本次查询得到的值。

```bash
baijimu runtime service list --workspace-id <WORKSPACE_ID> \
  --keyword workspace-object-storage --json

baijimu runtime service call --workspace-id <WORKSPACE_ID> \
  <BUSINESS_ID> listObjectStorageProfiles --params '{}' --json
```

从 `data.profiles` 中选择状态为 `ACTIVE` 的 Profile。需要新建时，先读取创建方法的参数定义，再提交名称。省略 `maxObjectBytes` 时由对象存储服务使用环境默认值。

```bash
baijimu runtime service get --workspace-id <WORKSPACE_ID> \
  <BUSINESS_ID> --method createObjectStorageProfile --json

baijimu runtime service call --workspace-id <WORKSPACE_ID> \
  <BUSINESS_ID> createObjectStorageProfile \
  --params '{"name":"application-assets"}' --json
```

拿到列表项或创建响应中的完整 `profileRef` 后，在目标项目的能力列表里确认 `object-storage` 的 `bindingSupported` 为 `true`，然后绑定到 Environment。`capability get` 用于查看接口合同，绑定前无需运行它。

```bash
baijimu workspace hosted-service capability list \
  --workspace-id <WORKSPACE_ID> --project-id <PROJECT_ID>

baijimu workspace hosted-service config-binding create \
  --workspace-id <WORKSPACE_ID> --environment-id <ENVIRONMENT_ID> \
  --name assets --provider-key object-storage --profile-ref <PROFILE_REF>
```

同名 Provider 按项目、工作区、全局的顺序选择。全局 Provider 可供多个工作区使用，每个 Environment 仍须显式绑定自己的 `profileRef`。平台按项目所属工作区校验 Profile，跨工作区的绑定会被拒绝。找不到可用的 Provider 时，请环境管理员确认登记状态。

## 把绑定字段交给后端

在 Environment 配置中为 `assets` 声明以下变量。已有配置时，把这些变量合入当前配置，再提交完整配置并部署。[环境变量来源](/development/backend-development/configuration-and-auth/)

```json
{
  "schemaVersion": "1.0.0",
  "variables": [
    { "name": "OBJECT_STORAGE_API_URL", "source": { "kind": "resource", "binding": "assets", "field": "apiBaseUrl" } },
    { "name": "OBJECT_STORAGE_ACCESS_TOKEN", "source": { "kind": "resource", "binding": "assets", "field": "accessToken" } },
    { "name": "OBJECT_STORAGE_MAX_OBJECT_BYTES", "source": { "kind": "resource", "binding": "assets", "field": "maxObjectBytes" } }
  ]
}
```

后端用绑定提供的地址和 Token 发起对象 API 请求。Profile Token 只在后端使用，上传客户端只需要本次请求返回的短期 URL 和 headers。

## 申请签名并上传

后端先校验用户的业务权限，为新文件分配对象 key，再用 `Authorization: Bearer <OBJECT_STORAGE_ACCESS_TOKEN>` 调用 `POST {OBJECT_STORAGE_API_URL}/upload-requests`。例如，上传内容为 `hello` 的 5 字节文本文件时，请求体如下。

```json
{
  "contractVersion": "1.1.0",
  "key": "attachments/hello.txt",
  "contentType": "text/plain",
  "contentLength": 5,
  "expiresInSeconds": 120
}
```

响应包含 `method`、`url`、`headers` 和 `expiresAt`。在签名过期前，客户端向该 URL 发出 `PUT`，原样发送响应里的 headers，正文直接放文件字节。签名 URL 本身就是本次上传凭据，无需携带 Profile Token。

## 验证上传结果

1. 收到 PUT 成功响应后，后端带 Profile Token 调用 `HEAD {OBJECT_STORAGE_API_URL}/objects/{key}`，核对 `Content-Length` 和 `Content-Type`。签名申请会校验声明的大小，但实际上传字节数以 HEAD 返回为准。
2. 需要核对内容时，用相同凭据调用 `GET {OBJECT_STORAGE_API_URL}/objects/{key}` 读取文件字节。接入验收可以上传已知内容的小文件并逐字节比较。
3. 校验通过后保存对象 key，供业务记录引用。短期签名 URL 会过期，不能用作长期文件引用。

Profile 释放后，平台会撤销其访问凭据并清理该 Profile 下的对象。验收文件应放在专用测试 Profile，便于验收结束后一起清理。
