Shrineのuploaderごとに事前署名付きURLを設定しuppyと組み合わせて使う

Shrine のpresign_endpointを使うと S3 へのダイレクトアップロードを実装できます。

plugin :presign_endpoint
routes.rb
Rails.application.routes.draw do
  mount Shrine.presign_endpoint(:cache) => "/s3/presign"
end

shrine/direct_s3.md at master · shrinerb/shrine

Shrine は uppy という JavaScript のファイルアップローダーとの相性が良く、Shrine でダイレクトアップロードを実装する際には、下記のように書くことで対応できます。

uppy.use(Uppy.AwsS3, {
  companionUrl: "/s3/presign/",
});

複数の uploader でアップロードするディレクトリの出し分けをしつつ、uppy との組み合わせでダイレクトアップロードを実現したい場合の設定方法がネットにも情報がほぼなかったので書いておきます。

前提

やりたいこと

  • Shrine の presign_endpoint を使って事前署名付き URL を発行し、uppy と組み合わせて使う
  • Shrine の uploader ごとにアップロードするディレクトリを変える
    • VideoUploader → /videos
    • ImageUploader → /images
    • みたいな感じ

uploader ごとにディレクトリを分ける

Shrine 側の設定で uploader ごとにディレクトリを変えるのはそこまで複雑ではなく、以下の 2 通りのやり方があります。

  • プラグインの default_storage を使う
  • uploader ごとに @storages 変数を上書きする

参考:

@storagesを使った実装で動作確認をしましたが、やっていることは同じなのでdefault_storageでも適用できると思います。default_storage を使った実装例は GitHub で検索するといくつかヒットします。
Search · "plugin :default_storage"

uppy の companionUrl に合わせるため routes.rb の設定を変更

今回は uploader ごとにアップロードするディレクトリを変えたいので、事前署名付き URL も uploader ごとに別のものを設定する必要があります。また、routes.rb で設定する内容は、uppy の companionUrl とも関連するので、まず uppy の方から見ていきます。

uppy の companionUrl に設定したパスには、自動で /s3/params が付与されます。これはドキュメントなどに書いてあったとかではなく、動作確認する中で分かったことです。GitHub で調べてみたのですが、関連する実装としてはこの辺りかなと思います。

uppy/index.js at transloadit/uppy

事前署名付き URL を発行するエンドポイントを uploader ごとに動的に設定するため、以下では引数を式展開しています。

// uploader は引数で指定している想定
// 例: uploader = "videos"を受け取った場合 /presign/videos/s3/params となる
uppy.use(Uppy.AwsS3, {
  companionUrl: `/presign/${uploader}`,
});

routes.rb の設定も上記のエンドポイントに合わせて設定します。presign_endpointは uploader ごとの設定が可能です。

routes.rb
# uploader ごとに presign_endpoint を設定
# 末尾に /s3/params と付けているのはuppyのcompanionUrlに合わせるため
mount VideoUploader.presign_endpoint(:cache) => '/presign/videos/s3/params'
mount ImageUploader.presign_endpoint(:cache) => '/presign/images/s3/params'

Presign Endpoint Setup · Shrine