S3DirectMultipartUpload CI

Rails アプリに組み込み、S3 へのダイレクトマルチパートアップロード API を提供する mountable engine。認証・セッション管理はホストアプリケーション(::ApplicationController 由来の before_action 等)に委譲する。

名称と対象範囲

  • この gem は、AWS S3 の「Multipart upload」機能に対して、アップロードセッション管理と presigned URL の払い出しを行うためのエンジンです。設計は AWS ドキュメント “Multipart upload overview”(https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpuoverview.html)で説明されている CreateMultipartUploadUploadPartCompleteMultipartUpload のフローに対応しています。
  • 「direct」は、実データの転送がホストアプリケーションを経由せず、クライアントから S3 に対して直接 PUT される点(この gem は presigned URL だけを発行する)を指しています
  • 実装上、byte_size が 5MB 未満の場合は README 記載の通りマルチパートではなく単一の PutObject 用 presigned URL を返しますが、本 gem の主対象は「S3 Multipart upload を利用するようなサイズのオブジェクトを、クライアントから直接 S3 に送る」ケースです

開発方針

  • 社内利用ツールを共有するために public repository としていますが、積極的な機能追加やサポートは想定していません。
  • Issue/PR は歓迎しますが、対応はベストエフォートです。
  • クリティカルな不具合やセキュリティ連絡は [email protected] までお願いします。

インストール(GitHub 公開リポジトリから利用する場合)

Gemfile に以下を追加し、bundle install を実行する。

gem 's3_direct_multipart_upload', github: 'logmi/s3_direct_multipart_upload'

導入手順

  1. ルーティングに mount S3DirectMultipartUpload::Engine, at: '<お好みのパス>' を追加する(<mount_path>/s3_direct_multipart_uploads 系のエンドポイントが有効になる)。
  2. bin/rails s3_direct_multipart_upload:install:migrations でマイグレーションをホストへコピーし、bin/rails db:migrate を実行する。

設定

  • バケットやリージョンなどの S3 接続情報は、ホストアプリの config/storage/{env}.yml に定義した s3_direct_multipart サービスを参照する。
    • 例 (production.yml):
      s3_direct_multipart:
        service: S3
        bucket: your-bucket
        region: ap-northeast-1
        endpoint: https://s3.ap-northeast-1.amazonaws.com # 任意
        force_path_style: false # 必要に応じて
      
    • 例 (development/test):
      s3_direct_multipart:
        service: Disk
      
    • s3_direct_multipart が未定義の場合は起動時に例外となる。
  • キープレフィックス: uploads/s3_direct/yyyyMMdd/<session_uuid> 形式で自動付与する。

セキュリティ・運用上の注意

  • エンジン側では skip_forgery_protection を有効化しており、認証/認可やレート制御はホストアプリの ::ApplicationController 由来の before_action 等に委譲する前提です。CSRF 対策や利用権限チェックが必要な場合は mount 先で必ずガードを追加してください。
  • UploadSession#expires_at を設定するだけで、期限切れセッションのクリーンアップや abort_multipart_upload は未実装です。S3 上に未完了アップロードが蓄積しないよう、ホスト側で定期バッチを用意して後始末してください。

ファイルサイズとパート数の扱い

  • chunk_size は 5MB 以上を推奨し、5GB 以下に収めること(S3 制約)。byte_size に対して期待パート数が 10,000 を超える場合はアップロードを拒否します。
  • byte_size が 5MB 未満の場合はマルチパートを使わず単一パートとして扱い、1 件の presigned URL だけを返します(chunk_sizebyte_size に自動調整)。

エンドポイント

  • POST <mount_path>/s3_direct_multipart_uploads
  • POST <mount_path>/s3_direct_multipart_uploads/:upload_session_id/parts
  • POST <mount_path>/s3_direct_multipart_uploads/:upload_session_id/complete

レスポンス仕様は doc/s3_direct_multipart_upload/api.yml を参照。

ドキュメント導線

配布形態

  • Rubygems で公開(gem 's3_direct_multipart_upload')。ソースリポジトリは現在 private なので、利用者は .gem に同梱されたドキュメント(README/SPEC/OPERATIONS/AGENTS と OpenAPI 定義)を参照してください。
  • GitHub の issue/PR はベストエフォート対応。問い合わせは README 冒頭のメールアドレスまで。

開発・貢献のはじめかた

  • セットアップ: bundle install を実行(必要なら gem update --system で Rubygems を最新化)
  • 開発用設定: config/storage/development.yml / config/storage/test.yml にある s3_direct_multipart (Disk) を利用する前提で動くため追加設定は不要
  • テスト: bundle exec rspec を実行
  • マイグレーション: ホスト側では bin/rails s3_direct_multipart_upload:install:migrationsbin/rails db:migrate を実行

Disk サービス利用時の開発用エンドポイント

  • service: Diskstub_responses: true)のまま presigned URL に対して PUT できるよう、開発/テスト環境限定で PUT /s3_direct_multipart_upload/dev/storage/*path を用意しています。
  • 環境変数:
    • S3_DIRECT_DEV_ENDPOINT: presigned URL に埋め込むベース URL。未設定時は http://localhost:3000
    • S3_DIRECT_DEV_SECRET: 署名生成に使うシークレット。未設定時は dev-secret
  • 使い方:
    1. ActiveStorages3_direct_multipartservice: Disk で設定する。
    2. UploadSession#presigned_parts が返す URL(クエリに uploadId, partNumber, expires_at, signature を含む)に対し、クライアントから PUT する。
    3. リクエストボディは tmp/s3_direct_multipart_upload/<object_key>.parts/<partNumber> に保存され、ETag ヘッダーが返る。
  • 本番環境ではルート自体が無効になるため、開発専用の安全な代替として利用できます。
  • Disk + stub_responses 利用時の upload_id 重複対策: development/test では config/initializers/s3_direct_dev.rbcreate_multipart_upload をユニークな upload_idSecureRandom.uuid)で stub しています。既存セッションが残っている場合でも一意制約に抵触しなくなります。
  • ダウンロード確認: development/test では UploadSession#complete! 時にパートファイルを結合し、tmp/s3_direct_multipart_upload/<object_key> に単一ファイルを生成します。GET /s3_direct_multipart_upload/dev/storage/:upload_session_id/download にアクセスすると結合済みファイルを返します(completed セッションのみ許可、欠損時は 422)。

ライセンス

MIT