> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cyberun.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# 上传输入文件

> 先上传文件，再把它的引用传给任何接收文件参数的工作流。

工作流可以声明 `type: file` 的参数。要为这类工作流提交任务，
请先上传文件，然后把返回的 `file_key` 作为该参数的值传入。
上传走 S3 预签名 `PUT` —— 不占用 API 服务器的带宽。

## 整体流程

按顺序三次调用：

1. `POST /api/v1/r/files/presign` —— 描述文件。返回
   `file_key`、`upload_url`、`upload_headers`。
2. `PUT <upload_url>` —— 把文件主体直接上传到 S3，原样发送
   `upload_headers` 中的每一个头。
3. `POST /api/v1/r/workflows/{workflowId}/run`（或
   `/r/workflows/slug/{workflowSlug}/run`），把 `file_key` 作为
   文件参数的值。

## 请求预签名 URL

```http theme={null}
POST /api/v1/r/files/presign HTTP/1.1
Authorization: Bearer sk-...
Content-Type: application/json

{
  "file_name": "photo.png",
  "file_size": 1048576,
  "content_type": "image/png"
}
```

响应：

```json theme={null}
{
  "file_key": "inputs/.../photo.png",
  "upload_url": "https://s3.example.com/bucket/key?X-Amz-Signature=...",
  "upload_headers": {
    "Content-Length": "1048576",
    "If-None-Match": "*"
  }
}
```

| 字段               | 说明                              |
| ---------------- | ------------------------------- |
| `file_key`       | 不透明引用。作为任意 `type: file` 参数的值传入。 |
| `upload_url`     | 带签名查询串的 S3 `PUT` URL。           |
| `upload_headers` | `PUT` 时**必须**原样发送的请求头。已经签进签名里。  |

## 上传到 S3

把文件主体作为 `PUT` 请求体发送，并附带 `upload_headers` 中的所有
请求头。签名校验这些头 —— 缺少 `Content-Length` 或
`If-None-Match` 会被 S3 返回 `403`。如果该 key 上已经存在对象，
S3 返回 `412`（防止覆盖）。

```bash theme={null}
curl -X PUT \
  -H "Content-Length: 1048576" \
  -H "If-None-Match: *" \
  --upload-file photo.png \
  "<upload_url>"
```

`Content-Length` 必须与第 1 步发送的 `file_size` 完全一致。

## 提交工作流

把 `file_key` 作为参数值：

```http theme={null}
POST /api/v1/r/workflows/slug/img2img/run HTTP/1.1
Authorization: Bearer sk-...
Content-Type: application/json

{
  "parameters": {
    "input_image": "inputs/.../photo.png",
    "prompt": "enhance"
  }
}
```

任务运行时由代理从 S3 取回文件。

## 端到端示例

<CodeGroup>
  ```python Python theme={null}
  import requests

  API_KEY = "sk-..."
  BASE = "https://core.cyberun.cloud/api/v1/r"
  HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}

  # 1. 预签名
  presign = requests.post(
      f"{BASE}/files/presign",
      headers=HEADERS,
      json={
          "file_name": "photo.png",
          "file_size": 1048576,
          "content_type": "image/png",
      },
  ).json()

  # 2. PUT 到 S3 —— 把 presign 返回的每个 header 都带上
  with open("photo.png", "rb") as f:
      requests.put(presign["upload_url"], data=f, headers=presign["upload_headers"])

  # 3. 提交一个接收文件参数的工作流
  resp = requests.post(
      f"{BASE}/workflows/slug/img2img/run",
      headers=HEADERS,
      json={"parameters": {"input_image": presign["file_key"], "prompt": "enhance"}},
  )
  print(resp.json()["task_id"])
  ```

  ```bash curl theme={null}
  # 1. 预签名
  PRESIGN=$(curl -s -X POST \
    -H "Authorization: Bearer sk-..." \
    -H "Content-Type: application/json" \
    -d '{"file_name":"photo.png","file_size":1048576,"content_type":"image/png"}' \
    https://core.cyberun.cloud/api/v1/r/files/presign)

  FILE_KEY=$(echo "$PRESIGN" | jq -r '.file_key')
  UPLOAD_URL=$(echo "$PRESIGN" | jq -r '.upload_url')

  # 2. PUT —— 把 upload_headers 展开成 -H 参数
  HEADERS_ARGS=()
  while IFS= read -r h; do HEADERS_ARGS+=(-H "$h"); done < <(
    echo "$PRESIGN" | jq -r '.upload_headers | to_entries[] | "\(.key): \(.value)"'
  )
  curl -X PUT "${HEADERS_ARGS[@]}" --upload-file photo.png "$UPLOAD_URL"

  # 3. 提交工作流
  curl -s -X POST \
    -H "Authorization: Bearer sk-..." \
    -H "Content-Type: application/json" \
    -d "{\"parameters\":{\"input_image\":\"$FILE_KEY\",\"prompt\":\"enhance\"}}" \
    https://core.cyberun.cloud/api/v1/r/workflows/slug/img2img/run
  ```

  ```ts Node.js theme={null}
  const API_KEY = process.env.CYBERUN_KEY!;
  const BASE = 'https://core.cyberun.cloud/api/v1/r';

  // 1. 预签名
  const presign = await fetch(`${BASE}/files/presign`, {
  	method: 'POST',
  	headers: {
  		Authorization: `Bearer ${API_KEY}`,
  		'Content-Type': 'application/json'
  	},
  	body: JSON.stringify({
  		file_name: 'photo.png',
  		file_size: 1048576,
  		content_type: 'image/png'
  	})
  }).then((r) => r.json());

  // 2. PUT 到 S3，带上签名头
  const fileBody = await Bun.file('photo.png').arrayBuffer();
  await fetch(presign.upload_url, {
  	method: 'PUT',
  	headers: presign.upload_headers,
  	body: fileBody
  });

  // 3. 提交工作流
  const run = await fetch(`${BASE}/workflows/slug/img2img/run`, {
  	method: 'POST',
  	headers: {
  		Authorization: `Bearer ${API_KEY}`,
  		'Content-Type': 'application/json'
  	},
  	body: JSON.stringify({
  		parameters: { input_image: presign.file_key, prompt: 'enhance' }
  	})
  }).then((r) => r.json());

  console.log(run.task_id);
  ```
</CodeGroup>

## 保留期和限额

* 通过 `/r/files/presign` 上传的**输入文件**保留 **1 天**，
  之后由 S3 生命周期策略清除。请在此窗口内提交任务。
* **任务输出**保留 **7 天**。之后 `GET /r/tasks/{taskId}/result`
  返回的 `download_url` 不再有效。
* 单文件默认大小上限为 **500 MB**。更大的上限是部署级配置。
  接近或超过此上限的文件请改用分片上传端点
  （`/uploads/multipart/...`） —— 每个分片单独预签名。

## 大文件分片上传

超过单次上限的文件请使用分片流程：

1. `POST /api/v1/uploads/multipart/initiate` —— 开始一次上传并获取 upload ID。
2. `POST /api/v1/uploads/multipart/presign-parts` —— 为每个分片请求签名 URL。
3. 对每个签名 URL `PUT` 对应的分片。
4. `POST /api/v1/uploads/multipart/complete` —— 完成并取回同样形态的 `file_key`。

单次上传更简单 —— 仅在必要时使用分片。

分片端点（`initiate`、`presign-parts`、`complete`）只接受用户会话令牌
—— 它们会以 `401` 拒绝 `sk-` 集成凭证和 `dk-` 设备凭证。单次的
`/r/files/presign` 三者都接受。如果你用 `sk-` 或 `dk-` 凭证调用 API，
请使用单次预签名流程。

## 相关

* [API 参考](/zh/api-reference/introduction) —— `/r/files/presign`
  与 `/uploads/multipart/*` 端点规格。
* [订阅任务事件流（SSE）](/zh/api-reference/streaming-events) ——
  提交后跟随任务进度。
* [生成 API 密钥](/zh/cloud/guides/api-key) —— 签发 `sk-` 凭证。
