2021年10月13日水曜日

画像ファイルをDBに格納して表示する【Rails備忘録】

画像のアップロードとDBへの保存

Rails環境が用意できており,db:createも済んでいるものとする.

まず,contentモデルを作る.stored_file_nameはファイル名(string型)で,アップロードしたファイルそのものはバイナリデータ(binary型)としてstored_fileに格納する.

$ bin/rails g model content stored_file_name:string stored_file:binary

続いて,コントローラを作る.

$ bin/rails g controller contents_controller index create show

ルーティング(config/routes.rb)を次のように修正しよう.

Rails.application.routes.draw do

  resources :contents, only: [ :index, :create, :show ]

end

コントローラは次のように記述する.

app/controllers/contents_controller.rb を以下のように記述する.とりあえず,indexとshowは空のままとする.

class ContentsController < ApplicationController

  def index

  end


  def create

    stored_file = content_params[:stored_file]

    content = {}

    if stored_file != nil

      content[:stored_file] = stored_file.read

      content[:stored_file_name] = stored_file.original_filename

      @content = Content.create(content)

    end

    redirect_to contents_path

  end


  def show

  end


  private

  def content_params

    params.require(:content).permit(:stored_file, :stored_file_name)

  end

end

ビューを作る.アップロードするファイルを選ぶフォームを用意する.

app/views/contents/index.html.erb を次のように編集する.

<h1>Contents Upload Test</h1>

<%= form_with model: Content.new do |f| %>

  <div class='field'>

    <%= f.file_field :stored_file %>

  </div>

  <div class='actions'>

    <%= f.submit 'Upload' %>

  </div>

<% end %>


画像のDBからの読み出しと表示

次に,格納した画像を表示することを考えよう.まずは,登録した画像のファイル名の一覧を表示させるところから手を付ける.

コントローラは次のように修正する.

app/controllers/contents_controller.rb のindexメソッドを用意する.とりあえずは,登録した画像全てを表示させるようにする.

  def index

    @contents = Content.all.order('created_at asc')

  end

次はビューの変更.app/views/contents/index.html.erb を次のように変更する.登録した画像のファイル名一覧が表示されるようにする.

<h1>Contents Upload Test</h1>

<%= form_with model: Content.new do |f| %>

  <div class='field'>

    <%= f.file_field :stored_file %>

  </div>

  <div class='actions'>

    <%= f.submit 'Upload' %>

  </div>

<% end %>


<h2>Registered Files</h2>

<% if @contents&.length > 0 %>

  <ul>

  <% @contents.each {|c| %>

    <li><%= c.stored_file_name %></li>

  <% } %>

  </ul>

<% end %>

さて,次はいよいよ show 画面で画像を表示させる手順である.まずは,ビューを書き換えてshow 画面へのリンクを用意する.ビューの後半を次のように書き換える.

<h2>Registered Files</h2>

<% if @contents&.length > 0 %>

  <ul>

  <% @contents.each {|c| %>

    <li><%= link_to c.stored_file_name, content_path(c) %></li>

  <% } %>

  </ul>

<% end %>

これで,このリンクを叩くと show 画面に遷移するようにできた.

show のコントローラを作成する.といっても次のような単純なものでOK.

  def show

    @content = Content.find(params[:id])

  end

show のビューはシンプルなものとする.

app/views/contents/show.html.erb は次の一行だけのファイルである.

<%= image_tag send_img_content_path(@content), width: '400px' %>

ここで send_img_content_path という謎のメソッドが現れた.これを有効にするために,ルーティングの設定を次のように修正する.

Rails.application.routes.draw do

  resources :contents, only: [ :index, :create, :show ] do

    member do

      get 'send_img'

    end

  end

end

そして,それを受けて画像データを送るメソッド send_img をコントローラに追記する.

  def send_img

    tmp = Content.find(params[:id])

    if (tmp.stored_file.size > 0)

      send_data(tmp.stored_file, :disposition => 'inline')

    end

  end

先のリンクがクリックされたときの動作は次のようなものとなる.

  1. 指定されたcontent_idをパラメータとしてcontent#showメソッドが起動,ビューであるshow.html.erbがレンダリングされる
  2. そのなかに書かれているimage_tagで生成された<img>のsrc="..."に指定されているのはsend_imgのパスになる.したがって,再度,クライアントからサーバへの通信が行われ,content#send_imgが起動する.
  3. content#send_imgによってDBのなかの画像データがファイル化され,送られる.

めでたし.めでたし.




0 件のコメント:

コメントを投稿