アップロードしたCSVを元データにテーブルを作る。

こんな要件があるのかわからないけどうちのシステムにはあったので。
アップロードしたCSVを前に使ったCSVパースライブラリを通して配列に突っ込み、
それをとりあえずテーブル化したrhtmlに渡してみた。
アップロードモデルの作り方次第で少しやり方が変わるかもしれないけど、
あくまでCSVの内容にモデルは触れないようにしている。ほんとはURLを突っ込む
予定なんだけどまだしてません。

元ファイルがパースエラー出した時にどうraiseさせるとか、そういったフォローも今
入れてません。このあたりはおいおい必要になるはず。

とりあえずuploadモデルを作成。create_uploads.rbにはURLと作成日付だけ用意
しておいた。今回は結局DB使ってないけど。

  def self.up
    create_table :uploads do |t|
      t.column "filepath", :string, :null => false
      t.column "created_at", :datetime
    end
  end

アップロード先のディレクトリをpublic配下に用意。publicの外でもいけるかもしれないけど
その場合ディレクトリトラバーサルとかそのあたり注意なのかな。特に直接見られてどうと
いうことはないと思われるやり取りなのでpublic/filesを用意してそこに持っていくことにする。
uploadモデルのsaveメソッドをオーバーライドしてファイルの保管。
upload.rb

  def self.save(file)
    File.open("public/files/#{file.original_filename}", "w"){ |f| f.write(file.read) }
  end
  
  def self.filepath(file)
    return "public/files/#{file.original_filename}"
  end

今回DBをやりとりせずにやるだけやっちゃう系でいってるのでfilepathをベタで返すメソッドを用意。saveの方はWindowsだとモードを"wb"にしないと壊れるらしい?

upload_controller.rbの作成。

require "NKF"
require "CSV"

class UploadController < ApplicationController

  def index
    render :action => 'upload_form'
  end  

  def upload
    @filename = params[:upload][:file].original_filename
    if @filename != "" then
      if Upload.save(params[:upload][:file])
        @uploads = parse_csv(Upload.filepath(params[:upload][:file]))
      else
        render :action => 'upload_form'
      end
    end
  end

  def upload_form
  end

  private
  def parse_csv(upfile)
    a = Array.new
    open("#{upfile}").each {|line|
      b = Array.new
      CSV.parse(line).each{|row|
        row.each{|item|
          b.push(NKF::nkf("-w", item.to_s))
        }
      }
      a.push(b)
    }
    return a
  end
end

CSV.parseで1行のarrayが作られるのでその要素を切り出してひとつひとつ変換。
このあたりもっときれいに書けそうな気がするけどどう手をつけるといいのかな。
余裕が出来たら調べないと…

裏方は大体こんな感じ。あとはフォームのrhtmlと結果表示のrhtml。
upload_form.rhtml

<html>
<head>
<title>ファイルアップロード</title>
</head>
<body>
<h1>ファイルアップロードサンプル</h1>
<% form_tag ({:action => 'upload'}, :multipart =>true) do %>
    ファイルを選んでください:<%= file_field  "upload", 'file' %>
<%= submit_tag "upload" %>
<% end %>
</body>
</html>

upload.rhtml

<h1>Upload結果</h1>
<table border="1">
<% for item in @uploads %>
    <tr>
        <% item.each{|column|%>
        <td><%=h column %></td>
        <%}%>
    </tr>
<% end %>
</table>

とりあえず全件表示って感じで。CSVでやりとりとか結構な分量になってるものが多い
のでこれそのまま使っていいかはなんとも。
一応こんな風にしてやってみたけどちゃんとした説明が出来るくらい理解しているかって
いうところもあるし見直していかないといけないなあ。もっとこうしろ的な指摘があればぜひ。