Rails4で実装するCSVのインポート / エクスポート
2014.11.28
WEBサイト制作

はじめに
前回の記事で Rails と Bootstrap を利用して管理画面を実装しました。
今回はそちらで作ったアプリケーションをベースにして、ユーザー一覧のCSVインポート / エクスポートの機能を追加していきます。
・CSVファイルの仕様
WindowsのExcelで利用できるCSVにしたいと思います。
- 文字コード: Shift-JIS
- 改行コード: CR+LF
CSVエクスポート(出力)
DBに入っているユーザーデータをCSVでダウンロードする処理を実装します。 まずはCSVモジュールとNKFモジュールをrequireします。
・/config/application.rbrequire 'csv'
require 'nkf'
次に index.csv.ruby というビューファイルを作成して、CSVの出力処理を実装します。
・/app/views/admin/users/index.csv.ruby(新規)csv_str = CSV.generate do |csv|
csv << @admin_users.column_names
@admin_users.all.each do |user|
csv << user.attributes.values_at(*@admin_users.column_names)
end
end
NKF::nkf('--sjis -Lw', csv_str)
CSV#generateメソッドのパラメータで出力エンコードを指定できるらしいのですが、なぜか効かなかったのでNKFで SJIS, CR+LF に変換して出力しています。
これで “/admin/users.csv” にアクセスするとCSVファイルがダウンロードされます。
ちなみにテンプレートファイルに “.ruby” が使えるようになったのはRails4からのようです。
* 参考: Rails4になってcsvが出力しやすくなった
ユーザー一覧にダウンロードボタンをつければ完成です。
・/app/views/admin/users/index.html.erb<%= link_to 'CSVダウンロード', params.merge({:format => :csv}), class: 'btn btn-success' %>
CSVインポート(入力)
続いてCSVのインポートです。
まずルート追加。
“/admin/users/import” にPOSTでインポートできるようにします。
namespace :admin do
resources :users do
collection do
post :import
end
end
end
Userのコントローラにimportメソッドを追加します。
・/app/controllers/admin/users_controller.rbdef import
if params[:csv_file].blank?
redirect_to(admin_users_url, alert: 'インポートするCSVファイルを選択してください')
else
num = Admin::User.import(params[:csv_file])
redirect_to(admin_users_url, notice: "#{num.to_s}件のユーザー情報を追加 / 更新しました")
end
end
次にコントローラで呼び出しているUser#importメソッドを追加します。
ここがCSVインポートの実際の処理ですね。
# CSVファイルの内容をDBに登録する
def self.import(file)
imported_num = 0
# 文字コード変換のためにKernel#openとCSV#newを併用。
# 参考: http://qiita.com/labocho/items/8559576b71642b79df67
open(file.path, 'r:cp932:utf-8', undef: :replace) do |f|
csv = CSV.new(f, :headers => :first_row)
csv.each do |row|
next if row.header_row?
# CSVの行情報をHASHに変換
table = Hash[[row.headers, row.fields].transpose]
# 登録済みユーザー情報取得。
# 登録されてなければ作成
user = find_by(:id => table["id"])
if user.nil?
user = new
end
# ユーザー情報更新
user.attributes = table.to_hash.slice(
*table.to_hash.except(:id, :created_at, :updated_at).keys)
# バリデーションOKの場合は保存
if user.valid?
user.save!
imported_num += 1
end
end
end
# 更新件数を返却
imported_num
end
最後にテンプレートにCSVファイル登録用のフォームを追加して完了です。
・/app/views/admin/users/index.html.erb<div class="panel panel-default">
<div class="panel-body">
<%= form_tag(import_admin_users_path, :method => :post, multipart: true) do %>
<div class="form-group">
<%= file_field_tag :csv_file %>
</div>
<%= submit_tag "CSVインポート", :class => "btn btn-primary" %>
<% end %>
</div>
</div>
「山崎」さんはCP932の拡張文字の確認用に入れてみました。
(Shift-JISで読み込むと「崎」が化ける)
まとめ
RubyとRailsの機能のおかげでコード量も少なく実装できたと思います。
ただ、今回はエラー発生時のユーザーへの通知や、Shift-JIS以外のファイルの対応を行なっていないのでそのあたりの処理は工夫のしどころですね。
参考サイト
・Exporting CSV and Excel
・Rails4になってcsvが出力しやすくなった
・CSV を文字コード変換しつつロード