# 機能設計書 30-Data Streaming

## 概要

本ドキュメントは、Ruby on Railsのデータストリーミング機能の設計仕様を定義する。Data Streamingは、大容量ファイルのダウンロード配信やバイナリデータの送信を効率的に行うための機能である。

### 本機能の処理概要

**業務上の目的・背景**：Webアプリケーションにおいて、大容量のファイル（PDF、画像、動画、CSVエクスポート等）をダウンロードさせる場合、ファイル全体をメモリにロードすることは非効率である。Data Streaming機能は、send_file/send_dataメソッドを提供し、ファイルを効率的にストリーミング配信することで、メモリ使用量を抑えつつ高速なファイル配信を実現する。

**機能の利用シーン**：ファイルダウンロード、画像表示、PDF配信、CSVエクスポート、動画ストリーミングなど、バイナリデータをクライアントに送信する場面で使用する。

**主要な処理内容**：
1. ファイルパスの検証と読み取り権限確認
2. Content-Type、Content-Dispositionヘッダーの設定
3. Rack::Sendfileを使用したX-Sendfile最適化（サーバー設定時）
4. ファイルのストリーミング送信
5. バイナリデータのインライン送信

**関連システム・外部連携**：Rack::Sendfile（X-Sendfile最適化）、Webサーバー（nginx/Apache）のsendfile機能と連携する。

**権限による制御**：ファイルアクセス権限はアプリケーションロジックで制御。ファイルパスのサニタイズが重要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 7 | メールプレビュー画面 | 補助機能 | EMLファイルダウンロード（send_data）処理 |

## 機能種別

ファイル配信 / データ送信

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| path | String | Yes (send_file) | ファイルパス | 存在し読み取り可能であること |
| data | String | Yes (send_data) | バイナリデータ | - |
| options[:filename] | String | No | ダウンロード時のファイル名 | - |
| options[:type] | String/Symbol | No | Content-Type | MIMEタイプまたはシンボル |
| options[:disposition] | String | No | Content-Disposition | "inline" または "attachment" |
| options[:status] | Integer | No | HTTPステータスコード | 有効なHTTPステータス |
| options[:url_based_filename] | Boolean | No | URLからファイル名を推測させるか | true/false |

### 入力データソース

- ファイルシステム上のファイル
- メモリ上のバイナリデータ
- データベースから取得したBLOBデータ

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| body | String/File | ファイルまたはバイナリデータ |
| Content-Type | String | MIMEタイプ |
| Content-Disposition | String | インライン表示またはダウンロード |
| Content-Transfer-Encoding | String | "binary" |

### 出力先

- HTTPレスポンスボディ
- ブラウザ（ダウンロードまたはインライン表示）

## 処理フロー

### 処理シーケンス

```
1. send_file / send_data 呼び出し
   └─ オプションの正規化
2. ファイル検証（send_fileの場合）
   └─ ファイル存在・読み取り権限確認
3. ヘッダー設定
   └─ send_file_headers! でヘッダー生成
   └─ Content-Type 決定
   └─ Content-Disposition 設定
4. レスポンス送信
   └─ send_file: response.send_file
   └─ send_data: render body: data
5. Rack::Sendfile処理（設定時）
   └─ X-Sendfileヘッダー変換
   └─ Webサーバーが直接配信
```

### フローチャート

```mermaid
flowchart TD
    A[send_file/send_data呼び出し] --> B{send_file?}
    B -->|Yes| C[ファイル存在確認]
    B -->|No| D[send_file_headers!]
    C --> E{ファイルあり?}
    E -->|No| F[MissingFile例外]
    E -->|Yes| G[ファイル名決定]
    G --> D
    D --> H[Content-Type設定]
    H --> I[Content-Disposition設定]
    I --> J[Content-Transfer-Encoding設定]
    J --> K{send_file?}
    K -->|Yes| L[response.send_file]
    K -->|No| M[render body: data]
    L --> N[Rack::Sendfile処理]
    M --> O[レスポンス返却]
    N --> O
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-30-01 | デフォルトContent-Type | application/octet-stream | type未指定時 |
| BR-30-02 | デフォルトDisposition | attachment | disposition未指定時 |
| BR-30-03 | ファイル名推測 | 拡張子からMIMEタイプを推測 | type未指定でfilename指定時 |
| BR-30-04 | X-Sendfile最適化 | Webサーバーにファイル配信を委譲 | config設定時 |

### 計算ロジック

Content-Type決定ロジック：
```ruby
# 1. options[:type] が指定されていればそれを使用
# 2. options[:filename] の拡張子からMIMEタイプを推測
# 3. どちらもなければ application/octet-stream
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | データベース操作なし |

### テーブル別操作詳細

本機能はデータベースを直接操作しない。ファイルシステムまたはメモリからデータを読み取る。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| MissingFile | ActionController::MissingFile | ファイルが存在しないか読み取り不可 | ファイルパスを確認 |
| ArgumentError | ArgumentError | type が nil | typeオプションを指定 |

### リトライ仕様

リトライ機構は提供されない。エラー時は適切なHTTPステータスを返す。

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

本機能はデータベーストランザクションを使用しない。

## パフォーマンス要件

- 大容量ファイルはX-Sendfileを使用してWebサーバーから直接配信
- メモリにファイル全体をロードしない（send_file使用時）
- send_dataは小〜中サイズのデータ向け

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

- ユーザー入力をファイルパスに使用しないこと（パストラバーサル防止）
- ファイルアクセス権限を適切に検証すること
- Content-Dispositionでファイル名をサニタイズすること
- 機密ファイルへのアクセス制御を実装すること

## 備考

- send_fileはファイルシステム上のファイル向け
- send_dataはメモリ上のデータ向け
- 動的生成ファイルはsend_dataを使用
- 静的ファイルはWebサーバーからの直接配信を検討

---

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

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

### 推奨読解順序

#### Step 1: DataStreamingモジュールの基本構造を理解する

データストリーミングのエントリーポイントを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | data_streaming.rb | `actionpack/lib/action_controller/metal/data_streaming.rb` | send_file、send_data、send_file_headers! |

**読解のコツ**: DataStreamingモジュールはActionController::Renderingをincludeし、renderメソッドを利用している。

**主要処理フロー**:
- **18-19行目**: デフォルト値の定数定義
- **77-86行目**: send_file でファイル送信
- **122-125行目**: send_data でバイナリデータ送信
- **127-152行目**: send_file_headers! でヘッダー設定

#### Step 2: send_fileメソッドを理解する

ファイル送信処理の詳細を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | data_streaming.rb | `actionpack/lib/action_controller/metal/data_streaming.rb` | send_file メソッド |

**主要処理フロー**:
- **78行目**: File.file? && File.readable? でファイル検証
- **80行目**: url_based_filename でない場合にFilenameを設定
- **81行目**: send_file_headers! でヘッダー設定
- **83-85行目**: status、content_type設定とresponse.send_file呼び出し

#### Step 3: send_dataメソッドを理解する

バイナリデータ送信処理の詳細を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | data_streaming.rb | `actionpack/lib/action_controller/metal/data_streaming.rb` | send_data メソッド |

**主要処理フロー**:
- **123行目**: send_file_headers! でヘッダー設定
- **124行目**: render でbodyをレンダリング

#### Step 4: ヘッダー設定を理解する

Content-Type、Content-Dispositionの設定処理を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | data_streaming.rb | `actionpack/lib/action_controller/metal/data_streaming.rb` | send_file_headers! メソッド |

**主要処理フロー**:
- **130-131行目**: Content-Type設定
- **134行目**: type が nil なら ArgumentError
- **136-144行目**: 拡張子からMIMEタイプを推測
- **146-149行目**: Content-Disposition設定
- **151行目**: Content-Transfer-Encoding設定

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

```
Controller Action
    │
    └─ include ActionController::DataStreaming
           │
           ├─ send_file(path, options) (77行目)
           │      │
           │      ├─ File.file?(path) && File.readable?(path) (78行目)
           │      │
           │      ├─ send_file_headers!(options) (81行目)
           │      │      │
           │      │      ├─ content_type設定 (130-144行目)
           │      │      │
           │      │      ├─ Content-Disposition設定 (146-149行目)
           │      │      │
           │      │      └─ Content-Transfer-Encoding設定 (151行目)
           │      │
           │      └─ response.send_file(path) (85行目)
           │             └─ Rack::Sendfile処理
           │
           └─ send_data(data, options) (122行目)
                  │
                  ├─ send_file_headers!(options) (123行目)
                  │
                  └─ render body: data (124行目)
```

### データフロー図

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

ファイルパス ───▶ send_file ───▶ HTTPレスポンス
      │                │
      │                ▼
      │         ファイル検証
      │                │
      │                ▼
      │         ┌─────────────────┐
      │         │ ヘッダー設定     │───▶ Content-Type
      │         │ send_file_headers│───▶ Content-Disposition
      │         └─────────────────┘
      │                │
      │                ▼
      │         ┌─────────────────┐
      │         │ Rack::Sendfile  │
      │         │ (X-Sendfile)    │───▶ Webサーバー直接配信
      │         └─────────────────┘

バイナリデータ ───▶ send_data ───▶ HTTPレスポンス
      │                │
      │                ▼
      │         ヘッダー設定
      │                │
      │                ▼
      └──────────▶ render body: data
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| data_streaming.rb | `actionpack/lib/action_controller/metal/data_streaming.rb` | ソース | DataStreamingモジュール本体 |
| content_disposition.rb | `actionpack/lib/action_dispatch/http/content_disposition.rb` | ソース | Content-Disposition生成 |
| response.rb | `actionpack/lib/action_dispatch/http/response.rb` | ソース | HTTPレスポンスクラス |
| streaming.rb | `activestorage/app/controllers/concerns/active_storage/streaming.rb` | ソース | ActiveStorage用ストリーミング |
| exceptions.rb | `actionpack/lib/action_controller/metal/exceptions.rb` | ソース | MissingFile例外定義 |
