Rails Carrierwaveでサムネイルのサイズをモデルごとに変更する
RailsでCarrierwaveで画像のアップロード画面を作っていた。当初は1つのモデルでのみ画像をアップロードしたかったのだが、ユーザーのプロフィール作成画面でも画像アップロード機能を実装したくなった。
そこで思い受かんだ解決方法は4つ。
- 別のアップローダをgenerateしてそれぞれ別のアップローダをマウントする
- 複数のモデルに対応出来るように複数のサイズを作成する
- Carrierwaveのresizeをif式で分岐させて処理を分ける
- 各モデルに任意のサムネイルサイズの定数を定義。Carrierwaveのアップロードのプロセス中にクラス名と定数を参照させて動的にリサイズする大きさを変更する
このうち1と2はきつい。前者は重複したコードが出来ることになるし後者は無駄な画像が毎回作られることになる。 3か4で迷った。ここは趣味もあると思うけどif式を多用するのに抵抗がある。そこで今回は4を採用してアップローダのコードを変更していった。
モデル
# Userモデル class User THUMBNAIL_SIZE = [100, 100] mount_uploader :image, ImageUploader end # Photoモデル class Photo THUMBNAIL_SIZE = [700, 700] mount_uploader :iamge, ImageUploader end
アップローダ
マウントするモデルが1つの場合は以下のようにしていた。
# app/uploader/image_uploader.rb class ImageUploader < CarrierWave::Uploader::Base version :thumb do process resize_to_fit: [700, 700] end end
複数モデルでthumbのサイズを動的に変更するために以下のように変更
# app/uploader/image_uploader.rb class ImageUploader < CarrierWave::Uploader::Base def dynamic_resize_fit size = model.class::THUMBNAIL_SIZE resize_to_fit size[0], size[1] end version :thumb do process dynamic_resize_fit end end
これでモデルごとにサムネイルのサイズを変更出来る。 モデルごとにバージョンを用意してサイズやリサイズのメソッド(resize_to_fitやresize_to_limit)を変更したい時はscaleメソッドを変更する等して対応するらしいが今回はその必要がなかったのでここまで。
Rails5 MySQL(innoDB)でutfmb4を使う
"Index column size too large. The maximum column size is 767 bytes"とエラー
入社2ヵ月の新米エンジニアです。ログとして残す。もっと良い解決の仕方があれば教えて欲しいというのも理由の一つ。 Railsのdb:migrate途中でエラー。 エラー内容は以下。
Mysql2::Error: Index column size too large. The maximum column size is 767 bytes.: CREATE UNIQUE INDEX
とか
Specified key was too long; max key length is 767 bytes
原因
直接の原因はdatabase.ymlのencodingをutf8からutf8mb4に変更したこと。 絵文字への対応が変更の目的。
ActiveRecordのString型カラムはvarchar(255)定義され、デフォルトだと最大で767byteまでしか使えない。utf8は1~3byteで1~4byteになるutf8mb4だと767byteをオーバーしてしまいエラーになる。
環境
- AWSのEC2サーバーの/var/www以下にRails 5.1.2
- anyanvで管理しているrbenvでruby2.4.1をglobal *devise用のモデルのdb:migrate中に今回のエラーが起きた
解決まで
最大767byteまでしか使えないならその制限を広げてしまえば解決出来るはず。MySQLにはinnodb_large_prefixというオプションが追加されているらしいのでこれをONにしたい。ONにしたら3072byteまで広げることが出来る。それをするためにinnodb_file_formatをAntelopeからBarracudaにしないといけない。
※MySQL 5.7.7以降はデフォルトでBarracuda。innodb_large_prefixもデフォルトでON。
自分の環境の設定が知りたい時はMySQLにログインして
show global variables like 'innodb%';
上のコマンドを叩くとinnodb云々の設定が確認出来る。
MySQLの設定を変更してdb:migrate
# /etc/my.cnf innodb_file_format=Barracuda innodb_file_format_max=Barracuda innodb_file_per_table=1 innodb_large_prefix=1
1はON。 設定を変更後
$ sudo service mysqld restart
してindexの所でエラーになったためテーブルが作成されている。drop tableしてdb:migrate。 ネットの情報見たらmy.cnfの書き方は様々だがここまででエラー解決してる人が多い。書き間違いがないか確認したりmysqlのエラーログを見たりするも解決できず。
databaseを削除して作成し直してからdb:migrate
drop database hoges; create database hoges;
してdb:migrateしたらマイグレーションに成功。
innodb_file_per_tableが原因だった。デフォルトでは1つのテーブルスペースファイルに全てのDBテーブルのデータが格納されるため、データ量が肥大化したらこのファイルも巨大化する。このオプションをオンにするとテーブル単位でスペースファイルが作成される。テーブル単位で別ディスクへの移動やバックアップ、リストアが出来て管理がしやすくなるらしい。ただオンとオフがあるってことは良し悪しがあるってことで、テーブル毎にバイナリファイルを作ってくれるためディスクI/Oが減るメリットの変わりにテーブルがあまりに多いとクラッシュした時にリカバリに時間がかかったりする。
参考になったサイト qiita.com