joppot

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

プログラミング

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

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

Pocket

SDI1473A

概要

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

form_forは基本的にデータベースのカラムに依存したフォームなので、pdfをアップロードするには一手間掛ける必要があります。

簡単なやり方として、form_tagがありますが、こちらは少し古いのでform_forを使って画像アップロードのフォームを作成しましょう。


前提

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

rails4のtwitterbootstrap gem でscaffoldを作る方法は下のURLにあるので、もし、同じように進行したい場合は下の記事を参考にしてください。
https://joppot.s3.amazonaws.com/2014/05/27/1455

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


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

Myfile scaffoldを作成して、pdfをアップロードしてみましょう。
プロジェクトの作成は省きます。気になる方は上のURLの記事を見てください。

Myfileモデルのscaffoldを作成する

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

rails g scaffold Myfile title:string filename:string comment:string

createmyfilescaffold

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

rake db:migrate

myfilemigration


twitterbootstrap gemのスタイルを反映させる

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

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

rails g bootstrap:layout application

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

bootstraplayout

bootstrapのテーマをMyfileコントローラーのviewに反映させるには下のコマンドを打ちます。
今回はMyfileモデルを作ったので、Myfilesを指定します。

rails g bootstrap:themed Myfiles

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

overwriteviews

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


pdfがアップロードできる様に編集する

サーバを起動します。

bundle exec rails s

下のURLに移動します。

http://localhost:3000/myfiles/

myfileviewindex

移動したら、「New」ボタンを押しましょう。下の様な画面が現れると思います。

newmyfile

このフォームで編集すべき場所は2つあります。
1つは、pdfファイルをアップロードするフィールドを作る事
2つ目はfilenameは自分で登録するのではなく、pdfファイルの名前を入れるので、このフィールドは必要ない事です。

Myfile viewを編集する

ではviewから編集していきましょう。
/app/views/myfiles/_form.html.erbを開きます。

ファイルの下の部分を

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

下の様に変更します。

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

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

なぜ、ハッシュを変えたか説明します。
form_forはデータベースに紐づいたフォームです。ハッシュ:filenameはデータベースのfilenameカラムに紐づいています。仮に、ハッシュを:filenameのままファイルをアップロードすると、恐らく、pdfファイルデータをそのままデータベースに収めようとします。もちろん、エラーがでてしまいます。
そこで、データベースのカラムに存在しない仮のハッシュ:fileを指定します。

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

Myfile Modelを編集する

先ほどviewで作成した:fileはデータベースのカラムにありません。なので、例えば、ストロングパラムスを使いたい場合は何らかの設定をする必要があります。

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

railsは便利で、データベースには収めないけど、値を受ける様に設定できます。

/app/models/myfile.rbを開きます。Myfileクラスの中に下の内容を書き込みます。

attr_accessor :file

こんな感じですね。

editMyfilemodel

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


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

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

require 'Kconv'

importkconvinmyfile

myfile_params関数を編集する

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

def myfile_params
  params.require(:myfile).permit(:title, :filename, :comment)
end

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

def myfile_params
  params.require(:myfile).permit(:title, :file, :comment)
end

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

createアクションを編集する

下の部分を

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

下の様にします。

def create
  @myfile = Myfile.new(myfile_params)
    
  file = myfile_params[:file]
  file_name = file.original_filename
  @myfile.filename= file.original_filename
  result = uploadpdf(file,file_name)
    
  respond_to do |format|
    if result=="success" && @myfile.save
      format.html { redirect_to @myfile, notice: 'Myfile was successfully created.' }
      format.json { render action: 'show', status: :created, location: @myfile }
    else
      deletepdf(file_name)
      format.html { render action: 'new' }
      format.json { render json: @myfile.errors, status: :unprocessable_entity }
    end
  end
end

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

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

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

こんな感じですね。

privateuploadfunc

このuploadpdf関数はpublicフォルダの中にpdfをアップロードしています。

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

def deleteipdf(file_name)
  File.unlink "public/"+file_name.toutf8
end

以上でpdfのアップロードができるようになりました。
早速やってみましょう。

pdfをアップロードする。

新しくファイルをアップロードする画面に行きます。必要項目を入力して、pdfファイルを選びます。

uploadpdffile

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

successpdffileupload

uploadpdffiletopublic

うまくいきましたね。

まとめ

pdfファイルの削除や更新はまた別にコーディングする必要があります。ただ、これの応用なので頑張ってください。

スポンサードリンク

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

-プログラミング
-

執筆者:


comment

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

関連記事

macにmiddlemanの環境構築をする

概要 みなさんこんにちはcandleです。今回はmacにmiddlemanの環境構築をしてみたいと思います。 middlemanといえばwebサイト作成支援ツールみたいなものです。 githubやhe …

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

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

phpのsnappyライブラリをmacで使用して、webサイトのサムネイルを取得する

概要 みなさんこんにちはcandleです。今回はsnappyを使用して、ウェブサイトのサムネイルを自動的に取得したいとおもいます。 snappyはそれ自体がhtmlを画像にするスクリプトではなく、wk …

railsのfluent-loggerとdevise gemを使ってユーザーの行動をfluentdサーバに収集する

概要 fluentdと言えば、ビックデータで扱うようなデータを集め出力するサーバですが、これとrailsのfluent-logger gemとdevise gemを組み合わせてユーザーの行動ログをとっ …

wordpressのbogoでサイトで使用する言語を「en-US」から「en」に変更する方法

English 日本語 概要 みなさんこんにちはcandleです。 今回はbogoプラグインのカスタマイズです。bogoはwordpressのサイトを簡単に他言語に対応することができる素晴らしいプラグ …

  • English
  • 日本語

プロフィール


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

スポンサードリンク

アーカイブ