3

I managed to upload files to s3 using shrine, but I'm trying to upload each photo to a different folder according to the album it belongs to.

Lets say i have a bucket named: abc:

Uploading images to the album: family should upload images to: abc/family/...

Uploading images to the album: friends should upload images to: abc/friends/...

I didn't find a way to do it in Shrine.storages in the initializer file.

I guess the way to do it is with default_storage and dynamic_storage plugins somehow, but i didn't succeed doing it yet.

any suggestions / solution?

Thanks a lot :)

Relations: Album has_many :photos Photo belongs_to :album

Photo class has image_data field for Shrine.

my code in the initializer: (basic stuff)

s3_options = {
  access_key_id:     ENV["S3_KEY"],
  secret_access_key: ENV["S3_SECRET"],
  region:            ENV["S3_REGION"],
  bucket:            ENV["S3_BUCKET"],
}

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

EDIT:

I found out there is a plugin named: pretty_location which adds a nicer folder structure, but its not exactly what i need, it adds /Photo/:photo_id/image/:image_name under the bucket, but i need the album name instead.

Ziv Galili
  • 1,405
  • 16
  • 20

2 Answers2

15

I did it!

by overriding generate_location in the ImageUploader file:

class ImageUploader < Shrine
  def generate_location(io, context = {})
    album_name  = context[:record].album_name if context[:record].album
    name  = super # the default unique identifier

    [album_name, name].compact.join("/")
  end
end

this will upload the files to: :bucket_name/storage/:album_name/:file_name

If you want other folder then "storage" you need to change the prefix under the Shrine.storages in the initializer file.

You might want to use parameterize on the field_name (in my case album_name.parameterize) so you wont have spaces and unwanted characters in the path.

For anyone out there looking for the answer! thats what worked for me, Enjoy.

If you have other working/better solution, please post it as well. thanks.

Ziv Galili
  • 1,405
  • 16
  • 20
  • This is not working if I use `Tus Server` with `Shrine`. Tus may be overriding Shrine configs. Any idea how we can do this using Tus Server? – Abhi May 30 '20 at 07:10
  • For this to work we must use `ImageUploader.upload` method or just using `photo.save`? – Abhi May 30 '20 at 13:28
0

Here is an example with even more customization reading from a JSON file

{"ID":"14","post_author":"2","guid":"wp-content\/uploads\/2012\/02\/Be-True-Website-Logo3.png","post_type":"attachment","post_parent":"13"}

This allows you to get the location of the guid by running a each loop like this

require 'json'
require 'pry'

file = File.read('files.json')
array = JSON.parse(file)
posts = array[2]["data"] #please change this to match your JSON file

posts.each do |post|
  post_id = post['post_parent']
  if Post.find_by_id(post_id)
    p = Post.find(post_id)
    g = Graphic.new(post_id: post_id)
    g.graphic = File.open(post["guid"])
  else
    g = Graphic.new
    g.graphic = File.open(post["guid"])
  end
  g.save
end

You can run the above file in rails console as well..

Below is what your Uploader should look like...

class GraphicUploader < Shrine
  include ImageProcessing::MiniMagick
  plugin :activerecord
  plugin :cached_attachment_data # for retaining the cached file across form redisplays
  plugin :restore_cached_data # re-extract metadata when attaching a cached file
  plugin :determine_mime_type
  plugin :remove_attachment

  def generate_location(io, context = {})
    name  = super # the default unique identifier

    if io.is_a?(File)
      initial_location  = io.to_path
      new_location      = initial_location.match(/(.*\/).*/)[1]
      final_location = [new_location + name].compact.join # returns original path
    else
      foo = [io.id].compact.join.to_s
    end

    end
  end
end
gustavoanalytics
  • 584
  • 6
  • 18