📁

cURLでActiveStorageのダイレクトアップロードを検証してみた

2024/06/21に公開

やりたいこと

  • ブラウザなどアプリケーション以外の所からファイルを直接S3などにアップロードし、かつそのファイルをDBで管理したい

使ったもの

処理全体の流れ

ざっくり言うと

  • 「今からこのファイルをアップロードするからそのためのURL頂戴」とRailsアプリケーションにリクエスト
  • Railsはそのリクエストを元にファイル管理用のDBテーブルにinsertしつつURLを発行
  • そのURLに対してファイルをアップロードする

実際の手順

  1. アップロード用のURLを発行するためのパラメータを用意
  2. 1で作成したパラメータを元にRailsアプリケーションでアップロード用のURLを発行
  3. URLに対してファイルをアップロード
  4. アプリケーション内で任意のモデルデータと紐づけ

※ 今回の手順はS3にアップロードする時のものです、S3との接続は言及しません

AnyModelにimageという名前でファイル管理していると仮定。

class AnyModel
  has_one_attached :image
end

1. アップロード用のURLを発行するためのパラメータを用意

必要なパラメータは以下4つ

  • ファイル名
  • バイトサイズ
  • チェックサム
  • Content−Type

チェックサムはファイルの同一性を担保するために使用する文字列で、ファイルの内容を元に作られる。
後の工程で発行したURLに対して約束通りのファイルがアップロードされているかを確認するために使われる。

ファイル内容 -> MD5ハッシュ値 -> ハッシュ値のバイナリ形式 -> Base64エンコード

という加工を行い生成する。

linux上だと以下コマンドで作成ができる。

openssl dgst -md5 -binary ./path/to/file | openssl enc -base64

2. 1で作成したパラメータを元にRailsアプリケーションでアップロード用のURLを発行

Rails内で以下のメソッドを実行する。試しにexample.pngのケースを考える。

blob = ActiveStorage::Blob.create_before_direct_upload!(
  key: "S3上のkey",
  filename: "example.png",
  byte_size: 100000,
  checksum: "#{作成したチェックサム}",
  content_type: "image/png"
)

blob.service_url_for_direct_upload(expires_in: 5.minutes.to_i)
# => 5分間有効なURLが返却される

blob.id
# => ファイル管理テーブル内でのID(blob_id)が取得できる

3. URLに対してファイルをアップロード

今回はcurlコマンドでアップロードを行う

$ curl -X PUT --upload-file ./exapmle.png \
  -H "Content-Type: image/png" \
  -H "Content-MD5: "#{作成したチェックサム}" \
  '#{返却されたURL}'

ここでチェックサムと一緒に送り、同一性を担保する。

ここで別ファイルや誤ったチェックサムで送信するとS3からエラーが返却される。

4. アプリケーション内で任意のモデルデータと紐づけ

あとはblob_idを元に任意のモデルインスタンスと紐づければOK

blob = ActiveStorage::Blob.find(blob_id)

AnyModel.find(id).image.attach(blob)

これで

AnyModel.find(id).image

とすればファイルが取得できるようになる。

SMARTCAMP Engineer Blog

Discussion