LoginSignup
0
0

More than 3 years have passed since last update.

rails6 ActionTextの関連付けされない画像データをバックグラウンド削除してみた

Posted at

はじめに

Rails6の新機能の1つのActionText(リッチテキストコンテンツと編集をかんたんに導入できる)の関連付けされてない画像データをsidekiqでバックグラウンド削除しようという内容です。
削除jobを実行するPostモデル(ポスト)と実行しないArticleモデル(記事)を使って比較していきます。

WEBページ

GitHub

環境

  • Ruby 2.6.3
  • Rails 6.0.2
  • gem aws-sdk-s3 1.60.2

問題点

こちらがActionTextを実装したPostモデルとArticleモデルのER図になります。
er-diagram.png

active_storage_blobsにアップロードした画像が保存され、投稿を完了(save)した時にactive_storage_attachmentsにaction_text_rich_textsとの関連情報が保存されます。

しかし、画像をアップロードした時点で active_storage_blobs に新規レコードが作成されてしまい関連付けされなかった場合にも残り続けてしまいます。

以下sidekiqでjobを実行していないArticleモデルでのgifです。
↓一覧画面の上部にactive_storage_attachments, active_storage_blobsを表示しています
ezgif.com-video-to-gif-compressor.gif

画像添付をやめたのにActiveStorage::Blobの数: 2に増えてしまっています。

本題

関連付けされなかったデータを削除するjobを作成します。

app/jobs/delete_unreferenced_blob_job.rb
class DeleteUnreferencedBlobJob < ApplicationJob
  sidekiq_options queue: :default, retry: 3
  require 'aws-sdk-s3' 

  def perform(*args)
    # 全Blobのidを取得
    blob_ids = ActiveStorage::Blob.pluck(:id)
    # 関連付けされているBlobの取得
    _blob_ids = ActiveStorage::Attachment.pluck(:blob_id).uniq
    # 関連付けされていないBlobの割り出し
    unreferenced_blob_ids = blob_ids - _blob_ids
    # 関連付けされていないBlobの画像ファイルを削除
    if Rails.env.production?
      s3 = Aws::S3::Resource.new(
        region: 'ap-northeast-1',
        credentials: Aws::Credentials.new(
          Rails.application.credentials.dig(:aws, :access_key_id),  # S3用アクセスキー
          Rails.application.credentials.dig(:aws, :secret_access_key)  # S3用シークレットアクセスキー
        )
      )
      bucket = s3.bucket('aws-on-rails6')
      unreferenced_blob_ids.each do |id|
        s3_file_key = ActiveStorage::Blob.find(id).key
        bucket.object(s3_file_key).delete
        bucket.objects({prefix: "variants/#{s3_file_key}"}).batch_delete!
      end
    end
    # 関連付けされていないBlobの削除
    ActiveStorage::Blob.where(id: unreferenced_blob_ids).delete_all
  end
end

本番環境のみs3の画像ファイルも削除しています。

これをPostモデルのsave, update, delete時に実行します。

posts_controller.rb
def create
    if @post.save
            DeleteUnreferencedBlobJob.perform_later
                    .
                    .
def update                                   
    if @post.update
            DeleteUnreferencedBlobJob.perform_later
                    .
                    .
def destroy
    DeleteUndreferencedBlobJob.perform_later if @post.destroy
                    .
                    .

Postモデルで先程と同じことをしてみます。
ezgif.com-video-to-gif (1).gif

ActiveStorage::Blobの数: 1となり削除されているのが確認できました!

注意点

この実装だとユーザーAが画像添付ポストを作成時、ユーザーBが別のポストをsaveやupdateした場合でもユーザーAのsave前の関連付けされていない画像データが消えてしまうので、複数ユーザーが利用するサービスではメンテナンス時などに実行するのが望ましいでしょう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0