Search code examples
ruby-on-railshttp-headersjobscloudinaryrails-activestorage

How do you set a file to be uploaded to a specific folder when using Cloudinary and Active Storage?


I understand how this can be down when uploading directly to Cloudinary using the below syntax and passing the folder name as an argument.

Cloudinary::Uploader.upload("sample.jpg", :use_filename => true, :folder => "folder1/folder2")

However, I am using ActiveStorage, and when I upload a photo in the above manner it isn't attached to my Post model or associated in any way with my app.

I am using the following code to attach images

post.send(:images).attach io: StringIO.new(new_data), filename: blob.filename.to_s, 
          content_type: 'image'

It does not accept an argument to specify the folder. I have tried my best to read both ActiveStorage and Cloudinary docs in an attempt to find a way to make this work, however, I cannot seem to figure it out.

I have seen that setting a custom folder header may be a way to get this to work, but again cannot figure out how to set a custom header for the above code which takes place in the below job.

require 'tmpdir'
require 'fileutils'
require 'open-uri'
class ResizeImagesJob < ApplicationJob
  queue_as :default

  def perform(post)
    post.images.each do |image|
      blob = image.blob
      blob.open do |temp_file|
        path = temp_file.path
        pipeline = ImageProcessing::MiniMagick.source(path)
        .resize_to_limit(1200, 1200)
        .call(destination: path)
        new_data = File.binread(path)
        post.send(:images).attach io: StringIO.new(new_data), filename: blob.filename.to_s, 
                           content_type: 'image'
      end
      image.purge_later
    end
  end
end

What the above job is doing is waiting until after a post is created and then resizing and reattaching the photos to the post well deleting the originals. I am taking this approach to avoid integrity errors that take place resizing a post directly on Cloudinary after upload.

What I want to do is store the resized photos in a different folder. The reason for this is that I am using direct_upload and it is possible for users to upload photos without ever creating a post. Thus causing me to store unused, photos. This would provide an easy way to identify and deal with such images.


Solution

  • For folder names based on Rails environment

    You can dynamically set your folder on storage.yml:

    cloudinary:
      service: Cloudinary
      folder: <%= Rails.env %>
    

    And so Cloudinary will automatically create folders based on your Rails env:

    image

    This is a long due issue with Active Storage that seems to have been worked around by the Cloudinary team. Thanks for the amazing work ❤️