Reactで実装したフロントエンドから画像をコントローラーに送ってActiveStorageに保存したい
Rails × React SPAの作成に当たり画像データをActiveStorageで扱いたい。しかしActiveStorageに保存するデータ形式は
reactで直接扱えないのでBase64を用いたencodingとdecodeを用いて保存を行う。
//画像は事前にvalueを取得できないのでFileReaderを使ってデータを読み取る
const file = e.target.files[0];
const reader = new FileReader()
reader.onload = () => {
//以下2つはactivestorageの保存に必要
user.avatar.data = reader.result
user.avatar.filename = file.name
}
reader.readAsDataURL(file) //戻り値はata:image/jpeg;base64,/9j/4AA…のようになる。これをサーバーサイドで複合する
こんな感じの記述でフロント側で画像を読み込むとbase64形式でdataが取得できる。
サーバーサイドではこれを復号化する形でActiveStorageに画像を保存する。
def create
@user = User.new(sign_up_params)
if @user.valid?
if params[:user][:avatar][:data] != "" && params[:user][:avatar][:filename] != "" #画像データ自体は送られてくるので中身が空かどうか判定をする
blob = ActiveStorage::Blob.create_after_upload!(
io: StringIO.new(decode(params[:user][:avatar][:data]) + "\n"), #UserModal.jsxでfilereaderを使って取得した文字列を復号する
filename: params[:user][:avatar][:filename] #filenameはUserModal.jsxで取得
)
@user.avatar.attach(blob) #先に作っておいた画像とuserを紐付ける
end
実際フロントからは画像をbase64形式でencodeした文字列dataと、ファイルの名前filenameをリクエストとして送信するため
実行環境やsystem specについては問題なく通っていた。しかしrequest specではうまく通らなかった。
自分が今まで知っていた画像を添付する方法は
FactoryBot.define do
factory :user do
nickname {Faker::Name.name}
email {Faker::Internet.free_email}
password {'Pass1234'}
password_confirmation {'Pass1234'}
after.build do |user|
user.avatar.attach…
end
end
のようにfactorybotでafter callbackを用いる方法か
user.avatar = fixture_file_upload('spec/fixtures/test_image.jpg', filename: 'test_image.jpg', content_type: 'image/jpg')
のような記述をテストコード上で挿入するかのどちらかだった。
しかしこれらはbase64を用いた形式に合わず、いろいろ不都合だったので修正。
①画像データをencodeした文字列を取得
このサイトで画像データをbase64形式にencodeできる。
ここで取得した文字列をspec/fixturesに test_image.jpg.bin のような形式で保存
②encodeした画像データをテストコード上で読み込む
Base64形式の文字列は
user_params[:avatar][:data] = File.read('spec/fixtures/test_image.jpg.bin') #base64形式でコントローラーで扱うため、encode舌文字列をパラメータにセット
このようにFile.readを使うことで読み込むことができる。
こうすることで無事テストが通った。実行環境の動きにも近いのでより保守性も高まったと言えると思う。やったね
注意点
modelテストやsystemテストではあくまで画像データを直接読み込むので、画像データそのものは残しておく。
参考
RSpecで、Base64エンコードされた画像ファイルをテストする
https://www.malanka.tech/entry/2020/07/18/070000