Madogiwa Blog

主に技術系の学習メモに使っていきます。

RubyonRails:Gemを使わずに画像アップロード処理を実装する

こんばんは、まどぎわですφ(..)
皆さんは画像アップロード処理はどうやって実装していますか?
RailsだとCarrierWaveを使った実装がメジャーだと思いますが、今回はあえてGemを使わずに画像アップロード処理を実装する方法をメモしておきます。
railsのバージョンは、5.1.2を使用しています。

画像アップロード

画像をアップロードする方法には、大きく分けて2つの方法があります。

  • 任意のディレクトリに配置する
  • DBにバイナリとして保存する

それぞれ方法をみていきましょう!

任意のディレクトリに配置する

今回はアップロードのみを行う画面を用意して任意のディレクトリに配置するケースを想定してみました(・∀・)

f:id:madogiwa0124:20180407084733p:plain

View側のポイントは、form_tagの引数にmultipart: trueを設定することと、file_field_tagを使うことです。

view

<%= form_tag(upload_process_path, method: 'post', multipart: true) do %>
  <label>
    ファイルを指定:
    <%= file_field_tag :upfile %>
  </label>
  <%= submit_tag 'アップロード' %>
<% end %>

controller

  def upload_process
    # パラメータからファイルを取得
    file = params[:upfile]
    # ファイル名を取得
    name = file.original_filename
    # 出力先のパスを設定※今回は、public/docs配下に配置
    output_path = Rails.root.join('public/docs', name)
    # ファイルを出力
    File.open(output_path, 'wb'){ |f| f.write(file.read) }
  end

DBにバイナリとして保存する

今回はUserの編集画面でプロフィール画像を設定し、それをDBに保存するようなケースを想定してみました(・∀・)

f:id:madogiwa0124:20180407085925p:plain

ポイントは、string型のctypeとbinary型のphotoプロパティを持たせることと、それぞれに下記値を設定してあげることです。

  • ctype = アップロードファイルのcontent_type
  • photo = アップロードファイルの中身

view(抜粋)

<%= form_with(model: user, local: true, multipart: true) do |form| %>

  <div class="field">
    <%= form.label :photo %>
    <%= form.file_field :photo, id: :author_photo %>
  </div>

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>

controller(抜粋)

  def create
    @user = Author.new(author_params)
    if @user.save
      redirect_to @user, notice: 'User was successfully created.'
    else
      format.html { render :new }
    end
  end

    def user_params
      if params[:user][:photo]
        # ctypeをパラメータに設定されたアップロードファイルから設定
        params[:user][:ctype] = params[:user][:photo].content_type
        # photoにアップロードファイルの中身を設定
        params[:user][:photo] = params[:user][:photo].read
      end
      params.require(:user).permit(:name, :birth, :address, :ctype, :photo)
    end

おまけ:DBに保存した画像を表示する方法

まずは画像を表示するアクションを定義します。
send_dataを使うことで、ファイルそのものを返却することが出来ます(・∀・)

controller(抜粋)

  def show_image
    send_data @user.photo, :type => @user.ctype, :disposition => 'inline'
  end

そしてそのメソッドへのroutingを定義します。
これによりusers/id/show_imageでユーザーのプロフィール画像にアクセス出来るようになりました!

routes

  resources :users do
    member do
      get 'show_image'
    end
  end
$ rake rotes
show_image_user GET /users/:id/show_image(.:format)  users#show_image

最後にviewのimageのurlに先程作成したroutingを設定すれば、画像を表示することが出来ます(・∀・)

<p>
  <strong>Photo:</strong>
  <%= image_tag(show_image_user_path(@user), :width => 300) %>
</p>

↓こんな感じ

f:id:madogiwa0124:20180407093512p:plain

おわりに

今回は、gemを使わずにファイルアップロード機能を実現する方法を整理してみました。
試してはないですが、herokuにリリースする際に画像アップロードする機能は、careerwaveを使って普通に実装すると24時間で消えてしまいますが、今回のようにDBに保存するようにすれば消えずに残すことが出来る気がしました。φ(..)

簡単なユーザーのアイコン等の容量が小さく、簡単なアップロード処理であればgemを使わずに実装してみても良いかもしれませんね(・∀・)

参考資料

cre8cre8.com

qiita.com