🎉

rails ×vuejsで画像を貼り付けてエクセル出力する機能を実装する

2023/04/04に公開

rails × vuejsで画像を貼り付けてエクセル出力する機能を作る

背景

S3に保存してる画像を選択して、出力ボタンを押すことで画像が添付されたエクセルファイルをダウンロードする機能が欲しいと言われ実装してみた。

環境

  • windows10
  • backend
    • ruby 2.7.2
    • ruby on rails 6.1.3
  • front
    • vuejs 2.6.11
    • vuetify2
  • db
    • mysql 5.6

DB構造

・users
idと名前、ユーザーごとの画像のurlがあったとします。* S3とかに保存しているパス

id name image_url
1 aaa https://aaa
2 bbb https://bbb
3 ccc https://ccc

手順

① xlsxファイルを用いるため、gemを追加する

Gemfile
# output xlsx
gem 'caxlsx'
gem 'caxlsx_rails'

https://github.com/caxlsx/caxlsx

② フォームボタンを作成

/front/views/reports/userImagesReports.vue
フォームで、選択したユーザーのidを配列に入れておく。
出力ボタンを押すと、エクセルファイルをダウンロードする。

userImagesReports.vue
<template>
// 割愛
<v-btn @cilck.stop="outputs">出力</v-btn>
</template>
<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
  name: 'userImagesReports',
  methods: {
    outputs (){
      // userIds [1,2,3] array
      const encodeURI = encodeURI(this.userIds.join(',')
      const link = document.createElement('a')
      link.href = `api/report/reports?user_ids=${userIds}`
      link.setAttribute('download', 'user_images.xlsx'
      document.body.appendChild(link)
      link.click()
    }
  }
})
</script>

③リクエストを受け取って、出力するファイルを生成する

/controller/reports_controller.rb
class ReportController < ApplicationController
  def reports
    xlsx_data = Report.user_images_reports(params)
    send_data(
      xlsx_data.to_stream.read,
      type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
      filename: "user_images_report.xlsx"
    )
  end
end
/models/report.rb
class Report < ApplicationRecord
  def self.user_images_reports(params)
    require 'open-uri'
    
    # 配列に戻す
    user_ids = params[:user_ids].split(',')
    now = Time.now().strftime('%Y%m%d%H%M%S')
    
    # xlsx
    Axlsx::Package.new do |package|
      package.use_shared_strings = true
      package.use_autowidth = false
      
      # ワークブックの作成
      wb = package.workbook
      # スタイル定義 *別ファイルに記述
      service = AxlsxSupportService.new
      styles  = service.load_styles(wb)
      
      # 1シート目 シート名を決める
      wb.add_worksheet(name: 'users') do |worksheet|
        # 1行目(A1) # スタイルはいらない場合はnil
	worksheet.add_row(
	  ["ユーザー情報"],
	  style: [styles[:default],
	  height: 15.75
	 )
	# 2行目(A2) 空欄
	worksheet.add_row []
	
	# ここから画像の添付に入る
	
	# まずは、tmp配下に画像フォルダが生成されていなければ生成
	FileUtils.mkdir_p("#{Rails.root.join('tmp', 'user_images')}/") unless FileTest.exist?("#{Rails.root.join('tmp', 'user_images')}/")
	# 開始位置
	start_position = 0
	# x,y (2,2) は、 C3の右上角に画像の左端がくる
	x = 2
	start_position_y = 2
	
	user_ids.each_with_index do |user_id, i|
	  user = find(user_id)
	  # 3行目(A3)
	  worksheet.add_row(
	    [user.name],
	    style: [styles[:default]]
	  )
	  
	  # 画像の保存先
	  file_path = "#{Rails.root.join('tmp', 'user_images')}/user-image-#{now}-#{user_id.to_s}.jpg"
	  y = start_position_y
	  # これまでの高さ  + 画像分の高さ * 数
	  if i != 0
	    y = 2 + (12 * i)
	  end
	  
	  begin
	    # 読み込んで、指定パスに書き込み
	    open(file_path, "wb") do |f|
	      f.write open(user.image_url).read
	    end
	    # 書き込んだパスを指定
	    worksheet.add_image(image_src: file_path, hyperlink: "#") do |image|
	      image.width = 300
	      image.height = 200
	      image.start_at x,y
	    end
	  rescue => e
	    logger.error(e)
	  end
	  
	  # 画像取得の失敗に関わらず、画像分の高さを調節
	  11.times {worksheet.add_row Array.new(1, '')}
	end # users end 
      end # worksheet end
    end # axlsx end
  end
end

注意

件数が多くなると、タイムアウトになる。
その場合、例外処理がないためにエラー画面となってしまう。
front側で、例外を受け取れるようにしたい。

最後に

front側でもっと良い書き方があればコメントにください。。

Discussion