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

# 上传文件

> POST /v1/files — 上传文件并拿到文件 ID，供专家在沙盒中读取

把文件上传到平台，拿到一个文件 ID，然后在 [调用专家](/zh/developers/api/agents-run) 时通过 `attachment_file_ids` 传入。平台会把文件放进专家的沙盒（`/home/user/uploads/`），专家可以用 `read` / `grep` 读取内容——PDF、Word、Excel 都能读。

<Note>
  **为什么是文件 ID，不是 URL**：平台不会去下载你提供的任意链接。这意味着（1）你不需要把私有文档暴露成公开可读的链接；（2）平台不会被当成代理去访问你的内网。读取链接由平台在调用时临时签发，只在平台内部流转。
</Note>

## 快速开始

用 SDK 一行搞定（SDK 内部封装了下面三步）：

<CodeGroup>
  ```python Python theme={null}
  from profy import Profy

  async with Profy(api_key="sk-pro-your-key") as client:
      file = await client.files.create("./报关单.pdf")

      result = await client.agents.run(
          "my-expert",
          "读一下这份报关单，把商品明细列出来",
          attachment_file_ids=[file.id],
      )
      print(result.text)
  ```

  ```typescript TypeScript theme={null}
  import { Profy } from "@profy-ai/sdk";
  import { readFile } from "node:fs/promises";

  const client = new Profy({ apiKey: "sk-pro-your-key" });

  const file = await client.files.create({
    data: await readFile("./报关单.pdf"),
    filename: "报关单.pdf",
  });

  const result = await client.agents.run(
    "my-expert",
    "读一下这份报关单，把商品明细列出来",
    { attachmentFileIds: [file.id] },
  );
  console.log(result.text);
  ```
</CodeGroup>

## 上传流程

不用 SDK 的话，上传是三步：换取上传凭证 → 直传对象存储 → 登记。

字节直传对象存储、不经过平台 API 服务，所以大文件不受请求体大小限制。

### 1. 换取上传凭证

```
POST https://api.profy.cn/v1/files/upload-url
```

<ParamField body="filename" type="string" required>
  文件名，不能包含路径分隔符（`/`、`\`），最长 255 字符。
</ParamField>

<ParamField body="content_type" type="string" required>
  MIME 类型，例如 `application/pdf`。必须在平台允许的类型白名单内。
</ParamField>

<ParamField body="size" type="integer" required>
  文件字节数，上限 100 MB。这一步会用它做存储配额预检查。
</ParamField>

```json 响应 theme={null}
{
  "code": 0,
  "message": "ok",
  "data": {
    "upload_url": "https://cos.example.com/...&X-Amz-Signature=...",
    "file_key": "platform-api/3f2c.../报关单.pdf",
    "content_type": "application/pdf",
    "expires_in": 600
  }
}
```

### 2. 直传对象存储

对 `upload_url` 发一个 `PUT`，请求体是文件字节。

<Warning>
  `Content-Type` 必须和第 1 步传的 `content_type` 完全一致，签名覆盖了这个头，不一致会 403。

  这个 `PUT` **不要带** `Authorization` 头 —— 带了会让对象存储改用请求头鉴权、忽略 URL 里的签名，从而失败。
</Warning>

```bash theme={null}
curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: application/pdf" \
  --data-binary @报关单.pdf
```

### 3. 登记文件

```
POST https://api.profy.cn/v1/files
```

<ParamField body="file_key" type="string" required>
  第 1 步返回的 `file_key`。
</ParamField>

<ParamField body="filename" type="string">
  文件名。不传则用 `file_key` 里的那一段。
</ParamField>

<ParamField body="content_type" type="string">
  MIME 类型。不传则用对象存储记录的类型。
</ParamField>

平台会先确认对象真的已上传（并以对象存储记录的大小为准，不采信请求里声明的大小），然后返回文件 ID：

```json 响应 theme={null}
{
  "code": 0,
  "message": "ok",
  "data": {
    "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "name": "报关单.pdf",
    "type": "application/pdf",
    "size": 284913
  }
}
```

## 删除文件

```
DELETE https://api.profy.cn/v1/files/{file_id}
```

逻辑删除并清理对象存储。只能删自己的文件；删除不存在的 ID 也返回成功（幂等）。

<CodeGroup>
  ```python Python theme={null}
  await client.files.delete(file.id)
  ```

  ```typescript TypeScript theme={null}
  await client.files.delete(file.id);
  ```
</CodeGroup>

## 限制

| 项       | 限制                                                   |
| ------- | ---------------------------------------------------- |
| 单文件大小   | 100 MB                                               |
| 单次调用附件数 | 20                                                   |
| 上传凭证有效期 | 10 分钟                                                |
| 文件类型    | 平台 MIME 白名单（PDF / Office / 文本 / 代码 / 图片 / 音视频 / 压缩包） |
| 存储配额    | 按账户套餐，超出返回 403                                       |

## 错误码

| HTTP 状态码 | 说明                                   |
| -------- | ------------------------------------ |
| `400`    | 参数缺失或无效（文件名含路径分隔符、类型不允许、超出大小上限、对象为空） |
| `401`    | 认证失败                                 |
| `403`    | 存储配额不足                               |
| `404`    | `file_key` 对应的对象不存在（还没上传，或凭证已过期）     |

## 下一步

<CardGroup cols={2}>
  <Card title="调用专家" icon="robot" href="/zh/developers/api/agents-run">
    用 `attachment_file_ids` 把文件交给专家
  </Card>

  <Card title="Python SDK" icon="python" href="/zh/developers/sdk-python">
    Python SDK 完整指南
  </Card>
</CardGroup>
