Rails でQRコードを作成

Rails での QR コード生成について調べてみると、RQRCodeという gem を利用して実装していることが多いようです。
こちらを触ってみます。

目次

実行環境

  • Rails 5.2.3

参考

導入

Gemfile にgem 'rqrcode'を追記して、bundle installを実行。

実装

今回は Site モデルを作成して、URL を登録。
show 画面で QR コードが作成されるようにしてみます。

とりあえず scaffold で用意

以下コマンドで site を用意。

1
2
rails generate scaffold site url:string
rails db:migrate

http://localhost:3000/sites にアクセスすると以下の画面が出るはずです。


一旦ここまでできたら OK です。
適当に URL を登録しておきます。

コントローラの編集

QR コードを取扱いできるように sites コントローラを編集します。
as_svg のパラメータは、RQRCodeのままです。

app/controllers/sites_controller.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class SitesController < ApplicationController
require 'rqrcode'
before_action :set_site, only: [:show, :edit, :update, :destroy]

#省略

def show
qrcode = RQRCode::QRCode.new(@site.url)
@qr = qrcode.as_svg(
offset: 0,
color: '000',
shape_rendering: 'crispEdges',
module_size: 6,
standalone: true
)

end

#省略

private
# Use callbacks to share common setup or constraints between actions.
def set_site
@site = Site.find(params[:id])
end
end

ビューの編集(誤り)

app/views/sites/show.html.erb を以下のように編集します。
ただしここでは一度失敗します。

app/views/sites/show.html.erb
1
2
3
4
5
6
7
8
9
10
11
12
<p id="notice"><%= notice %></p>

<p>
<strong>Url:</strong>
<%= @site.url %>
</p>
<div>
<%= @qr %>
</div>

<%= link_to 'Edit', edit_site_path(@site) %> |
<%= link_to 'Back', sites_path %>

確認

http://localhost:3000/sites/1 にアクセスすると以下のような表示になります。
SVG がエスケープして表示されてしまいました。

修正が必要です。

ビューの編集(訂正)

改めて、app/views/sites/show.html.erb を以下のように編集します。
@qr.html_safeとするのがポイントでした。

app/views/sites/show.html.erb
1
2
3
4
5
6
7
8
9
10
11
12
<p id="notice"><%= notice %></p>

<p>
<strong>Url:</strong>
<%= @site.url %>
</p>
<div>
<%= @qr.html_safe %>
</div>

<%= link_to 'Edit', edit_site_path(@site) %> |
<%= link_to 'Back', sites_path %>

確認

改めて http://localhost:3000/sites/1 にアクセスすると以下のような表示になります。

QR コードの表示ができました。
登録した URL の QR コードが正しいのかは、スマートフォンで確認しています。

実装 2 ファイルを作成してリンクで表示する。

ここまでは表示都度 QR コードを作成していたので、今度はいったん画像を保存してリンクで表示することを試みてみます。

画像ファイルの url のカラムをテーブルに拡張

以下のコマンドで、site モデルに保存した画像の URL のカラムを追加します。

1
2
rails generate migration add_columns_to_sites imgurl:string
rails db:migrate

コントローラの編集

既に URL が投稿されているので、show アクションが呼び出されたときに imgurl が nil だったら QR コードのファイルを作成をするようにします。
最初からやるなら、create アクションに設定すればいい気がします。

app/controllers/sites_controller.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class SitesController < ApplicationController
require 'digest'
require 'rqrcode'
before_action :set_site, only: [:show, :edit, :update, :destroy]

OUTPUTPATH = "[publicへのフルパス]"

# 省略

# GET /sites/1
# GET /sites/1.json
def show
if @site.imgurl.nil?
create_qr
end
end

# 省略

private
# Use callbacks to share common setup or constraints between actions.
def set_site
@site = Site.find(params[:id])
end

# 省略

def create_qr
qrcode = RQRCode::QRCode.new(@site.url)
@qr = qrcode.as_svg(
offset: 0,
color: '000',
shape_rendering: 'crispEdges',
module_size: 6,
standalone: true
)
# ファイル名をURLをMD5でハッシュ化したものに設定
filename=Digest::MD5.hexdigest(@site.url)

IO.write("#{OUTPUTPATH}\\#{filename}.svg",@qr)
@site.imgurl="#{filename}.svg"
end
end

ビューの編集

image_tag を使用して画像を表示します。

app/views/sites/show.html.erb
1
2
3
4
5
6
7
8
9
10
11
12
<p id="notice"><%= notice %></p>

<p>
<strong>Url:</strong>
<%= @site.url %>
</p>
<div>
<%= image_tag "/#{@site.imgurl}" %>
</div>

<%= link_to 'Edit', edit_site_path(@site) %> |
<%= link_to 'Back', sites_path %>

確認

また http://localhost:3000/sites/1 にアクセスすると以下のような表示になります。

表示はこれまでと大差ありません。
しかし、public の中を見るとファイルが作成されているのが見て取れます。

html もリンクに変わっています。

ファイルを作成し、リンクで QR コード画像を表示することがでいました。

実装 3 とりあえずありそうなエラーを排除したバージョン

作成を進めていたら、以下のエラーがあったので対応しました。

  • ファイルを消すと、表示できない
    =>ファイルの存在確認を行い、再作成
  • URL にスペースが入ると使えない
    =>.strip で空白を削除
  • URL のバリデーションがない。
    =>設定します。参考:BitArts Blog - Rails で URL のバリデーション

コントローラの編集

app/controllers/sites_controller.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
class SitesController < ApplicationController
require 'digest'
require 'rqrcode'
before_action :set_site, only: [:show, :edit, :update, :destroy]

OUTPUTPATH = "[publicへのフルパス]"

#省略

# GET /sites/1
# GET /sites/1.json
def show
if @site.imgurl.nil? && exist_file?
create_qr
end
end

#省略

# POST /sites
# POST /sites.json
def create
@site = Site.new(site_params)
@site.url = @site.url.strip

respond_to do |format|
if @site.save
format.html { redirect_to @site, notice: 'Site was successfully created.' }
format.json { render :show, status: :created, location: @site }
else
format.html { render :new }
format.json { render json: @site.errors, status: :unprocessable_entity }
end
end
end

# PATCH/PUT /sites/1
# PATCH/PUT /sites/1.json
def update
respond_to do |format|
if @site.update(site_params)
@site.url = @site.url.strip
format.html { redirect_to @site, notice: 'Site was successfully updated.' }
format.json { render :show, status: :ok, location: @site }
else
format.html { render :edit }
format.json { render json: @site.errors, status: :unprocessable_entity }
end
end
end

#省略

private
# Use callbacks to share common setup or constraints between actions.
def set_site
@site = Site.find(params[:id])
end

#省略

def create_qr
qrcode = RQRCode::QRCode.new(@site.url)
@qr = qrcode.as_svg(
offset: 0,
color: '000',
shape_rendering: 'crispEdges',
module_size: 6,
standalone: true
)
# ファイル名をURLをMD5でハッシュ化したものに設定
filename=Digest::MD5.hexdigest(@site.url)

IO.write("#{OUTPUTPATH}\\#{filename}.svg",@qr)
@site.imgurl="#{filename}.svg"
end

def exist_file?
File.exist?("#{OUTPUTPATH}\\#{@site.imgurl}")
end
end

ビューの編集

app/views/sites/show.html.erb に変更はありません。

app/views/sites/show.html.erb
1
2
3
4
5
6
7
8
9
10
11
12
<p id="notice"><%= notice %></p>

<p>
<strong>Url:</strong>
<%= @site.url %>
</p>
<div>
<%= image_tag "/#{@site.imgurl}" %>
</div>

<%= link_to 'Edit', edit_site_path(@site) %> |
<%= link_to 'Back', sites_path %>

モデルの編集

バリデーションを追加しました。

app/models/site.rb
1
2
3
class Site < ApplicationRecord
validates :url,presence: true,format: /\A#{URI::regexp(%w(http https))}\z/
end

確認

バリデーションに引っかかれば、以下のように警告出せるようになりました。

空白削除を行ったので、QR コードを読んだときにただのテキストとして処理されなくなりましたが、
バリデーションと重複している要素とも感じます。
ただ、「空白が含まれています」って探すのも面倒な入力ミスなので、いいかなぐらいの感じです。

ファイルが削除されても、再度作成されるようにもなりました。


Rails での QR コード作成について確認できました。
これらを使って何か作りたいところです。

ではでは。