# アップロード

Source: https://aiand-docs-staging.pages.dev/ja/api/uploads/

Uploads API を使うと、大きなファイル (数百 MB の動画や複数 GB のクリップ) を 5 MiB 以上 64 MiB 以下のパートに分割してアップロードできます。各パートはアップロードセッションに対して送信され、`complete` を呼ぶと通常の [File オブジェクト](/ja/api/files/) に組み立てられ、推論リクエストから `file_id` で参照できるようになります。

[OpenAI Uploads API](https://platform.openai.com/docs/api-reference/uploads) とワイヤ互換です。公式 Python `openai` SDK の `client.uploads.upload_file_chunked(...)` ヘルパーが、一連のシーケンスを 1 回の呼び出しで実行します。相違点は 1 つ: パートは到着順に組み立てられるため、順番に 1 つずつアップロードし、同じ順序で complete してください ([パートのサイズと順序](#パートのサイズと順序) を参照)。

```http
POST /v1/uploads
POST /v1/uploads/{upload_id}/parts
POST /v1/uploads/{upload_id}/complete
POST /v1/uploads/{upload_id}/cancel
```

<Aside type="note">
  単発の上限内に収まるファイル (100 MiB 以下の画像、100 MiB 以下の動画) には [`POST
  /v1/files`](/ja/api/files/#ファイルをアップロードする) を使用してください。`/v1/uploads`
  は上限を超える場合、主に大きな動画クリップに対して使用します。
</Aside>

## 制限

| 制約                 | 値                                                                        |
| -------------------- | ------------------------------------------------------------------------- |
| 合計ファイルサイズ   | 8 GiB                                                                     |
| パートあたりのサイズ | 5 MiB 以上 64 MiB 以下。5 MiB 未満にできるのは最後のパートだけです        |
| セッションの有効期限 | 作成から 1 時間                                                           |
| 許可される purpose   | `video` (推奨)。`vision` も動作しますが、このサイズで使うケースはまれです |

1 時間以内に完了されなかったセッションは自動的にキャンセルされます。完了したファイルは Files API の標準的な 30 日有効期限を引き継ぎます。

### パートのサイズと順序

ファイルを 5 MiB 以上のパートに分割してください (SDK ヘルパーは 64 MiB を使います)。最後のパートだけは小さくてもかまいません。

パートは API に到着した順に番号が付けられ、その順序で組み立てられます。パートは順番に 1 つずつアップロードし、`complete` には同じ順序で `part_ids` を渡してください。他のどのパートよりも先に到着した 5 MiB 未満のパートは、それがファイル全体でない限り `400` で拒否されます。順序が入れ替わって到着した場合、`complete` は誤った順序でファイルを組み立てる代わりに `400` で拒否します。指定したパートのサイズの合計は、セッション作成時に申告した `bytes` と正確に一致する必要があります。

## クイックスタート

最も簡単なのは、Python SDK の高レベルヘルパーを使う方法です。チャンク分割はヘルパーが処理します。Node SDK には同等のヘルパーがないため、JavaScript タブでは生のエンドポイントを直接呼び出します。同じシーケンスを数行多く書くだけです。

<Tabs>
  <TabItem label="Python">
    ```python
    from pathlib import Path
    from openai import OpenAI

    client = OpenAI(
        api_key="sk-your-api-key",
        base_url="https://api.aiand.com/v1",
    )

    upload = client.uploads.upload_file_chunked(
        file=Path("clip.mp4"),
        purpose="video",
        mime_type="video/mp4",
    )

    print(upload.file.id)  # → file-vid789...
    ```

  </TabItem>
  <TabItem label="JavaScript">
    ```javascript
    import OpenAI, { toFile } from "openai";
    import fs from "node:fs/promises";

    const client = new OpenAI({
      apiKey: "sk-your-api-key",
      baseURL: "https://api.aiand.com/v1",
    });

    const PART = 64 * 1024 * 1024;
    const data = await fs.readFile("clip.mp4");

    const session = await client.uploads.create({
      filename: "clip.mp4",
      purpose: "video",
      bytes: data.byteLength,
      mime_type: "video/mp4",
    });

    const partIds = [];
    for (let offset = 0; offset < data.byteLength; offset += PART) {
      const chunk = data.subarray(offset, Math.min(offset + PART, data.byteLength));
      const part = await client.uploads.parts.create(session.id, {
        data: await toFile(chunk, "part"),
      });
      partIds.push(part.id);
    }

    const upload = await client.uploads.complete(session.id, { part_ids: partIds });
    console.log(upload.file.id); // → file-vid789...
    ```

  </TabItem>
</Tabs>

どちらの方法も、ファイルを均一なパートに分割し、セッションを開き、パートを順番に 1 つずつアップロードし、アップロード順の part id で `complete` を呼び出します。返される `upload.file.id` は通常の `file-...` ID で、他のファイルと同様にチャット補完で使用できます。

## マニュアルフロー

SDK を使わない場合は、生の HTTP エンドポイントに対して同じフローを実行できます。

### 1. セッションを作成する

```http
POST /v1/uploads
```

| フィールド  | 型      | 必須 | 説明                                                  |
| ----------- | ------- | ---- | ----------------------------------------------------- |
| `filename`  | string  | はい | 元のファイル名                                        |
| `purpose`   | string  | はい | `vision` または `video`                               |
| `bytes`     | integer | はい | 申告されたファイルの合計サイズ                        |
| `mime_type` | string  | はい | MIME タイプ。purpose の許可リストに対して検証されます |

```bash
curl https://api.aiand.com/v1/uploads \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "clip.mp4",
    "purpose": "video",
    "bytes": 524288000,
    "mime_type": "video/mp4"
  }'
```

#### レスポンス

```json
{
  "id": "upload-abc123",
  "object": "upload",
  "bytes": 524288000,
  "purpose": "video",
  "filename": "clip.mp4",
  "status": "pending",
  "created_at": 1719450000,
  "expires_at": 1719453600,
  "file": null
}
```

### 2. パートをアップロードする

```http
POST /v1/uploads/{upload_id}/parts
```

`data` フィールドを 1 つだけ含む multipart フォームでアップロードします。最後のパートを除くすべてのパートは 5 MiB 以上である必要があります。いずれのパートも 64 MiB を超えることはできません。パートは順番に 1 つずつアップロードしてください。到着順に番号が付けられ、`complete` にはその順序で指定する必要があります。申告 `bytes` を超えるパートと、ファイル全体ではないのに 5 MiB 未満の最初のパートは、`400` で拒否されます。

```bash
curl https://api.aiand.com/v1/uploads/upload-abc123/parts \
  -H "Authorization: Bearer sk-your-api-key" \
  -F "data=@clip.part1.bin"
```

#### レスポンス

```json
{
  "id": "part-xyz789",
  "object": "upload.part",
  "upload_id": "upload-abc123",
  "created_at": 1719450010
}
```

ソースファイルのすべてのチャンクを送信し終わるまで繰り返し、各 `part.id` を順序付きで記録しておきます。

### 3. アップロードを完了する

```http
POST /v1/uploads/{upload_id}/complete
```

| フィールド | 型       | 必須   | 説明                                                                                                                            |
| ---------- | -------- | ------ | ------------------------------------------------------------------------------------------------------------------------------- |
| `part_ids` | string[] | はい   | ステップ 2 で取得した `part.id` を、アップロードした順序で並べたリスト。サイズの合計は申告した `bytes` と一致する必要があります |
| `md5`      | string   | いいえ | 組み立て後のファイルの MD5 (任意)                                                                                               |

```bash
curl https://api.aiand.com/v1/uploads/upload-abc123/complete \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{ "part_ids": ["part-xyz789", "part-uvw456", "part-rst123"] }'
```

#### レスポンス

```json
{
  "id": "upload-abc123",
  "object": "upload",
  "bytes": 524288000,
  "purpose": "video",
  "filename": "clip.mp4",
  "status": "completed",
  "created_at": 1719450000,
  "expires_at": 1719453600,
  "file": {
    "id": "file-vid789",
    "object": "file",
    "bytes": 524288000,
    "purpose": "video",
    "filename": "clip.mp4",
    "created_at": 1719450090,
    "expires_at": 1722042090
  }
}
```

`file.id` はチャット補完で参照可能な通常のファイル ID です。ドキュメントの場合、`file` には `GET /v1/files/{file_id}` と同様に `page_count` も含まれます。

complete は冪等です。成功した `complete` のレスポンスが失われた場合、同じセッションに対して再度呼び出すと同じ `file` が返ります。

### 4. キャンセル (任意)

```http
POST /v1/uploads/{upload_id}/cancel
```

ペンディングのセッションを中止します。冪等です — 既に完了済みまたはキャンセル済みのセッションは `404` を返します。

```bash
curl -X POST https://api.aiand.com/v1/uploads/upload-abc123/cancel \
  -H "Authorization: Bearer sk-your-api-key"
```

## チャット補完でファイルを参照する

`complete` が `file.id` を返したら、他のファイルと同じように使えます:

```json
{
  "model": "<video-capable-model>",
  "messages": [
    {
      "role": "user",
      "content": [
        { "type": "text", "text": "Describe what happens in this clip." },
        { "type": "file", "file": { "file_id": "file-vid789" } }
      ]
    }
  ]
}
```

完全な例については [Files → チャット補完でファイルを参照する](/ja/api/files/#チャット補完でファイルを参照する) を参照してください。

## エラー

| ステータス | `error.code`            | 発生条件                                                                                                                                                                   |
| ---------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`      | `invalid_request_error` | フィールド欠落、未知の `purpose`、許可外の mime、申告 `bytes` が purpose ごとの上限を超過、パート本文が 64 MiB 超、`part_ids` が空または他のアップロードに属する ID を含む |
| `400`      | `invalid_request_error` | セッションに合わないパート: ファイル全体ではないのに 5 MiB 未満の最初のパート、または申告 `bytes` を超過                                                                   |
| `400`      | `invalid_request_error` | `part_ids` がアップロード順でない、同じパートを 2 回含む、最後以外に 5 MiB 未満のパートを含む、またはサイズの合計が申告 `bytes` と一致しない                               |
| `400`      | `invalid_request_error` | `pending` でないアップロード、または有効期限が切れたセッションへのパート追加や完了                                                                                         |
| `401`      | `invalid_api_key`       | `Authorization` ヘッダが欠落または無効                                                                                                                                     |
| `403`      | `zero_data_retention`   | 組織が[ゼロデータリテンション](/ja/organizations/zero-data-retention/)のため、アップロードできない                                                                         |
| `404`      | `not_found`             | 未知の `upload_id`、または別の組織に属する ID                                                                                                                              |
| `500`      | `server_error`          | 内部エラー。メッセージが新しいアップロードセッションの開始を求めている場合、そのセッションは `failed` になっており再試行できません                                         |

## 注意事項

- アップロードセッションおよび結果として生成されるファイルは、作成した **組織** にスコープされます。
- 各パートは受理された時点で永続的に保存されます。クライアントがアップロード中にクラッシュした場合は、既存の `part_ids` を一覧 (セッション有効期限まで保持) して続きから再開できます。
- レスポンスが失われた後に再送されたパート (OpenAI SDK は接続エラーや 5xx で自動的に再送します) は、使われない 2 つ目のパートになります。組み立てたい ID だけを渡してください。重複分はセッションとともに破棄されます。
- モデルはファイルの purpose に対応する capability を持つ必要があります。`purpose: "video"` には `video` 対応モデルが必要です — [`GET /v1/models`](/ja/api/models/) を参照してください。
