📁
cURLでActiveStorageのダイレクトアップロードを検証してみた
やりたいこと
- ブラウザなどアプリケーション以外の所からファイルを直接S3などにアップロードし、かつそのファイルをDBで管理したい
使ったもの
- Active Storageのダイレクトアップロードという機能を使用
- 今回はJSを使わずにcurlコマンドによる検証を行った
処理全体の流れ
ざっくり言うと
- 「今からこのファイルをアップロードするからそのためのURL頂戴」とRailsアプリケーションにリクエスト
- Railsはそのリクエストを元にファイル管理用のDBテーブルにinsertしつつURLを発行
- そのURLに対してファイルをアップロードする
実際の手順
- アップロード用のURLを発行するためのパラメータを用意
- 1で作成したパラメータを元にRailsアプリケーションでアップロード用のURLを発行
- URLに対してファイルをアップロード
- アプリケーション内で任意のモデルデータと紐づけ
※ 今回の手順は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
とすればファイルが取得できるようになる。
Discussion