4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Ruby on Rails】ActiveStrageでs3の画像も合わせて削除

Posted at

目標

ローカルだけでなく、s3上の画像も削除する

前提

ActiveStrageでの画像削除について
詰まってしまったので、備忘録として残します。
(手探りでの記述のため、無駄があるかと思いますがご了承ください)

また、s3上の不必要な画像を削除していきます。

postモデル
カラム name:string
アクション new index show edit update create destroy
ルート resouses :posts

開発環境

ruby 2.5.7
Rails 5.2.4.3
OS: macOS Catalina
heroku

ActiveStrageでの画像投稿導入

【Rails 5.2】 Active Storageの使い方
こちらが大変わかりやすく、導入もしやすかったので、
こちらを参考に導入してください。

【Rails】Herokuで画像を投稿できるようにする方法(ActiveStorage + Amazon S3)
herokuにデプロイする際はこちらも参考にしてください。

ActiveStrageでの画像削除について

ここらが本題です。
Railsガイドには削除方法が記載されており、purgeメソッドを使用します。
このpurgeメソッドを使うことにより、railsアプリから直接s3の画像の削除が可能です。

# avatarと実際のリソースファイルを同期的に破棄します。
user.avatar.purge

# Active Jobを介して、関連付けられているモデルと実際のリソースファイルを非同期で破棄します。
user.avatar.purge_later

1枚の画像削除について

s3の画像を削除しないといけないケースは
①画像を更新する場合②画像が紐づく投稿が削除された場合が考えられます。
また理由はまだ解明出来ていませんが、purge_laterではなく、purgeを使用すると
destroyアクションのdestroyがされず、if文がfalseとなったため、
purge_laterを使用するとうまくいきました。
1枚の画像削除についてはpurge_laterで非同期の破棄が必要かもしれません。

@post.image.purge_later && @post.destroyの順番は
先に@post.destroyをしてしまうと@post.image.purge_laterが
awsのサーバートラブル時に影響が出るかと思い、このような順番にしています。

app/controllers/posts_controller.rb
  def update
    @post = Post.find(params[:id])
    if @post.image.attached?
      @post.image.purge_later
    end
    if @post.update(post_params)
      redirect_to post_path(@post)
    else
      render :edit
    end
  end

  def destroy
    @post = Post.find(params[:id])
    if @post.image.attached?
      if @post.image.purge_later && @post.destroy
        redirect_to posts_path
      else
        render :edit
      end
    else
      if @post.destroy
        redirect_to posts_path
      else
        render :edit
      end
    end
  end

  private
  def post_params
    params.require(:post).permit(:name, :image)
  end

複数画像削除について

Active Storageで複数画像の投稿・削除
こちらの記事を参考に実装してみましたが、
私の開発環境では、
ActiveSupport::MessageVerifier::InvalidSignature
というエラーになり解決出来なかったため、
画像の削除についてはすべて削除することとしました。
そのためimg_destroyアクションを新たに追加しました。

また、1枚の画像削除とは逆に複数画像削除はpurge_laterを使用すると、
たまにエラー(詳しくは覚えていませんが、処理が多すぎる?みたいなエラー)が
出ることがあるものの、purgeを使用するとうまくいきました。

@post.image.purge && @post.destroyの順番は
先に@post.destroyをしてしまうと@post.image.purgeが
awsのサーバートラブル時に影響が出るかと思い、このような順番にしています。

  def update
    @post = Post.find(params[:id])
    if @post.update(post_params)
      redirect_to post_path(@post)
    else
      render :edit
    end
  end

  def destroy
    @post = Post.find(params[:id])
    if @post.images.attached?
      if @post.images.purge && @post.destroy
        redirect_to posts_path
      else
        render :edit
      end
    else
      if @post.destroy
        redirect_to posts_path
      else
        render :edit
      end
    end
  end

  def img_destroy
    @post = Post.find(params[:id])
    if @post.images.purge
      redirect_to edit_post_path(@post)
    else
      render :edit
    end
  end

  private
  def post_params
    params.require(:post).permit(:name,images: [])
  end
config/routes.rb
delete 'post_image_delete/:id', to: 'posts#img_destroy', as: 'post_img_destroy'
app/views/posts/edit.html.erb
<% if @post.images.attached? %>
  <%= link_to "全削除", post_img_destroy_path(@post), method: :delete, "data-confirm" => "削除してもよろしいでしょうか?", class: "btn btn-danger" %>
<% end %>

まとめ

ActiveStrageでs3の画像も合わせて削除する方法でしたが、
手探りのため、より良い記述方法があるかもしれません。

私自身は、gem refile-s3のエラーが解決できなかったため、ActiveStorageに移行したため
同じ状況の人の少しでも力になれたら幸いです。詳しくはこちら

またtwitterではQiitaにはアップしていない技術や考え方もアップしていますので、
よければフォローして頂けると嬉しいです。
詳しくはこちら https://twitter.com/japwork

4
3
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
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?