本番環境にminimagickで合成した画像を投稿する
環境
- rails7
- minimagick
- carrierwave
- s3
状況
s3にバケット名/uploads/frame.png
がアップロードしてあります
ユーザーが投稿した画像にframe.png
をminimagickで合成して投稿する
これを実現するために書いていきます。
最初のエラー
本番環境でframeを合成して投稿しようとするとErrno::ENOENT (No such file or directory @ rb_sysopen - uploads/tmp~~~
というエラーが出ました。こちらは、〜〜ていうファイルもディレクトリもないよ
というものです。
最後に全部のコードを載せるので抜粋で載せますが、当初はpost.rbに記述してある"/uploads/post/image/#{output_filename}"
に保存する予定なのにtmpという一時保存に入ってしまいました。
このエラーを含め状況を再度確認するために以下のように区分けして1つずつ確認しました。
- 開発環境でadd_frameなし
- 開発環境でadd_frameあり
- 本番環境でadd_frameなし
- 本番環境でadd_frameあり
の4つです。その結果本番環境のadd_frameあり
のみエラーになりました。
そこで、次に本番環境のadd_frameあり
のどこまでできるのかを確認しました。
- 投稿画像のみで表示する
- frameを合成して表示する
この結果、投稿画像のみでも不具合があり、修正しました。
すると次のエラーが発生しました。
2つめのエラー
やってるときは整理しながら解消できなかったので、次の解消法に行くまでにAIに質問したり、質問サイトに投稿したり、、、色々ありました。なんやかんやあり、frame合成できないけど以下のエラーが出ていました。
OpenURI::HTTPError (403 Forbidden):
初見のエラーです。
ざっくり書くとアクセス許可エラーです[こちらを参照しました](https://saturncloud.io/blog/how-to-resolve-openhttp-403-forbidden-openurihttperror-with-rails-openuri-and-amazon-s3/)
このサイトに書いてあることを1つずつやっていき解消できました。
あとがき
あっさり書きましたが、解消するのに1週間弱かかりましたw
不貞腐れてやらない日もあったり、、、
ですが、エラーが出たら区分けしてどこまで成功していてどこからエラー
なのかを分けていく作業が重要だなと思いました。
(追記)
s3の方で書名付きurl
を発行してます。こちら、時間や期間を定めてアクセス許可を出すものです。常に必要な方はバケットポリシーにあーだこーだするみたいです。(これからやります。。)
コード
class Post < ApplicationRecord
require 'aws-sdk-s3'
mount_uploader :image, ImageUploader
before_save :add_frame
private
def add_frame
return unless image.present?
# 投稿画像のパス
input_path = image.url
# 額縁画像のパス
s3 = Aws::S3::Resource.new(region: 'ap-northeast-1')
obj = s3.bucket('バケット名').object('使う画像のKey')
url = obj.presigned_url(:get)
Rails.logger.info "frame_path"
# 投稿画像を読み込む
image = MiniMagick::Image.open(input_path)
image.resize "210x280!"
Rails.logger.info "image open"
# 額縁画像を読み込む
frame = MiniMagick::Image.open(url)
frame.resize "210x280!"
Rails.logger.info "frame open"
# 投稿画像と額縁画像を合成する
result = image.composite(frame) do |c|
c.compose "Over"
end
Rails.logger.info "compose_ok"
# 合成した画像を保存する
output_filename = "#{Time.now.to_i}.png"
output_path = Rails.root.join("public", "uploads", "post", "image", output_filename)
result.write(output_path)
self.compose_image = "/uploads/post/image/#{output_filename}"
Rails.logger.info "outpath_ok"
end
end
要所にあるRails.logger.info "frame_path"
というものは、fly logs
した際にどこまで実行できたかを表示するために書いてあります。実際に動かすには全くいらないコードです。
Discussion