ruby-on-railsrails-activestorage

How to clean up ActiveStorage record and storage folder of development environment?


According to Rails (edge6.0) guides, we can do maintenance work for ActiveStorage used in System Test and Integration Test by calling the following statement respectively

  # System Test
  FileUtils.rm_rf("#{Rails.root}/storage_test")

  # Integration Test
  FileUtils.rm_rf(Rails.root.join('tmp', 'storage'))

I want to know -

Are there any Rails built-in functions or rake commands or gems to do the following?

  1. remove the orphan blobs (ActiveStorage::Blob records those are no longer associated with any ActiveStorage::Attachment record)
  2. remove the orphan files (files those are no longer associated with any ActiveStorage::Blob record)

I do not see any relevant rake tasks with rails --tasks.

Currently, I am using

# remove blob not associated with any attachment
ActiveStorage::Blob.where.not(id: ActiveStorage::Attachment.select(:blob_id)).find_each do |blob|
  blob.purge # or purge_later
end

and this script to clean orphan files (via rails console)

# run these ruby statement in project rails console
# to remove the orphan file
include ActionView::Helpers::NumberHelper

dry_run = true
files = Dir['storage/??/??/*']

orphan = files.select do |f|
  !ActiveStorage::Blob.exists?(key: File.basename(f))
end

sum = 0
orphan.each do |f|
  sum += File.size(f)
  FileUtils.remove(f) unless dry_run
end

puts "Size: #{number_to_human_size(sum)}"

Solution

  • Well there is a easier way now, not sure when it changed, but there is one caveat (it leaves the empty folder):

    # rails < 6.1
    ActiveStorage::Blob.left_joins(:attachments).where(active_storage_attachments: { id: nil }).find_each(&:purge)
    
    # rails >= 6.1
    ActiveStorage::Blob.missing(:attachments).find_each(&:purge)
    

    This will delete the record in the database and delete the physical file on disk, but leave the folders the file existed in.