5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[Rails]CarrierWaveの学習

Last updated at Posted at 2022-01-12

gem「CarrierWave」とは

Railsアプリから簡単にファイルをアップロードでき、またアップロードしたファイルを扱うための便利なメソッドが使えるgem。

利用イメージ

gemをインストールした上で、以下の仕込みを行う。

  • アップロード対象ファイル(以降fileと呼ぶ)のuploaderを作成
  • fileを扱うmodelにuploaderを連携、アップロード情報を扱うカラム(以降upload_infoと呼ぶ)を設定
  • fileを扱うテーブルにカラムupload_infoを設定(string型)

すると、gemによって拡張されたインスタンスメソッドupload_infoを使い、
model.upload_info = file とすると、
uploaderによってfileがアップロードされ、
テーブルのupload_infoカラムにアップロード先のファイルパスが書き込まれる。

また、その他のgemで拡張されたインスタンスメソッドで色々便利にfileを扱えるようになる。

導入

前提: Rails6系の場合

gemのインストール

# Gemfileへ以下を記述
gem 'carrierwave', '~> 2.0'

利用手順

①前提

あらかじめ、以下を決定しておく必要がある。

  • アップロード情報を扱うテーブルとカラム名
  • アップロード先

ここでは、犬の成長記録を管理するテーブルdog_recordsがあり、
その中に犬の画像ファイルのアップロード情報を扱うカラムdog_imageがあるものとする。
また、アップロード先は、Railsアプリケーションが動作するマシン上の/dog_images/であるものとする。

以降の実装するものと、その動作イメージを以下に示す。

carrierwave-ページ4.drawio (1).png

②アップローダーの生成

# アップローダーファイルの生成
rails generate uploader dog_image
=> app/uploders/dog_image_uploader.rb が生成される

③アップローダーの設定

dog_image_uploader.rb
class DogImageUploader < CarrierWave::Uploader::Base
  # アップロード先を記述(ローカルのパスでもS3でも可らしい)
  def store_dir
    # 相対パスで記載すると、"public/uploads/dog_images" にアップロードされる。
    'uploads/dog_images'
  end
end

④アップロード情報のカラムとアップローダーとの連携

dog_record.rb
class DogRecord < ApplicationRecord
  # 「dog_image」カラムに「DogImageUploader」を連携
  mount_uploader :dog_image, DogImageUploader
end

⑤テーブルにアップロード情報のカラムを追加

dog_recordsテーブルにdog_imageカラムをstring型で追加する。

⑥ファイルのアップロードをするAPIのアクションメソッドの設定

前提

APIでのファイルの送信方法は、主に以下2つがあるらしい。

(A) multipart/form-data を利用して送信
(B) Base64化してJSONに文字列としてセットして送信

いろいろな情報があり、実装コスト、ファイルサイズのコスト、処理スピードなどいろいろな意見があったが、
イマイチ反対のことをいっている部分があり、これらについてよくわからなかった。

・・・が、(B)の方法を取らないと、そもそもJSONでデータを送れないので、
JSONでやり取りする体系をとっているAPI群をサービスとして提供する場合、ファイル送信を扱うAPIだけ特異点になってしまう
画像ファイル以外にもデータを送りたいときにJSONでないとやりづらそう
(B)でやる場合のサーバー側の実装を見てみたらすごく単純だったが、(A)の方はいまいちよくわからなかった

という3点から(B)を採用することにした。

本題

※クライアント側からPOSTメソッドでリクエストボディに「Base64で文字列化した画像データ」を挿入してリクエスト
→サーバー側で受け取って、Base64画像データをデコードして画像ファイルを取り出し、アップローダーに紐付いているカラムにセットする

という流れになる。

また、ここではdog_records_controller.rbuploadアクションメソッドで、ファイルをアップロードするものを例とする。
また、リクエストボディで送信するパラメータは以下のようなものとする。

{
  "image": {
    "file_name": "<保存するファイル名>"
    "base64": "<Base64エンコードした画像ファイルの文字列>"
  }
}
dog_records_controller.rb
def upload
  # Base64文字列化された画像をデコードしてデータ(バイナリ)を取り出す
  dog_image_binary = Base64.decode64(params[:image][base64])
  # ファイル名を取り出す
  dog_image_file_name = params[:image][file_name]

  # ファイル名を名前と拡張子に分離
  dog_image_file_basename = File.basename(dog_image_file_name, ".*")
  dog_image_file_extname = File.extname(dog_image_file_name)

  
  # <ファイル名>で空っぽのテンポラリファイルを作成
  # テンポラリファイルは「Dir.tmpdir」に保存される。
  # 自動的に削除はされないので、不要になった時点で「unlink」メソッドで削除する)
  dog_image_temp_file = Tempfile.new([dog_image_file_basename,dog_image_file_extname])

  # テンポラリファイルをバイナリモードにする
  dog_image_temp_file.binmode
  
  # バイナリ状態の画像データを書き込む
  dog_image_temp_file.write(dog_image_binary)

  # 対象テーブルのレコードを扱うモデルのインスタンスを生成
  dog_record = DogRecord.new()

  # carrierwaveのuploaderが紐づくカラムに画像データファイル(File型)の値をセットする
  dog_record.dog_image = dog_image_temp_file

  # 対象テーブルにレコードをsaveする
  # ・・・すると、ファイルが「store_dir」へアップロードされ、「dog_records」テーブルの「dog_image」カラムへアップロード情報が書き込まれる
  dog_record.save!
end

⑦アップロード情報を扱う

DBに保存したアップロード情報は、以下のように操作できる(例として)

# DogRecordのインスタンスを取得
dog_record = DogRecord.first

# アップロードファイルのURLを取得
dog_record.dog_image.url

# アップロードファイルのパスを取得
dog_record.dog_image.current_path

# アップロードファイル名を取得
dog_record.dog_image_identifier

# アップロードファイル情報があるか確認
dog_record.dog_image.file.nil?

# アップロードファイル情報を削除
dog_record.remove_dog_image!
dog_record.save

動作確認

# クライアント側
## 1. 適当な画像ファイルをローカルに用意してBase64でエンコード
base64 <画像ファイルのパス>
=>
表示されたBase64文字列をコピペしておく

## 2. POSTメソッドでbase64で文字列化した画像データを挿入してリクエスト(postmanなりcurlなり)
## ※以下のJSONキーバリューをリクエストボディへセット
{
  "image": {
    "file_name": "<保存するファイル名>"
    "base64": "<Base64エンコードした画像ファイルの文字列>"
  }
}

# サーバー側
## 3. public/<store_dirのパス>に画像が保存されていることを確認する

参考

APIでのファイルアップロード方法

ファイルの扱い

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?