railsでエクセルを編集してクライアントに返そうとしたときのやり方を残しておきます。
作るもの
- クライアントがエクセルファイルをアップロードする
- サーバが受け取ったエクセルに何かしらの変更を加える
- クライアントが変更されたファイルをダウンロードする
といった動きになる機能をRailsで作ります。
出来上がったソースコードはこちら
Excelの編集
RubyXLというgemを使います。
他にもエクセルを開けるgemはあるのですが、
- Roo: 読み込みのみ
- AXSLX: 新規作成のみ
という感じで用途が絞られています。
今回は、編集ができるRubyXLを使います。
単体だと、以下のような感じで使えます。
require 'rubyXL'
# ファイルを読み込んでRubyXL::Workbookにデシリアライズ
workbook = RubyXL::Parser.parse("path/to/Excel/file.xlsx")
# ブック→シート→行→セルという構造になっている
worksheet1 = workbook[0]
row1 = worksheet1[0]
cell1 = row1[0]
# 書き込むときはWorksheetのメソッドが使える
worksheet1.add_cell 0, 0, 'changed'
# 保存
workbook.save
rails new
railsプロジェクトを作ります。
今回は横着してモデルを作らないので、ActiveRecordなどいらないモジュールを生成しないようオプションを付けます。
rails new -MOCJ
ルーティング
excelリソースへのルートを作ります。
加えて、ルートをexcelsにします。
下記とおりルーティングされます
- GET / -> ExcelsController#show
- GET /excels -> ExcelsController#show
- POST /excels -> ExcelsController#create
GET /excelsでファイルを受け取るフォームを表示し、POST /excelsでサーバへのファイルアップロードと編集後ファイルの送信を行います。
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
root to: "excels#show"
resource :excel, only: [:show, :create]
end
コントローラ
showメソッドでは何もせず、ページを表示するだけです。
createにはエクセルファイルが送られて来るので、ファイルの編集とクライアントへの送信を行います。
class ExcelsController < ApplicationController
def show
end
def create
file = params[:file]
# ファイルを開く
workbook = RubyXL::Parser.parse file.path
# 編集する
workbook[0].add_cell 0, 0, 'changed'
# 編集したファイルを送る
send_data workbook.stream.string, type: file.content_type, filename: 'modified.xlsx'
end
end
アップロードされたファイルはUploadedFileオブジェクトとして受け取ることができます。
RubyXL::Parser.parseはFileオブジェクトではなくパスを引数に取るので、アップロードされたファイルのパスを指定してRubyXL::Workbookオブジェクトに変換します。
アップロードされたファイルは、tempfileとして保存されていて、
UploadedFile#pathでこのtempfileのパスを取得できます。
編集したファイルは、send_dataメソッドを使って送信します。
データとして渡すのは、Workbook#streamで取得できるバイナリのストリームを、Stream#stringで文字列にしたものです。
コンテントタイプを指定する必要がありますが、UploadedFileは受信したときのContent-Typeを記録しているので、同じものを指定しておけばいいでしょう。
ビュー
エクセルファイルをアップロードするためのビューを作ります。
<%= form_tag({action: :create}, multipart: true) do %>
<div>
<%= file_field_tag :file %>
</div>
<div>
<%= submit_tag "Send" %>
</div>
<% end %>
ファイル選択と送信ができます。
送信したファイルはExcelsController#createで処理します。
試してみる
このようなEXCELファイルを用意し、
送ってみます。
編集されたファイルのダウンロードが始まります。
開いてみます。期待通り編集されたエクセルをダウンロードできました。
まとめ
- RubyXLでエクセルの編集ができる
- アップロードされたファイルはUploadedFileオブジェクトに記録される
- RubyXLで保存したワークブックは、保存しなくてもデータ送信できる
UploadedFileの存在を知らなかったので、うまくいくかわからなかったのですが、うまくrailsと一緒に使うことができました。