目標
ローカルだけでなく、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のサーバートラブル時に影響が出るかと思い、このような順番にしています。
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
delete 'post_image_delete/:id', to: 'posts#img_destroy', as: 'post_img_destroy'
<% 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