Yanonoblog!

こつこつと

RailsとReactでCSVダウンロード機能を実装する例

はじめに

業務関係でCSVの実装について把握する必要があったためダミーコードでまとめていきたいと思います。

実装したことがなかったので大枠が理解できれば良きです。

CSVファイルサンプル

https://sastd.com/csv-file/

こちらのCSVサンプルサイトから拝借したデータを型にしてダウンロード機能の実装例をまとめます。

image

CSVRubyで出力する

/api/v1/hoge/export.csv.ruby - CSV

CSVデータを生成するためのRubyコードを含むビューファイルです。

Ruby CSV 公式: https://docs.ruby-lang.org/ja/latest/class/CSV.html

require "csv"

# CSVヘッダーの設定
def header
  %w[
    全国地方公共団体コード
    郵便番号
    都道府県名_カナ
    市区町村名_カナ
    町域名_カナ
    都道府県名
    市区町村名
    町域名
  ]
end

# CSVの各行に出力する内容を設定
def body(data)
  [
    data.region_code,
    data.postal_code,
    data.prefecture_name_kana,
    data.city_name_kana,
    data.town_name_kana,
    data.prefecture_name,
    data.city_name,
    data.town_name
  ]
end

# BOMの設定(Windowsなどでは、このコードがあることでUTF-8として認識される)
bom = "\uFEFF"

# CSVデータの生成
output = CSV.generate(bom, force_quotes: true) do |csv|
  csv << header  # ヘッダー行の出力

  # データ(一覧)のそれぞれについて、CSVの新しい行を出力
  @data.each do |data|
    csv << body(data)
  end
end

# 文字コードをUTF-8に指定し、無効な文字列を置き換える
output.encode("utf-8", invalid: :replace, undef: :replace)

このファイルでは、CSVのヘッダとボディが定義され、実際のCSVデータが生成されます。

/api/v1/hoge/addresses_controller.rb - コントローラー

先程記載したビューファイルを使ってCSVデータを生成し、クライアントにレスポンスとして返します。

class Api::V1::AddressesController < ApplicationController
  def export
    #...
    # データを取得したり、ビジネスロジックを実行したりするコード
    #...

    # respond_to メソッドを使って、クライアントの要求に応じて異なるフォーマットでレスポンスを返すことが可能です。
    respond_to do |format|
      format.csv { render 'export' }  # ここで export.csv.ruby ビューが呼び出されます
    end
  end
end

Railsはアクション名(例:export)とフォーマット(例:csv)に基づいて適切なビューテンプレートを見つけようとします。

そのアクション名と.csv拡張子を組み合わせたファイル名(例:export.csv.ruby)をビューディレクトリに配置すれば、Railsはそのビューファイルを自動的に見つけてレンダリングします。

/config/routes.rb - ルーティング

CSVデータを返すAPIのルーティングです。

Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resources :addresses do
        collection do
          get :export
        end
      end
    end
  end
end

GETリクエストの /api/v1/addresses/export というURLを AddressesControllerexport アクションにマッピングします。

フロント側 - ダウンロードボタン

フロントの方も用意してみます。

onClickなどでダウンロード実行

「Download CSV」というボタンを作成しており、このボタンがクリックされるとonDownloadという関数が実行されます。

import React from 'react';

interface DownloadButtonProps {
  onDownload: () => void;
}

const DownloadButton: React.FC<DownloadButtonProps> = ({ onDownload }) => {
  return (
    <button onClick={onDownload}>Download CSV</button>
  );
}

export default DownloadButton;

ダウンロード実行内容 - onDownload

サーバからCSVデータを取得し、ブラウザ上でダウンロードを実現するコードです。

エラーハンドリングは適当です。

import axios from 'axios';
import { saveAs } from 'file-saver';

const onDownload = async () => {
  try {
    const response = await axios.get('/api/v1/addresses/export', { responseType: 'blob' });
    const blob = new Blob([response.data], { type: 'text/csv' });
    saveAs(blob, 'addresses.csv');
  } catch (error) {
    console.error('ダウンロードに失敗しました。:', error);
  }
};

axios.getを使ってサーバからCSVデータを取得します。

responseTypeオプションをblobに設定することでレスポンスボディがBlobオブジェクトとして返されます。

今回はfile-saverというライブラリを使用しています。

Blobオブジェクトをfile-saversaveAs関数に渡すことでファイルをダウンロードすることができます。

Blob

Blobは、JavaScriptでバイナリデータを表現するためのオブジェクトで、ファイルのようなイミュータブル(不変)な生のデータを表現します。

ファイル操作、HTTP操作、画像操作など、多岐にわたる用途に使用されます。

ブラウザ上でダウンロードを実装する場合は、大きく分けると二通りです。

  • BlobからオブジェクトURLを生成し、それをa要素のhref属性に設定してダウンロードリンクを作成する
  • file-saverのようなライブラリを使って、Blobからダウンロードファイルを直接生成する

どちらの方法も、JavaScriptをサポートするすべてのブラウザで機能します。

おしまい

本記事の内容は以上になります! 実装したことが今までなかったため、イメージが固まってよかったと思います。


プログラミングスクールのご紹介 (卒業生より)

お世話になったプログラミングスクールであるRUNTEQです♪

https://runteq.jp/r/ohtFwbjW

こちらのリンクを経由すると1万円引きになります。

RUNTEQを通じて開発学習の末、受託開発企業をご紹介いただき、現在も双方とご縁があります。

もし、興味がありましたらお気軽にコメントか、TwitterのDMでお声掛けください。

https://twitter.com/outputky