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

# やりたいこと
### 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ファイルを生成するための条件をセットします。
```eruby
<%= 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`は[ドキュメント](https://apidock.com/rails/ActionController/Streaming/send_data)に書かれている通り、`view`の`erbファイル`を作成せずにブラウザ側にデータを返却することができます。

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

```ruby
def generate_csv
    # csvファイルで使うデータを作成
    # ２次元配列を用意 [[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.generate`は`rails`で標準に搭載されているメソッドで、与えられたデータを`CSVオブジェクト`としてブロックに渡します。その後、ブロック内で`CSVオブジェクト`に行を追加することができます。

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

```
  [
    ['名前', '', '', '', '', ''],
    ['山田太郎', '', '', '', '', '' ],
    ['', '', '', '', '', '' ],
    ['着手日', '完了日', '担当者', '内容', '備考', '確認者' ],
    ['2022/10/01', '2022/12/01', 'rails太郎', 'railsの勉強をする', '特になし', 'rails鈴木' ],
  ]
```
その後、前述した`table_data.each { |data| csv.add_row(data) }`で1行ずつデータを挿入しています。

最終的に以下のようなファイルを生成することができました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2508607/46dc0d75-4bbb-c88a-7400-6d1265136039.png)


# おまけ
最初は以下のように`javascript`内でデータを送信する方法を試みました。
しかし、これでは`controller`にデータを送信できても、最終的なデータの返却先が`javascript`なのでダウンロードがうまく働きません。

```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】静的ファイルのダウンロードが出来ない。](https://qiita.com/tanakanata7190/items/80e23d9a35b853796da5)
を参考にしながら今の内容に至りました。

# 最後に
ここまで見ていただきありがとうございました。
自分の理解が足りていないところが山ほどありますので、
間違っているところがあればご指摘をいただけると嬉しいです m(__)m。