t__nabe_log

雑多 作業、学習ログ多め。

Rails Carrierwaveでサムネイルのサイズをモデルごとに変更する

RailsでCarrierwaveで画像のアップロード画面を作っていた。当初は1つのモデルでのみ画像をアップロードしたかったのだが、ユーザーのプロフィール作成画面でも画像アップロード機能を実装したくなった。

そこで思い受かんだ解決方法は4つ。

  1. 別のアップローダをgenerateしてそれぞれ別のアップローダをマウントする
  2. 複数のモデルに対応出来るように複数のサイズを作成する
  3. Carrierwaveのresizeをif式で分岐させて処理を分ける
  4. 各モデルに任意のサムネイルサイズの定数を定義。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以降はデフォルトでBarracudainnodb_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

blog.kamipo.net

blog.kamipo.net

qiita.com