joppot

コピペで絶対動く。説明を妥協しない

プログラミング

railsのform_forとstrong parametersを使用して画像をアップロードする

投稿日:2014年5月28日 更新日:

Pocket

gold-texture

概要

みなさんこんにちはcandleです。今回はrailsのform_forを使って、railsプロジェクトのpublicディレクトリに画像をアップロードしてみましょう。

form_forは基本的にデータベースのカラムに依存したhtmlのフォームなので、画像をアップロードするなど、データベースに保存しないものを送る場合は一手間掛ける必要があります。
簡単なやり方として、form_tagがありますが、こちらは少し古いのでform_forを使って画像アップロードのフォームを作成しましょう。


前提

今回もtwitterbootstrap gemとscaffoldを使って説明します。別段、bootstrap gemが入っている必要がありませんが、見栄えが多少異なります。

rails4のtwitterbootstrap gem でscaffoldを作る方法は下のURLにあるので、もし、同じように進行したい場合は下の記事を参考にしてください。

ruby on rails4でtwitterbootstrap3のgemを使用してscaffoldを作成する

プロジェクトがすでに作成されている

SPONSORED LINK


scaffoldでデータベースを用意する

Users モデルを作成して、そこに紐づく画像をアップロードしてみましょう。
プロジェクトの作成は省きます。気になる方は上のURLの記事を見てください。

Usersモデルのscaffoldを作成する

早速、scaffoldを作ります。下のコマンドを実行しましょう。

rails g scaffold User name:string password:string age:integer image:string

scaffolduser

マイグレーションファイルが作成されますので、それをデータベースに反映させます。

rake db:migrate

railsrakeusermigrate


twitterbootstrap3のスタイルを反映させる

もしも、twitterbootstrap gemがインストールされているなら、次のコマンドを入力して、テーマを反映できます。

あまり良い選択肢ではないかもしれませんが、bootstrapのレイアウトをapp/views/layouts/application.html.erbに上書きします。

rails g bootstrap:layout application

もちろん、application.html.erbを上書きしてよいのか聞いてきますので「y」と入力して、許可しましょう。
bootstraplayout

bootstrapのテーマをUserコントローラーのviewに反映させます。
Userモデルを作ったので、Usersを指定します。

rails g bootstrap:themed Users

これを実行すると、テーマが自動的にuserで使うビューに書き込まれます。
実行すると分かる様に、すでにファイルがscaffolldで作成されているので、コンフリクト(衝突)が起きてしまいます。上書きをそれぞれ許可しましょう。質問されたら「y」と答えれば良いです。

これでスキャホールドが完成しました。


サーバの起動とデザインの確認

サーバを起動します。

bundle exec rails s

下のURLに移動します。

http://localhost:3000/users/

userindex

「New」ボタンを押して新しいユーザーを加えます。

newを押すと分かるように、imageの箇所がtext fieldになっています。画像をアップロードしたいので、file_fieldに変更します。

newuserform


user viewを変更する

/app/views/users/_form.html.erbを編集します。

ファイルの下の部分を

<div class="form-group">
  <%= f.label :image, :class => 'control-label' %>
  <div class="controls">
    <%= f.text_field :image, :class => 'form-control' %>
  </div>
</div>

下の様に変更します。

<div class="form-group">
  <%= f.label :imageobject, :class => 'control-label' %>
  <div class="controls">
    <%= f.file_field :imageobject %>
  </div>
</div>

上の変更点は2つです。1つは「f.text_field」を「f.file_field」に変更した事。
もう1つはハッシュを「:image」から「:imageobject」に変更したことです。

form_forはデータベースに紐づいたフォームです。ハッシュ:imageはデータベースのimageカラムに紐づいています。仮に、ハッシュを:imageのままファイルをアップロードすると、その画像のファイルデータをそのままデータベースに収めようとします。もちろん、エラーがでてしまいます。
そこで、データベースのカラムに存在しない仮のハッシュ:imageobjectを指定します。

これでuserのviewの変更はおしまいです。

UserModelを編集する

先ほどviewで作成した:imageobjectはデータベースのカラムにありません。

なので、例えば、ストロングパラムスを使いたい場合は何らかの設定をmodelにする必要があります。

やりたいことはこういうことです。「データベースにはカラムはないけれど、form_forで投げてきた画像ファイルをストロングパラムスで受け入れたい」

railsは便利にも、データベースには収めないけど、値を受ける様に設定できます。
/app/models/user.rbを開きます。Userクラスの中に下の内容を書き込みます。

attr_accessor :imageobject

こんな感じですね。

usermodelattr-1

モデルの編集は以上です。

Userコントローラーでフォームの値を受け取る

今度は/app/controllers/users_controller.rbを開きます。
users_controller.rbの一番上に、下のコードを宣言しておいてください。後々使います。

require 'Kconv'

kconv

user_params関数を編集する

ストロングパラムスで受け入れる要素を変更します。
フォームから直接:imageを受けなくなったので、
下の内容の:imageを

def user_params
  params.require(:user).permit(:name, :password, :age, :image)
end

下のように:imageobjectに変更します。

def user_params
  params.require(:user).permit(:name, :password, :age, :imageobject)
end

先ほど、modelで:imageobjectを足し加えたのでエラーはでません。

createアクションを編集する

下の部分を

def create
   @user = User.new(user_params)
   respond_to do |format|
     if @user.save
       format.html { redirect_to @user, notice: 'User was successfully created.' }
       format.json { render action: 'show', status: :created, location: @user }
     else
       format.html { render action: 'new' }
       format.json { render json: @user.errors, status: :unprocessable_entity }
     end
   end
 end

下の様にします。

def create
    @user = User.new(user_params)

    image = user_params[:imageobject]
    image_name = image.original_filename
    @user.image= image.original_filename
    result = uploadimg(image,image_name)

    respond_to do |format|
      if result=="success" && @user.save
        format.html { redirect_to @user, notice: 'User was successfully created.' }
        format.json { render action: 'show', status: :created, location: @user }
      else
        deleteimg(image_name)
        format.html { render action: 'new' }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

最初のimage変数への代入は先ほど設定したストロングパラムスから:imageobjectだけを取り出して代入しています。

@user.imageへの代入は画像のファイル名を収めています。
uploadimg関数は自前で作りました。下の関数をuserコントローラーのprivate関数の中に入れてください。

def uploadimg(img_object,image_name)
  ext = image_name[image_name.rindex('.') + 1, 4].downcase
  perms = ['.jpg', '.jpeg', '.gif', '.png']
  if !perms.include?(File.extname(image_name).downcase)
    result = 'アップロードできるのは画像ファイルのみです。'
  elsif img_object.size > 4.megabyte
    result = 'ファイルサイズは4MBまでです。'
  else
    File.open("public/#{image_name.toutf8}", 'wb') { |f| f.write(img_object.read) }
    result = "success"
  end
  return result
end

uploadimg関数の説明はしませんが、けっこう有名な記述をコピペで動かしています。
このuploadimg関数はpublicフォルダの中に画像をアップロードしています。

もう1つprivateの中にdeleteimg関数を書きます。これは、データベースへデータの保存が失敗した時、アップロードしたファイルの削除をする関数です。

def deleteimg(image_name)
  File.unlink "public/"+image_name.toutf8
end

以上で画像のアップロードができるようになりました。

早速やってみましょう。

画像をアップロードする

新しいユーザーを作成する画面に行きます。必要項目を入力して、画像を選びます。

createnew

「Create User」を押すと、画像がpublicフォルダにアップロードされて、データベースのimageカラムには画像のファイル名が収まります。

publi3dcimage

successuploadimage

うまくいきましたね。

まとめ

ファイルの削除や更新はまた別にコーディングする必要があります。ただ、これの応用なので頑張ってください。この記事、少し長くなってしまったので、もしかするとわかりにくい所があるかもしれません、もしあれば、コメント欄なりに質問してくれば返信、編集なりして、対応させてもらいます。

スポンサードリンク

「為になったなぁ」と思ったら、シェアお願いします。

-プログラミング
-,

執筆者:


comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

関連記事

ruby on railsのckeditor gemの使い方をscaffoldを用いて解説する

概要 みなさんこんにちはcandleです。今回はruby on railsのckeditorという素晴らしいgemを使って見たいと思います。 ckeditorとはweb版の高機能なwordとかテキスト …

Stinger3のURLまたはタブの横にあるロゴを変更する

概要 みなさんこんにちはcnadleです。Stinger3のカスタマイズをしましょう。どんなwebサイトでもURLの周辺にロゴがありますよね。今回はそれを変更します。 条件 WordPressを利用し …

emacsでplantUMLをplantuml-modeを使って作成する

Autumn leaves on wood table 概要 みなさんこんにちはcandleです。今回はplantUMLをサポートするemacsのplantuml-modeを導入して使ってみましょう。 …

railsのsimple-captcha gemを使用してform_forを使ったフォームを画像認証する

概要 みなさんこんにちはcandleです。公に開かれたフォームはスパムや総当り攻撃を受けやすいです。 そこで、画像認証を導入して、悪意のあるフォームを拒否してみましょう。 simple-captcha …

railsのscaffoldでremote formで送信後jsonを取得する

概要 みなさんこんにちはcandleです。 今回はrailsのformをremoteを使ってajaxした時に、 サーバに送った値をjavascript側で受け取る方法を紹介します。 ちなみに、私は下の …

  • English
  • 日本語

プロフィール


ベンチャー企業のCTOをやってます。大学時代にプログラミングを始め、javaから入門し、C++へて、PHPへと進み、会社ではRailsを使用。自動化が大好きなプログラマー

スポンサードリンク

アーカイブ