[rails] CSV非同期ダウンロードでやったこと

#大量データのCSVダウンロード
既存のやり方・問題

```ruby

respond_to do |format|
  format.html
  format.csv do
    ~~~~~~
  end
end
```

- ロードが長い
- タイムアウトエラーになる

この2つを解決しようとして色々検索して試してみましたが、どれもうまく行かず。
さらには、大量データダウンロードの非同期処理はあんまりやってなさそう（？）

ということで、実際に試したことを書いていきます

##解決策
**定時処理でファイル作成-> S3にアップロード**
**リンクでS3のファイルをダウンロード**

というやり方に行き着きました。

####それまでに試したこと
非同期処理
挫折ポイント -> job で `send_date` が使えなかった。(ActionController のメソッド（？）)
             使い方はありそうだったが、うまく行かず断念。


##実装

###定時処理

**CSVデータを作成する処理**

```jobs/file_upload_job.rb
require 'csv'
require 'aws-sdk'

class FileUploadJob < ApplicationJob
  def perform
    csv_data = CSV.generate do |csv|
      csv << ['クラス', '名前'] #CSVのヘッダーの当たる部分
      User.each do |user|
        csv << [user.classroom.name, user.name]
      end
    end
  end
end
```

**+ファイルをアップロードする処理**

```jobs/file_upload_job.rb
require 'csv'
require 'aws-sdk'

class FileUploadJob < ApplicationJob
  def perform
    csv_data = CSV.generate do |csv|
      csv << ['クラス', '名前'] #CSVのヘッダーの当たる部分
      User.each do |user|
        csv << [user.classroom.name, user.name]
      end
    end

    if Rails.env.development?
      # ローカルだとS３の確認ができないので、tmp下に作成するようにして確認する
      File.open(File.join(Rails.root, 'tmp', "#{Date.current.year}_#{Date.current.month}.csv"), 'w'){ |f| f << csv_data }
    else
      s3 = Aws::S3::Resource.new(client: s3_client)
      bucket = s3.bucket(Settings.s3.bucket_name)
      object = bucket.object("uploads/csv/#{Date.current.year}_#{Date.current.month}.csv")
      object.put(body: csv_data, acl: 'public-read')
    end
  end

  private
    def s3_client
      Aws::S3::Client.new(
        access_key_id: ENV['AWS_ACCESS_KEY_ID'],
        secret_access_key: ENV['AWS_SECRET_KEY_ID'],
        region: 'ap-northeast-1'
      )
    end
end
```

こんな感じになります。
`ENV['AWS_ACCESS_KEY_ID']` `ENV['AWS_SECRET_KEY_ID']`
`Settings.s3.bucket_name`
`bucket.object("uploads/csv/#{Date.current.year}_#{Date.current.month}.csv")`

このあたりの定数やファイルのpathは、自分の環境に合わせてください。

`schedule.rb`の設定等は省きますが、設定してください。

###ダウンロードボタン

```app/views/~~/index.html.haml
= link_to 'CSVダウンロード', csv_download_path

```

```app/controllers/~~_controller.rb
def index; end

def csv_download
  year = Date.current.year
  month = Date.current.month

  if Rails.env.development?
    if File.exist?("#{Rails.root}/tmp/#{year}_#{month}.csv")
      data = File.open(File.join(Rails.root, 'tmp', "#{year}_#{month}.csv"), 'r')
      send_data data.read,
                filename: "#{year}_#{month}.csv"
    else
      # ファイルが無いときの処理
    end
  else
    s3 = Aws::S3::Resource.new(client: s3_client)
    object = s3.bucket(Settings.s3.bucket_name).object("uploads/csv/#{year}_#{month}.csv")
    if object.present?
        send_data object.get.body.read,
                  filename: "#{year}_#{month}.csv"
    else
      # ファイルが無いときの処理
    end
  end
end

private
  def s3_client
    Aws::S3::Client.new(
      access_key_id: ENV['AWS_ACCESS_KEY_ID'],
      secret_access_key: ENV['AWS_SECRET_KEY_ID'],
      region: 'ap-northeast-1'
    )
  end
```

こんな感じで行けると思います。
予期するファイル名があればダウンロードするといった感じです。

```ruby

rescue Aws::S3::Errors::NoSuchKey => e
  flash[:notice] = "ファイルがありません。"
  redirect_to root_path
```

のようなコードも足しておくと良さそうです。


##まとめ
- 大量CSVダウンロードでタイムアウトエラーから始まり、それを回避するためのコードを使用してもダウンロードニ時間がかかるところから今回の作業をしました。
- 記事であまりCSV非同期ダウンロードがなかった。（自分のやりたいことに似ているもの）
- 非同期ダウンロードとは違う形になっているかもしれないですが、結果的には、リンクを踏んで非同期でダウンロードするよりもダウンロードのスピードは上がっていると思うので、良いかなと思います。
- CSVの勉強とS3へのアップロード・ダウンロードを勉強できてよき。
- ただ、ローカルだと`else`文後の処理が確認できなかったのが、難点。
- ぜひご参考までに見ていただけたら〜〜〜