Today I want to tell you how to do global preprocessing of uploaded files in ActiveStorage. Unfortunately ActiveStorage design doesn’t provide this feature out of the box, or I haven’t found any information about it. But sometimes you may need to process attachments for all models in a project.

To achieve this goal, we need to override several methods of singleton class of ActiveStorage::Blob class, in my version (rails 6.1) they are build_after_upload, build_after_unfurling and upload. To do this, let’s make a module, for example in lib/active_storage/blob_preprocessor.rb:

# lib/active_storage/blob_preprocessor.rb
module ActiveStorage
  module BlobPreprocessor
    def build_after_upload(io:, filename:, content_type: nil, metadata: nil, service_name: nil, identify: true, record: nil) #:nodoc:
      preprocess!(io)
      super(io: io, filename: filename, content_type: content_type, metadata: metadata, service_name: service_name, identify: identify, record: record)
    end 

    def build_after_unfurling(key: nil, io:, filename:, content_type: nil, metadata: nil, service_name: nil, identify: true, record: nil) #:nodoc:
      preprocess!(io)
      super(key: key, io: io, filename: filename, content_type: content_type, metadata: metadata, service_name: service_name, identify: identify, record: record)
    end

    def upload(io, identify: true)
      preprocess!(io)
      super(io, identify: identify)
    end

    def preprocess!(io)
      # Here we can do everything with io Tempfile, but we should not change its path.
      # Thats why this method doesn't return new Tempfile.   
    end
  end
end

Finally we have to include this module in ActiveStorage::Blob.singleton_class. We can do this in config/application.rb

#...
    config.to_prepare do
      ActiveStorage::Blob.singleton_class.include ActiveStorage::BlobPreprocessor
    end
#...