ファイルストレージ
RailwayのS3互換ストレージバケットを使ったファイルのアップロードと配信。
アプリがユーザーアップロードファイル(画像、PDF、エクスポート)を保存する必要があるときは、RailwayのS3互換ストレージバケットを使います。ファイルはデフォルトでプライベートで、短時間有効な署名付きURLでユーザーに配信します。
リファレンス実装
クライアントを自作しないでください。このファイルをそのままアプリにコピーします:
apps/staff-portal/app/_lib/storage.ts → apps/<your-app>/app/_lib/storage.ts
2つの関数をエクスポートします: createStorageClientFromEnv()(環境変数を読み、設定済みのS3クライアントを返す)とgetPresignedUrl(storage, key, expiresIn)(時間制限付きのダウンロードURLを生成)。
依存関係を追加
アプリのpackage.jsonに:
package.jsonjson
{
"dependencies": {
"@aws-sdk/client-s3": "^3.709.0",
"@aws-sdk/s3-request-presigner": "^3.709.0"
}
}モノレポルートから pnpm install。
環境変数
以下4つがすべて必要です。環境ごとにRailwayでGitHub secret経由で設定します — 環境変数を参照。
| 変数 | 目的 |
|---|---|
BUCKET_ENDPOINT | S3互換APIのエンドポイント |
BUCKET_ACCESS_KEY_ID | 認証情報 |
BUCKET_SECRET_ACCESS_KEY | 認証情報 |
BUCKET_NAME | 読み書き対象のバケット |
オプション:
BUCKET_REGION— デフォルトはus-east-1。
必須変数が欠けていると、createStorageClientFromEnv()がMissing storage environment variablesをスローします。
クライアントの使い方
サーバー側での利用typescript
import {
createStorageClientFromEnv,
getPresignedUrl,
} from '@/app/_lib/storage';
const storage = createStorageClientFromEnv();
// 7日間有効な署名付きURLを生成
const url = await getPresignedUrl(
storage,
'my-app/photos/avatar.png',
3600 * 24 * 7
);
// URLをブラウザに返す — ブラウザは直接ファイルを取得できる。バケット構成
すべてのキーにアプリ名のプレフィックスを付けてファイルをグループ化します:
my-app/photos/...
checkin-board/attendances/...
staff-portal/documents/...
こうすることで、1つのバケットが複数アプリを綺麗に扱えます。
つまづきポイント(一度は読んでおく価値あり)
これらはリファレンスのstorage.tsに組み込み済みですが、「修正」してしまわないよう覚えておきましょう:
BUCKET_ENDPOINTの末尾スラッシュを削除 — 末尾スラッシュがあるとSignatureDoesNotMatchになります。forcePathStyle: trueがRailwayのS3互換APIには必須です。- チェックサム計算は
'WHEN_REQUIRED'に設定する必要があります — AWS SDK v3 ≥ 3.600はデフォルトでチェックサムヘッダを追加し、RailwayのTigrisは403を返します。
Claudeに聞く
新規アプリでファイルアップロードを設定
Claudeプロンプトmeal-plannerアプリにファイルアップロード機能を追加してください。staff-portalからstorage.tsをコピー、@aws-sdk依存関係を追加、「meal-planner/photos/」プレフィックスで画像を保存するPOST /api/uploadルートを作成してください。24時間有効な署名付きダウンロードURLを返してください。
クイズ
Quiz
バケットに保存されたファイルをユーザーにダウンロードさせる正しい方法は?