🎉
rails ×vuejsで画像を貼り付けてエクセル出力する機能を実装する
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'
② フォームボタンを作成
/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