はじめに
Railsで画面上のボタンをクリックしてファイルをダウンロードさせる機能を実装していた際に気がついたことがあるので備忘録として記録しておきます。
問題点
send_file
, send_data
を実行するアクションの呼び出しにlink_to
を利用すると、ブラウザ上でリンクをクリックした際にリクエストが2重に送信される。
解決方法
アクションの呼び出しにlink_to
ではなくbutton_to
を利用する。
詳細
ルーティング、コントローラ
http://xxx.xxx.xxx.xxx/download
にリクエストが来るとアプリ内で生成したファイルを渡す実装にしてました。
routes.rb
get 'download' => 'get#download'
get_controller.rb
class GetController < ApplicationController
def download
send_data 'hogehoge',
:filename => 'hoge.txt',
:type => 'text/plain',
:disposition => 'attachment'
end
end
修正前
ビュー
downloadアクションの呼び出しにlink_to
を利用して、CSS(bootstrap)で見た目をボタンにしてました。
xxx.html.erb
<%= link_to 'download', download_path, class: 'btn btn-info' %>
生成されたHTML
<a class="btn btn-info" href="/download">download</a>
ボタンクリック時の動作
ブラウザ上でボタンをクリックするとファイルがダウンロードされるため、正常に動作してるように見えますが、pumaのログを確認するとリクエストが2重に出力されてました。
Started GET "/download" for 127.0.0.1 at 2017-10-28 21:54:27 +0900
Processing by GetController#download as HTML
Rendering text template
Rendered text template (0.0ms)
Sent data hoge.txt (0.5ms)
Completed 200 OK in 1ms (Views: 0.3ms | ActiveRecord: 0.0ms)
Started GET "/download" for 127.0.0.1 at 2017-10-28 21:54:27 +0900
Processing by GetController#download as HTML
Rendering text template
Rendered text template (0.0ms)
Sent data hoge.txt (0.5ms)
Completed 200 OK in 1ms (Views: 0.4ms | ActiveRecord: 0.0ms)
修正後
ビュー
アクションの呼び出しにbutton_to
を利用するように修正しました。
xxx.html.erb
<%= button_to 'download', download_path, method: :get, class: 'btn btn-info' %>
生成されたHTML
<form class="button_to" method="get" action="/download">
<input class="btn btn-info" type="submit" value="download" />
</form>
ボタンクリック時の動作
pumaのログを確認するとリクエストが1回だけ出力されるようになりました。
Started GET "/download" for 127.0.0.1 at 2017-10-28 22:02:23 +0900
Processing by GetController#download as HTML
Rendering text template
Rendered text template (0.0ms)
Sent data hoge.txt (0.7ms)
Completed 200 OK in 2ms (Views: 0.5ms | ActiveRecord: 0.0ms)