# 機能設計書 74-ファイルアップロード

## 概要

本ドキュメントは、Medusaコマースプラットフォームにおけるファイル（画像等）のアップロード機能について記載する。

### 本機能の処理概要

ファイルアップロード機能は、商品画像やCSVファイルなどの各種ファイルをMedusaシステムにアップロードする機能である。アップロードされたファイルは設定されたストレージプロバイダー（ローカル、S3等）に保存され、一意のファイルキーとURLが返却される。

**業務上の目的・背景**：
ECサイトでは商品画像、カテゴリ画像、バナー画像など多数のメディアファイルを管理する必要がある。また、商品データや顧客データのCSVインポートなど、データファイルのアップロードも頻繁に行われる。本機能は、これらのファイル管理を統一的なAPIで提供し、ストレージプロバイダーを抽象化することで運用の柔軟性を確保する。

**機能の利用シーン**：
- 商品画像のアップロード
- カテゴリ・コレクションのサムネイル画像登録
- CSVファイルによる商品一括インポート
- 帳票・レポートファイルの保存
- ユーザーアバター画像のアップロード

**主要な処理内容**：
1. マルチパートフォームデータの受信
2. ファイルデータのBase64エンコード
3. ストレージプロバイダーへのアップロード
4. ファイルキーとURLの返却

**関連システム・外部連携**：
- ファイルモジュール（File Module）
- ストレージプロバイダー（S3、GCS、ローカル等）
- 商品モジュール（商品画像連携）

**権限による制御**：
- 管理者権限を持つユーザーのみ操作可能
- APIキー認証によるアクセス制御

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 11 | 商品メディア編集画面 | 補助機能 | 画像ファイルのアップロード処理 |
| 19 | 商品インポート画面 | 補助機能 | CSVファイルのアップロード |

## 機能種別

ファイル操作 / データ連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| files | File[] | Yes | アップロードするファイル（multipart/form-data） | 1件以上必須 |

### マルチパートファイルデータ

| フィールド | 型 | 説明 |
|-----------|-----|------|
| originalname | string | 元のファイル名 |
| mimetype | string | MIMEタイプ（image/jpeg等） |
| buffer | Buffer | ファイルバイナリデータ |

### 入力データソース

- 管理画面からのREST API呼び出し
- POST /admin/uploads（multipart/form-data）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| files | FileDTO[] | アップロード結果のファイル情報配列 |
| files[].id | string | ファイルキー（ストレージ内の識別子） |
| files[].url | string | ファイルアクセスURL |

### 出力先

- REST APIレスポンス（JSON形式）
- 設定されたストレージプロバイダー

## 処理フロー

### 処理シーケンス

```
1. APIリクエスト受信
   └─ POST /admin/uploads (multipart/form-data)
2. ファイルデータ取得
   └─ req.filesからMulter処理済みファイル取得
3. バリデーション
   └─ ファイル存在確認（空配列はエラー）
4. ワークフロー実行
   └─ uploadFilesWorkflow
5. ファイルデータ変換
   └─ Buffer → Base64エンコード
6. プロバイダーアップロード
   └─ FileProviderService.upload()
7. レスポンス返却
   └─ ファイルキーとURL
```

### フローチャート

```mermaid
flowchart TD
    A[APIリクエスト受信] --> B[Multerによるファイル解析]
    B --> C{ファイルあり?}
    C -->|No| D[エラー: No files uploaded]
    C -->|Yes| E[ワークフロー開始]
    E --> F[各ファイルを処理]
    F --> G[Base64エンコード]
    G --> H[プロバイダー.upload]
    H --> I{アップロード成功?}
    I -->|No| J[エラー返却]
    I -->|Yes| K[ファイルキー・URL取得]
    K --> L[レスポンス返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-74-01 | ファイル必須 | 1件以上のファイルが必須 | アップロード時 |
| BR-74-02 | 公開アクセス | アップロードファイルは public アクセスレベル | 現行実装 |
| BR-74-03 | ファイル名保持 | オリジナルのファイル名をfilenameとして使用 | アップロード時 |
| BR-74-04 | MIMEタイプ保持 | オリジナルのMIMEタイプを保持 | アップロード時 |

### 計算ロジック

#### Base64エンコード
```
content = file.buffer.toString("base64")
```

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| アップロード | - | - | DBへの直接保存なし（ストレージへ保存） |

### ストレージ操作詳細

| 操作 | プロバイダーメソッド | 入力 | 出力 |
|-----|---------------------|------|------|
| アップロード | upload() | ProviderUploadFileDTO | ProviderFileResultDTO |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 400 | INVALID_DATA | ファイルが添付されていない | ファイルを添付 |
| 401 | UNAUTHORIZED | 認証失敗 | 有効なトークンを使用 |
| 500 | INTERNAL_ERROR | プロバイダーエラー | プロバイダー設定確認 |

### リトライ仕様

ストレージプロバイダーのアップロード失敗時はワークフローエラー。クライアント側でリトライ。

## トランザクション仕様

- ファイルアップロードはストレージ操作のため、DBトランザクションなし
- ワークフローの補償アクション（ロールバック）は未定義

## パフォーマンス要件

- ファイルサイズ制限はプロバイダーとWebサーバー設定に依存
- 大容量ファイルは事前署名URLによるアップロードを推奨

## セキュリティ考慮事項

- 管理者APIのため認証必須
- MIMEタイプバリデーション（TODO: 設定で許可タイプを制限）
- アップロードファイルの公開範囲（public/private）制御

## 備考

- 現行実装ではアップロードファイルは全て "public" アクセスレベル
- 事前署名URL機能も別途提供（GET /admin/uploads/presigned-urls）
- ストリーミングアップロードも対応（getUploadStream）

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

ファイルアップロードの入出力データを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | upload-files.ts | `packages/core/core-flows/src/file/workflows/upload-files.ts` | UploadFilesWorkflowInput型定義 |

**読解のコツ**:
- filename, mimeType, content, accessの4フィールドが必要
- contentはBase64エンコード文字列
- accessは"public"または"private"

#### Step 2: エントリーポイントを理解する

API呼び出しの起点を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | route.ts | `packages/medusa/src/api/admin/uploads/route.ts` | POSTハンドラ |

**主要処理フロー**:
1. **9-34行目**: POSTハンドラの定義
2. **13行目**: `req.files`からMulter処理済みファイル取得
3. **15-20行目**: ファイル存在チェック
4. **22-31行目**: uploadFilesWorkflow呼び出し
5. **24-29行目**: ファイルデータ変換（originalname, mimetype, base64）

#### Step 3: ワークフローを理解する

アップロードのビジネスロジックを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | upload-files.ts | `packages/core/core-flows/src/file/workflows/upload-files.ts` | アップロードワークフロー |

**主要処理フロー**:
- **12-42行目**: UploadFilesWorkflowInput型定義（詳細なコメント付き）
- **72-79行目**: ワークフロー定義
- **77行目**: uploadFilesStepの呼び出し

#### Step 4: サービス層を理解する

モジュールサービスの実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | file-module-service.ts | `packages/modules/file/src/services/file-module-service.ts` | ファイルモジュールサービス |
| 4-2 | file-provider-service.ts | `packages/modules/file/src/services/file-provider-service.ts` | プロバイダーラッパー |

**主要処理フロー**:
- **36-57行目（file-module-service.ts）**: createFiles - ファイル作成メソッド
- **48-49行目**: 各ファイルをプロバイダー経由でアップロード
- **51-54行目**: key→id、url→urlのマッピング
- **37-41行目（file-provider-service.ts）**: upload - プロバイダー呼び出し

### プログラム呼び出し階層図

```
POST /admin/uploads (route.ts)
    │
    ├─ Multer (multipart解析)
    │      └─ req.files[]
    │
    └─ uploadFilesWorkflow
           │
           └─ uploadFilesStep
                  │
                  └─ FileModuleService.createFiles()
                         │
                         └─ FileProviderService.upload()
                                │
                                └─ IFileProvider.upload()
                                       └─ S3/GCS/Local etc.
```

### データフロー図

```
[入力]                    [処理]                              [出力]

multipart/form-data       uploadFilesWorkflow
{                           │
  files: [                  ├─> Multer解析                    {
    {                       │   buffer, originalname,           files: [
      originalname,         │   mimetype                          {
      mimetype,             │                                       id: "key_xxx",
      buffer                ├─> Base64エンコード                    url: "https://..."
    }                       │   buffer.toString("base64")         }
  ]                         │                                    ]
}                           ├─> プロバイダー.upload()          }
                            │   (S3, GCS, Local)
                            │
                            └─> key, url取得
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| route.ts | `packages/medusa/src/api/admin/uploads/route.ts` | ソース | アップロードエンドポイント |
| upload-files.ts | `packages/core/core-flows/src/file/workflows/upload-files.ts` | ソース | アップロードワークフロー |
| upload-files.ts | `packages/core/core-flows/src/file/steps/upload-files.ts` | ソース | アップロードステップ |
| file-module-service.ts | `packages/modules/file/src/services/file-module-service.ts` | ソース | ファイルモジュールサービス |
| file-provider-service.ts | `packages/modules/file/src/services/file-provider-service.ts` | ソース | プロバイダーラッパー |
| validators.ts | `packages/medusa/src/api/admin/uploads/validators.ts` | ソース | バリデーション定義 |
| middlewares.ts | `packages/medusa/src/api/admin/uploads/middlewares.ts` | ソース | Multerミドルウェア設定 |
