> For the complete documentation index, see [llms.txt](https://docs.maiagent.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.maiagent.ai/tech/api-integration/api_knowledge.md).

# Presigned 檔案上傳模式

目前在 MaiAgent 上需要上傳檔案的地方有兩處

1. 知識庫文件上傳
2. 訊息用到的附件上傳

## Presigned 上傳模式

MaiAgent 支援 **Presigned 上傳模式**，此模式允許客戶端直接將檔案上傳至雲端儲存（如 S3），而不需經過伺服器中繼。使用此模式時，伺服器僅負責生成具有時效性與安全性的 Presigned URL，客戶端利用該 URL 直接進行檔案上傳。

#### 與傳統上傳模式的差異：

1. **資料流向**
   * 傳統模式：檔案須經過伺服器，再由伺服器上傳到雲端儲存。
   * Presigned 模式：檔案由客戶端直接上傳至雲端，避免伺服器處理檔案的負擔。
2. **效能與成本**
   * 傳統模式可能導致伺服器資源消耗過大，並增加資料傳輸成本。
   * Presigned 模式減少伺服器負載，提高效能並降低傳輸成本。
3. **安全性**
   * Presigned URL 包含簽名與有效期，確保上傳請求僅限於授權用戶在指定時間內使用。

此模式特別適合大檔案或高頻率上傳的場景，提升效率的同時保持高度的安全性。

#### 預簽上傳流程圖

```mermaid
sequenceDiagram
    participant Client as Client
    participant Server as Sever
    participant S3 as Storage(S3)

    Note over Client,S3: 預簽上傳流程
    
    Client->>+Server: 1. 請求預簽 URL
    Note right of Client: POST /api/v1/upload-presigned-url/<br/>包含檔名、類型、大小
    
    Server->>Server: 2. 驗證請求參數
    Server->>Server: 3. 產生預簽參數
    Note right of Server: 設定有效期、權限等
    Server-->>-Client: 4. 回傳預簽 URL 和參數
    
    Client->>+S3: 5. 使用預簽 URL 直接上傳
    Note right of Client: POST 到預簽 URL<br/>帶上 fields 全部欄位 + 檔案 binary
    S3-->>-Client: 6. 上傳完成回應 (HTTP 204)
    
    opt 知識庫場景：註冊檔案到知識庫
        Client->>+Server: 7. 用 fields.key 註冊
        Note right of Client: POST /api/v1/knowledge-bases/{KB_ID}/files/<br/>body 帶 file = fields.key
        Server->>Server: 8. 建立 KnowledgeBaseFile 並排程解析
        Server-->>-Client: 9. 回傳 file record
    end
```

#### 傳統上傳流程圖

```mermaid
sequenceDiagram
    participant Client as Client
    participant Server as Server
    participant S3 as Storage(S3)

    Note over Client,S3: 傳統上傳流程
    
    Client->>+Server: 1. 上傳檔案請求
    Note right of Client: POST /api/v1/upload<br/>檔案內容包含在請求中
    
    Server->>Server: 2. 驗證檔案<br/>(大小、類型、格式)
    Server->>Server: 3. 儲存暫存檔
    
    Server->>+S3: 4. 上傳檔案至 S3
    Note right of Server: 使用 AWS SDK<br/>上傳暫存檔
    S3-->>-Server: 5. 上傳成功回應
    
    Server->>Server: 6. 清理暫存檔
    Server-->>-Client: 7. 回傳上傳結果
    Note left of Server: 回傳 S3 檔案位置
```

***

#### Presigned 上傳檔案

以下描述如何使用 MaiAgent 提供的 API 完成檔案的 Presigned 上傳過程。**知識庫場景**需要走完整 3 個步驟（Step 1 → 2 → 3），訊息附件場景則只需要 Step 1 → 2。

#### 1. **取得 Presigned URL**

**Endpoint**

`POST https://api.maiagent.ai/api/v1/upload-presigned-url/`

**說明**

客戶端向伺服器發送請求，取得一個 Presigned URL，該 URL 用於直接上傳檔案至雲端儲存。

**請求參數**

| 參數名稱        | 型別        | 必填 | 說明                                                                                                     |
| ----------- | --------- | -- | ------------------------------------------------------------------------------------------------------ |
| `filename`  | `string`  | 是  | 上傳檔案的名稱                                                                                                |
| `modelName` | `string`  | 是  | <p>模組名稱，用於分類上傳的檔案用途<br></p><p>知識庫請填寫<code>chatbot-file</code></p><p>訊息附件請填寫<code>attachment</code></p> |
| `fieldName` | `string`  | 是  | 檔案字段名稱，用於標識檔案用途                                                                                        |
| `fileSize`  | `integer` | 是  | 檔案大小（以位元組為單位），必須與實際 binary 大小完全一致，否則 Step 2 會被 S3 拒絕                                                   |

**範例請求**

```bash
curl --location 'https://api.maiagent.ai/api/v1/upload-presigned-url/' \
--header 'Authorization: Api-Key YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
    "filename": "document.pdf",
    "modelName": "chatbot-file",
    "fieldName": "file",
    "fileSize": 178329
}'
```

**回應範例**

```json
{
    "url": "https://s3.ap-northeast-1.amazonaws.com/whizchat-media-prod-django.playma.app",
    "fields": {
        "key": "media/chatbots/chatbot-file/86572e41-16ba-45dd-b049-28c69f77ffb0.pdf",
        "x-amz-algorithm": "AWS4-HMAC-SHA256",
        "x-amz-credential": "ASIATIVCN4X5XXXXXXXX/20260522/ap-northeast-1/s3/aws4_request",
        "x-amz-date": "20260522T020727Z",
        "x-amz-security-token": "IQoJb3JpZ2luX2VjEEoa...（很長的 STS token）",
        "policy": "eyJleHBpcmF0aW9uIjog...（base64 policy）",
        "x-amz-signature": "617e10d1db034ab351d3735de5c56287ecc926dbfe5b5884d2ce67deed363004"
    }
}
```

{% hint style="warning" %}
**Production 環境使用 AWS STS 短期憑證**（`x-amz-credential` 以 `ASIA` 開頭），回應的 `fields` 物件\*\*會包含 \*\***`x-amz-security-token`** 欄位。Step 2 上傳時必須一字不漏帶上所有 `fields` 欄位（含 `x-amz-security-token`），少帶會被 S3 拒絕。

Presigned URL **有效期約 1 小時**，請在取得後盡快使用。
{% endhint %}

***

#### 2. **上傳檔案至雲端儲存**

**說明**

使用從上述步驟取得的 `url` 和 `fields`，直接將檔案上傳至雲端儲存。**`fields` 物件中每一個欄位都必須帶上**（包含 `x-amz-security-token`）；不要寫死欄位清單，若 AWS 後續加新欄位，動態組裝可避免漏帶。

**上傳成功時 S3 回應 HTTP 204 No Content（空 body）**。

**範例請求（curl，含 STS token）**

```bash
curl --location 'https://s3.ap-northeast-1.amazonaws.com/whizchat-media-prod-django.playma.app' \
--form 'key="media/chatbots/chatbot-file/86572e41-16ba-45dd-b049-28c69f77ffb0.pdf"' \
--form 'x-amz-algorithm="AWS4-HMAC-SHA256"' \
--form 'x-amz-credential="ASIATIVCN4X5XXXXXXXX/20260522/ap-northeast-1/s3/aws4_request"' \
--form 'x-amz-date="20260522T020727Z"' \
--form 'x-amz-security-token="IQoJb3JpZ2luX2VjEEoa..."' \
--form 'policy="eyJleHBpcmF0aW9uIjog..."' \
--form 'x-amz-signature="617e10d1db034ab351d3735de5c56287ecc926dbfe5b5884d2ce67deed363004"' \
--form 'file=@"/path/to/document.pdf"'
```

**範例請求（Python，動態組裝，推薦做法）**

```python
import requests

# Step 1: 取得 Presigned URL
presign = requests.post(
    'https://api.maiagent.ai/api/v1/upload-presigned-url/',
    headers={'Authorization': 'Api-Key YOUR_API_KEY'},
    json={
        'modelName': 'chatbot-file',
        'fieldName': 'file',
        'filename': 'document.pdf',
        'fileSize': 178329,
    },
).json()

# Step 2: 把 fields 整個塞，所有欄位（含 x-amz-security-token）自動帶上
with open('/path/to/document.pdf', 'rb') as f:
    s3_response = requests.post(
        presign['url'],
        data=presign['fields'],
        files={'file': f},
    )
assert s3_response.status_code == 204, s3_response.text

file_key = presign['fields']['key']  # Step 3 要用的值
```

***

#### 3. **註冊檔案到知識庫（知識庫場景才需要）**

**Endpoint**

`POST https://api.maiagent.ai/api/v1/knowledge-bases/{knowledgeBasePk}/files/`

**說明**

Step 2 完成後，檔案 binary 已在 S3，但**還沒進入知識庫**。需要呼叫此 API 把上傳結果註冊成 `KnowledgeBaseFile`，系統才會開始解析、切片、建索引。

{% hint style="warning" %}
**`file` 欄位必須填入 Step 1 回應的 `fields.key`（MaiAgent S3 內部相對路徑），不是任意外部 URL**。若你拿到的是外部 URL（例如夥伴系統的下載連結），請先用 `GET` 下載檔案，再走 Step 1 → 2 → 3。
{% endhint %}

**路徑參數**

| 參數名稱              | 型別       | 說明              |
| ----------------- | -------- | --------------- |
| `knowledgeBasePk` | `string` | 知識庫的唯一識別碼（UUID） |

**請求參數**

| 參數名稱                            | 型別       | 必填 | 說明                                       |
| ------------------------------- | -------- | -- | ---------------------------------------- |
| `files`                         | `array`  | 是  | 要建立的檔案清單，可一次批次建立多個                       |
| `files[].filename`              | `string` | 是  | 原始檔名                                     |
| `files[].file`                  | `string` | 是  | Step 1 回應的 `fields.key`（相對路徑，**不是 URL**） |
| `files[].parser`                | `string` | 否  | 指定 parser 的 UUID，不填則用知識庫預設               |
| `files[].labels`                | `array`  | 否  | 檔案標籤（{id, name} 物件陣列）                    |
| `files[].rawUserDefineMetadata` | `object` | 否  | 用戶自定義 metadata                           |

**範例請求**

```bash
curl --location 'https://api.maiagent.ai/api/v1/knowledge-bases/86401a64-ad89-4847-a709-f4ccfa0af7b9/files/' \
--header 'Authorization: Api-Key YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
    "files": [
        {
            "filename": "document.pdf",
            "file": "media/chatbots/chatbot-file/86572e41-16ba-45dd-b049-28c69f77ffb0.pdf"
        }
    ]
}'
```

**回應範例**

```json
[
    {
        "id": "86572e41-16ba-45dd-b049-28c69f77ffb0",
        "filename": "document.pdf",
        "file": "https://media.maiagent.ai/media/chatbots/chatbot-file/86572e41-16ba-45dd-b049-28c69f77ffb0.pdf",
        "fileType": "pdf",
        "knowledgeBase": {
            "id": "86401a64-ad89-4847-a709-f4ccfa0af7b9",
            "name": "我的知識庫"
        },
        "size": 178329,
        "status": "initial",
        "parser": {
            "id": "535c5b86-0534-4d0a-abfe-82f3d37e769a",
            "name": "MaiAgent Parser",
            "provider": "maiagent",
            "order": 0,
            "supportsDiarization": false
        },
        "labels": [],
        "rawUserDefineMetadata": {},
        "createdAt": "1779425272000"
    }
]
```

**檔案狀態（`status`）**

| 值            | 說明          |
| ------------ | ----------- |
| `initial`    | 剛建立、等待處理    |
| `processing` | 正在解析、切片、建索引 |
| `done`       | 處理完成、可被檢索   |
| `failed`     | 處理失敗        |

***

## 常見錯誤與排查

**Q：S3 回 `The AWS Access Key Id you provided does not exist in our records`**

A：Step 2 漏帶 `x-amz-security-token`，或寫死了過期的 access key / S3 endpoint。請永遠用 Step 1 即時回傳的 `url` 跟 `fields`，把 `fields` 整個物件原封不動帶到 Step 2。

**Q：S3 回 `Policy Condition failed`**

A：通常是 Step 1 的 `fileSize` 跟實際 binary 大小不一致；重新計算實際檔案大小，重跑 Step 1。

**Q：Step 3 回 400，說 `file` 欄位有問題**

A：`file` 必須填 Step 1 回應的 `fields.key`（例如 `media/chatbots/chatbot-file/xxx.pdf`），不是 Step 3 回應的 full URL，也不是任意外部 URL。

**Q：我已經有一個外部 URL（例如夥伴系統的下載連結），可以直接給 Step 3 嗎？**

A：不行。Step 3 的 `file` 欄位是 MaiAgent 自家 S3 的相對 key。請先用 `GET` 把該外部 URL 的檔案抓下來，再走 Step 1 → 2 → 3。

**Q：Step 3 之後檔案一直 `status: processing`**

A：解析時間視檔案大小、類型而定，大檔案可能要數分鐘。可用 `GET /api/v1/knowledge-bases/{KB_ID}/files/{file_id}/` 輪詢狀態，等到 `done` 才能被 AI 助理檢索。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.maiagent.ai/tech/api-integration/api_knowledge.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
