🐡

【Rails】POST送信でCSVファイルをダウンロード

2023/01/30に公開

やりたいこと

CSVファイルダウンロード機能をPOST送信で実現したい。

CSVファイルはブラウザから取り出すことができるので、ブラウザのHTMLからアクションを起こし、ブラウザにCSVデータを返却する方法が良さそう。また、セキュリティ的な観点で、CSVファイル生成に必要なデータは、サーバー側(controller側)でDBから引くようにします。

方法

xxx.csvファイルをgetアクセスで読み込んでダウンロード(CSV形式での返却ファイル指定する必要あり。)

↑はググってよく見る方法

②HTMLのformからPOST送信のレスポンスをブラウザで受けとって、ダウンロード

既存のコードに追加する際に、最も綺麗に実装できそうな②をチョイスしました。

全体の大まかな流れ

  • CSVファイルを生成するための条件をformで送信
  • controller側で受け取った条件をもとに、必要なデータを用意する
  • CSVファイルを用意し、書込みを行う
  • 書込みが完了したCSVファイルをブラウザ側に返却する

実装内容

view 側

formヘルパーrailsのものを使うけど、送信方法はjavascriptで実行します。

f.submitではデータがブラウザに返却されない

formには、CSVファイルを生成するための条件をセットします。

<%= form_with url: csv_download_path, method: :post, class: 'js-CsvDownload' do |f| %>
  <%= f.text_field :evaluation_staff %> # 評価するスタッフ
  <%= f.text_field :staff %>            # タスクを実行するスタッフ
  <%= f.date_select :from_date %>       # 着手日
  <%= f.date_select :to_date %>         # 完了日
  <button onclick="submitCsvData()">csvでダウンロード</button>
<% end %>

<script>
    // ajaxレスポンスを取得してダウンロードする
    function submitCsvData() {
        // 送信
        $('.js-CsvDownload').submit();
    }
</script>

controller 側

send_dataドキュメントに書かれている通り、viewerbファイルを作成せずにブラウザ側にデータを返却することができます。

また、ダウンロードしたファイルはExcelで開く予定だったので、encoding: Encoding::SJISによって、Excelでも解釈できるShift-JISでエンコードします。

def generate_csv
    # csvファイルで使うデータを作成
    # 2次元配列を用意 [[1行目], [2行目], ... [n行目]]
    table_data = create_csv_data(csv_download_params)

    # csvファイルのデータをセット
    csv_data = CSV.generate(row_sep: "\n", encoding: Encoding::SJIS) do |csv|
      table_data.each { |data| csv.add_row(data) }
    end

    # ブラウザにデータ送信
    send_data(csv_data,
              disposition: 'attachment',
              filename: 'タスク管理シート.csv',
              type: :csv)
  end

def create_csv_data(csv_download_params)
  # 略
end

CSV.generaterailsで標準に搭載されているメソッドで、与えられたデータをCSVオブジェクトとしてブロックに渡します。その後、ブロック内でCSVオブジェクトに行を追加することができます。

データは、以下のような二次元配列で、CSVデータの行ごとに必要なデータを頑張って用意します。

※データ整形については割愛します。

[
    ['名前', '', '', '', '', ''],
    ['山田太郎', '', '', '', '', '' ],
    ['', '', '', '', '', '' ],
    ['着手日', '完了日', '担当者', '内容', '備考', '確認者' ],
    ['2022/10/01', '2022/12/01', 'rails太郎', 'railsの勉強をする', '特になし', 'rails鈴木' ],
  ]

その後、前述したtable_data.each { |data| csv.add_row(data) }で1行ずつデータを挿入しています。

最終的に以下のようなファイルを生成することができました。

おまけ

最初は以下のようにjavascript内でデータを送信する方法を試みました。

しかし、これではcontrollerにデータを送信できても、最終的なデータの返却先がjavascriptなのでダウンロードがうまく働きません。

$(function () {
      $(".js-click-csvDownload").on("click", function () {
          $.ajax({
              url: <%= @csv_generate_url %>,
              type: 'POST',
              processData: false,
              contentType: false,
              data: <%= @data.to_json %>,
              dataType: 'json'
          })
      })
  });

【Ruby on Rails】【JavaScript】静的ファイルのダウンロードが出来ない。

を参考にしながら今の内容に至りました。

最後に

ここまで見ていただきありがとうございました。

自分の理解が足りていないところが山ほどありますので、

間違っているところがあればご指摘をいただけると嬉しいです m(__)m。

Discussion