How to output CSV created by Rails to S3

I implemented the function to upload CSV created by Rails to S3 the other day. I had more consideration than I expected, so I will summarize the contents.

environment

Rails 5.2.3 Ruby 2.6.3

Premise

I do not expect to check CSV in Excel

Introduction of gem

You need to install the gem to access AWS from Rails. Add the following to your Gemfile:

Gemfile


gem 'aws-sdk', '~> 3'

Creating a common class

I wanted to use the CSV output function created this time as common logic, so create a file called ʻoutput_csv.rb in / app / lib`. Define the class in output_csv.rb as follows. All methods of this class should be class methods.

app/lib/output_csv.rb


require 'csv'
require 'nkf'

class OutputCsv
  class << self
    #Add methods here
  end
end

Output method implementation on S3

Create a method to output to S3. The CSV character string received as a parameter is output to S3. The CSV character string creation part will be described later.

app/lib/output_csv.rb


#CSV output method-- ①
def save_csv(data)
  #The file name is'Random string_Time stamp.csv'To
  file_name = "#{SecureRandom.urlsafe_base64(9)}_#{Time.current.strftime('%Y%m%d%H%M%S')}.csv"
  #Output to a folder named csv directly under the bucket
  file_full_path = "csv/#{file_name}"

  #CSV output locally in the development environment and to S3 in the production environment-- ②
  if Rails.env.development?
    File.open(Rails.root.join('tmp', file_name), 'w') do |f|
      #Convert character code using NKF-- ③
      f.puts(NKF.nkf('-x -w', data))
    end
  else
    #Instantiate an S3 client-- ④
    s3 = Aws::S3::Client.new
    #Output CSV to S3-- ⑤
    s3.put_object(bucket: ENV['AWS_S3_BUCKET'],
                  key: file_full_path,
                  body: NKF.nkf('-x -w', data),
                  content_type: 'text/csv')
  end
end

(1) Definition of CSV output method. Receives the CSV character string to be output as a parameter (data).

def save_csv(data)

(2) This time, I am trying to output locally in the development environment and to S3 in the production environment. If you want to output to S3 even in the development environment, this branch is unnecessary.

NKF is used to convert the character string.

f.puts(NKF.nkf('-x -w', data))

The first arguments -x and -w specify the following conversions. -x: Output half-width katakana without converting to full-width katakana -w: Output with utf-8

(4) Creating an instance of S3 client. Use this to access S3.

s3 = Aws::S3::Client.new

Note that we haven't passed any parameters to the constructor here. The reason is that the role of the EC2 instance on which this Rails application runs has permission to write to S3, so there is no need to consider the permission.

If you want to access S3 with IAM user privileges instead of roles, you need to specify the access key and secret key as follows.

s3 = Aws::S3::Client.new(
  access_key_id: 'your_access_key_id',
  secret_access_key: 'your_secret_access_key'
)

⑤ Create a CSV file on S3.

s3.put_object(bucket: ENV['AWS_S3_BUCKET'],
              key: file_full_path,
              body: NKF.nkf('-x -w', data),
              content_type: 'text/csv')

I'm using a method called put_object. The contents of each parameter are as follows.

-Bucket: The name of the bucket to output. Here, the description is based on the assumption that the environment variable ʻAWS_S3_BUCKET` is set. -Key: Specify the output directory name + file name. -Body: Outputs the character string converted by NKF explained above. -Content_type: The file format is specified. If you do not explicitly specify that it is a csv file here, it will not be recognized as a CSV file on S3.

Creating a CSV string

Create a CSV character string, pass the created character string to the save_csv method created above, and create a method to output a CSV file.

In the method to be created, the header item and data item are received as parameters, converted to CSV character string, and output to a file.

app/lib/output_csv.rb


#CSV string creation method-- ①
def execute(headers, values)
  output = CSV.generate do |csv|
    #Header output-- ②
    csv << headers
    #Output of data items-- ③
    values.each do |value|
      csv << value
    end
  end

  #CSV file output-- ④
  save_csv(output)
end

(1) Definition of CSV character string creation method. Receives an array of headers and data items as parameters. For example, if you want to create the following CSV

id,name,age
1,hoge,20
2,fuga,31
3,foo,43

Create an array like the one below and pass it to the parameters.

#header
headers = ['id', 'name','age']
#data item
values = []
values.push([1, 'hoge', 20])
values.push([2, 'fuga', 31])
values.push([3, 'foo', 43])

OutputCsv.execute(headers, values)

(2) The value of the header received by the parameter is set in CSV.

(3) The data item received by the parameter is set in CSV. Since it is an array, it is taken out and set line by line.

(4) Pass the created CSV character string to the save_csv method created above and output it to S3.

All chords

The whole code is below. The save_csv method is not supposed to be called from the outside, so it is made private.

app/lib/output_csv.rb


require 'csv'
require 'nkf'

class OutputCsv
  class << self
    def execute(headers, values)
      output = CSV.generate do |csv|
        #Header output
        csv << headers
        #Output of data items
        values.each do |value|
          csv << value
        end
      end

      #CSV file output
      save_csv(output)
    end

    private

    def save_csv(data)
      #The file name is'Random string_Time stamp.csv'To
      file_name = "#{SecureRandom.urlsafe_base64(9)}_#{Time.current.strftime('%Y%m%d%H%M%S')}.csv"
      #Output to a folder named csv directly under the bucket
      file_full_path = "csv/#{file_name}"

      #CSV output locally in the development environment and to S3 in the production environment
      if Rails.env.development?
        File.open(Rails.root.join('tmp', file_name), 'w') do |f|
          #Convert character code using NKF
          f.puts(NKF.nkf('-x -w', data))
        end
      else
        #Instantiate an S3 client
        s3 = Aws::S3::Client.new
        #Output CSV to S3
        s3.put_object(bucket: ENV['AWS_S3_BUCKET'],
                      key: file_full_path,
                      body: NKF.nkf('-x -w', data),
                      content_type: 'text/csv')
      end
    end
  end
end

how to use

CSV file output

Output the file to S3.

headers = ['id', 'name','age']
values = []
values.push([1, 'AIUEO', 20])
values.push([2, 'Kakikukeko', 31])
values.push([3, 'SA Shi Su Se So', 43])

OutputCsv.execute(headers, values)

Check the file output to S3

The CSV file will be output to the specified bucket and directory as shown below.

スクリーンショット 2020-09-18 16.26.03.png

Check the contents of the file

If you download the file output to S3 and check the contents, it will be as follows. スクリーンショット 2020-09-18 16.30.33.png

Recommended Posts

How to output CSV created by Rails to S3
[Rails] How to decide the destination by "rails routes"
How to write Rails
How to uninstall Rails
How to separate .scss by controller in Rails
How to remove the underline displayed by Rails link_to
How to download images from AWS S3 (rails, carrierwave)
[rails] How to post images
[Rails] How to use enum
[Rails] How to install devise
How to read rails routes
How to use rails join
How to terminate rails server
How to write Rails validation
How to write Rails seed
[Rails] How to use validation
[Rails] How to disable turbolinks
[Rails] How to use authenticate_user!
[Rails] How to use "kaminari"
[Rails] How to implement scraping
[Rails] How to make seed
How to write Rails routing
[Rails] How to install simple_calendar
Output Rails Routes as csv
[Rails] How to install reCAPTCHA
[Rails] How to use Scope
How to load UI created by Xib file into View
[Rails] Differences between redirect_to and render methods and how to output render methods
[Rails] How to delete images uploaded by carrierwave (using devise)
[Rails] How to display the list of posts by category
[Rails] How to use gem "devise"
How to deploy jQuery on Rails
[Rails] How to use devise (Note)
[Rails] How to use flash messages
[rails] How to display db information
[Rails] How to write in Japanese
[Rails] How to prevent screen transition
How to use Ruby on Rails
How to deploy Bootstrap on Rails
[Rails] How to speed up docker-compose
[Ruby on Rails] CSV output function
[Rails] How to add new pages
Rails on Tiles (how to write)
[Rails] How to write exception handling?
[Rails] How to install ImageMagick (RMajick)
[Rails] How to install Font Awesome
[Rails] How to use Active Storage
How to introduce jQuery in Rails 6
[Rails] How to implement star rating
How to return Rails API mode to Rails
How to get along with Rails
[Introduction to Rails] How to use render
How to install Swiper in Rails
[Rails / Routing] How to refer to the controller in the directory you created
[RSpec on Rails] How to write test code for beginners by beginners
[Rails] How to upload images to AWS S3 using Carrierwave and fog-aws
[Rails] How to upload images to AWS S3 using refile and refile-s3
How to prevent duplicate processing by addEventListener
How to implement search functionality in Rails
How to change app name in rails
How to use custom helpers in rails