Callback for Active Storage File Upload

Callback for Active Storage file upload

The answer from @Uleb got me 90% of the way, but for completion sake I will post my final solution.

The issue I had was that I was not able to monkey patch the class (not sure why, even requiring the class as per @user10692737 did not help)

So I copied the source code (https://github.com/rails/rails/blob/fc5dd0b85189811062c85520fd70de8389b55aeb/activestorage/app/models/active_storage/attachment.rb#L20)

and modified it to include the callback

require "active_support/core_ext/module/delegation"

# Attachments associate records with blobs. Usually that's a one record-many blobs relationship,
# but it is possible to associate many different records with the same blob. If you're doing that,
# you'll want to declare with <tt>has_one/many_attached :thingy, dependent: false</tt>, so that destroying
# any one record won't destroy the blob as well. (Then you'll need to do your own garbage collecting, though).
class ActiveStorage::Attachment < ActiveRecord::Base
self.table_name = "active_storage_attachments"

belongs_to :record, polymorphic: true, touch: true
belongs_to :blob, class_name: "ActiveStorage::Blob"

delegate_missing_to :blob

#CUSTOMIZED AT THE END:
after_create_commit :analyze_blob_later, :identify_blob, :do_something

# Synchronously purges the blob (deletes it from the configured service) and destroys the attachment.
def purge
blob.purge
destroy
end

# Destroys the attachment and asynchronously purges the blob (deletes it from the configured service).
def purge_later
blob.purge_later
destroy
end

private
def identify_blob
blob.identify
end

def analyze_blob_later
blob.analyze_later unless blob.analyzed?
end

#CUSTOMIZED:
def do_something

end
end

Not sure its the best method, and will update if I find a better solution

Do action after upload file with Active Storage

There is the dirty? method that you can use

class Post < ApplicationRecord
has_many_attached :photos

before_update :do_whatever, if: -> { photos.dirty? }

def do_whatever
# doing something
end
end

You might also be able to try before_update :do_whatever, if: -> { photos_changed? }

How to test if a new file is sent with Active Storage in the model?

You can use new_record? to check if file is new ie:

  def set_filename
if file.attached? && file.new_record?
self.file.blob.update(filename: "#{new_file_name()}.#{self.file.blob.content_type.split('/')[1]}")
end
end

Alternatively, use before_create instead of before_save so that set_name only runs when uploading new file.

Updated

Interestingly, ActiveStorage handles blob change outside model hooks. Apparently, it doesn't even support validation right now. There's no way to verify blob has changed as its state is not persisted anywhere. If you peek into rails log, notice rails purge old blob as soon as a new one is added.

Few options I can think of:

1.Update filename in controller eg:

original_name = params[:file].original_name
params[:file].original_name = # your logic goes here

2.Store blob file name in parent model and compare in before_save.

  def set_filename
if file.attached? && file.blob.filename != self.old_filename
self.file.blob.update(filename: "#{new_file_name()}.#{self.file.blob.content_type.split('/')[1]}")
end
end

None of these solutions are ideal but hope they give you some ideas.



Related Topics



Leave a reply



Submit