2

I'm using shrine gem for storing images in S3 by Ruby on Rails.

How can I make shrine behave as follows?

1. When uploading files from Frontend, they are stored in S3.
2. When deleting files from Frontend, they are deleted from Database, "but not deleted from S3"

That is, once I upload images in S3, they never be deleted by users' actions.

I found similar question, but what I want to realize is quite the opposite condition.


The present condition is like this.

I mounded shrine to ImageUploader in accordance with shrine S3 docs.

When uploading file, they are stored in S3. And when I try to delete files from Database, they are also deleted from S3.


This is the code block which uploads files to S3 and deletes from S3.

config/initializers/shrine.rb

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


s3_options = {
    access_key_id: ENV[ACCESS_KEY_ID],
    secret_access_key: ENV[SECRET_ACCESS_KEY],
    region: ENV[REGION],
    bucket: ENV[BUCKET],
}

Shrine.storages = {
    cache: Shrine::Storage::S3.new(prefix: ..., **s3_options),
    store: Shrine::Storage::S3.new(prefix: ..., **s3_options),
}


Shrine.plugin :activerecord           
Shrine.plugin :cached_attachment_data 
Shrine.plugin :restore_cached_data 

Shrine.plugin :url_options,
              cache: { public: true, host: ENV[CLOUDFRONT_URL] },
              store: { public: true, host: ENV[CLOUDFRONT_URL] }

On ImageUploader < Shrine, set resize logics. And on Photo model, mount the uploader like include ImageUploader::Attachment(:image)

When deleting files from Database, I used ActiveRecord like this.(on this point, they are also deleted from S3)

class PhotoController < ApiController

def destroy
  photo = Photo.find(params[:id])
  photo.destroy!

  render json: photo, serializer: PhotoSerializer
end
batch
  • 135
  • 1
  • 8
  • Hello @batch, and welcome. Could you please provide the code blocks that you used to "uploading file, they are stored in S3" and to "delete files from Database". That way we could actually help pointing you in the right direction. – ErvalhouS Aug 25 '20 at 16:35
  • @ErvalhouS Thanks for the reply! I just provided the code block, so I'm happy if you check the code. – batch Aug 26 '20 at 04:25
  • @ErvalhouS Thank you. The first method1 works perfectly! You saved my life! – batch Aug 26 '20 at 15:45
  • @batch did you find a solution which works with the AR callbacks, but doesn't delete the files on S3? I want to accomplish the same as you. – BvuRVKyUVlViVIc7 Sep 27 '20 at 17:18

3 Answers3

2

There are two ways you can achieve what you want.

Method 1: Skip ActiveRecord callbacks that trigger Shrine's callbacks by using delete instead of destroy on your controller.

Method 2: Add mirroring and backup to your Shrine configuration like so

 Shrine.storages = { cache: ..., store: ..., backup: ... }

 Shrine.plugin :mirroring, mirror: { store: :backup }
ErvalhouS
  • 4,178
  • 1
  • 22
  • 38
2

I found a nice and clean way...

In your shrine.rb initializer just create a custom class:

class NoDeletionShrineS3Storage < Shrine::Storage::S3
  def delete(id)
    true
  end

  def delete_prefixed(delete_prefix)
    true
  end

  def clear!(&block)
    true
  end
end


Shrine.storages = {
  cache: Shrine::Storage::FileSystem.new..), # temporary
  store: NoDeletionShrineS3Storage.new(
      bucket: "mybucket", # required
      region: "eu-central-1", # required
      access_key_id: '123..',
      secret_access_key: '123..',
    )
}

BvuRVKyUVlViVIc7
  • 11,641
  • 9
  • 59
  • 111
2

You can also use the keep_files Shrine plugin: https://shrinerb.com/docs/plugins/keep_files

Dom Eden
  • 111
  • 1
  • 2