Uploader not using correct storage despite default_storage plugin


I’ve got a Filesystem cache and store, and now I’m trying to add an S3 store because I wish to add direct S3 uploading.

My Uploader is nice and straight-forward:

class AssetUploader < Shrine
  plugin :default_storage, cache: :s3_cache, store: :s3_store

My initializer/shrine.rb

require "shrine"
require "shrine/storage/file_system"
require "shrine/storage/memory"
require "shrine/storage/s3"

s3_options = {
  bucket: 'foo',
  region: 'us-east-1',
  access_key_id: 'access_key_id',
  secret_access_key: 'secret_access_key'

Shrine.storages = {
  cache: Shrine::Storage::FileSystem.new("tmp/shrine", prefix: "uploads-cache"),
  store: Shrine::Storage::FileSystem.new("tmp/shrine", prefix: "uploads"),
  s3_cache: Shrine::Storage::S3.new(prefix: "cache", **s3_options),
  s3_store: Shrine::Storage::S3.new(**s3_options)

Shrine.plugin :activerecord
Shrine.plugin :backgrounding
Shrine.plugin :cached_attachment_data # for retaining the cached file across form redisplays
Shrine.plugin :default_storage
Shrine.plugin :remove_invalid
Shrine.plugin :restore_cached_data # re-extract metadata when attaching a cached file
Shrine.plugin :validation

Shrine.plugin :presign_endpoint, presign_options: -> (request) {
  # Uppy will send the "filename" and "type" query parameters
  filename = request.params["filename"]
  type     = request.params["type"]

    content_disposition:    ContentDisposition.inline(filename), # set download filename
    content_type:           type,                                # set content type (defaults to "application/octet-stream")
    content_length_range:   0..(10*1024*1024),                   # limit upload size to 10 MB

Finally my routes contains (amongst many other things):

Rails.application.routes.draw do
  # ...
  mount AssetUploader.presign_endpoint(:cache) => "/s3/params"
  # ...

A lot of this is based off the Direct S3 upload wiki page. Hence I’ve got my form and the Uppy JS all hooked up but it fails on the presign request:

Started OPTIONS "/s3/params?filename=Screenshot%202021-07-29%20at%2012.29.45.png&type=image%2Fpng&" for at 2021-08-22 20:18:00 +0100
Started GET "/s3/params?filename=Screenshot%202021-07-29%20at%2012.29.45.png&type=image%2Fpng&" for at 2021-08-22 20:18:00 +0100
NoMethodError (undefined method `presign' for #<Shrine::Storage::FileSystem:0x00007f857e8f7e00>

The thing that jumps out is the reference to Shrine::Storage::FileSystem. As you recall, my uploader has the default_storage plugin configured to use the s3 storages yet it seems it’s not taking that in to account.

Have I misunderstood the default_storage plugin, or the presign_endpoint plugin, or both?

Thanks in advance!

You have defined the default storage plugin in two locations:

  1. your AssetUploader and secondly
  2. initializer/shrine.rb - try *removing the default storage plugin from this file - or otherwise configure it with the required defaults:Default Storage · Shrine

Ah, I see what you mean. I think I was of the misguided view that in the initializer/shrine.rb I had to declare the plugins I wanted to leverage.

I’m most grateful for your suggestion – thanks for taking time and effort to respond – but with regret that additional plugin declaration wasn’t the source the problems: it resulted in the same error.

However, I did decide to try out an idea – pure guesswork though – the mount in the routes.rb refers to the .presign_endpoint() method and takes an argument. I’d been following the Shrine docs and Wiki pages and blindly passed :cache as the argument, because I was assuming it was the cache concept, and not the storage named :cache. So I changed it to

mount AssetUploader.presign_endpoint(:s3_cache) => "/s3/params"

where the argument is now the name of the storage relating to my S3 based cache storage. And this resolves the Shrine error. (I’m now getting CORS errors instead but at least that’s an S3+JS issue rather than Shrine).

I think one of the complexities with Shrine is how there’s the concept of cache and store, and there are storage engines that are assigned to them, and in the majority of docs and sample code, they are also named cache and store to match the concepts. In fairness to the presign_endpoint plugin docs, on re-reading it does say the argument is a storage identifier. But nonetheless, it’s pretty hard to quickly determine when cache/store is conceptual or an identifier.

Thanks again!

glad you found it. mine was at best a guess. I did not actually try with a script, which i prob should have.