Thin web serverでのsend_fileのタイミング

開発環境に使っているThin web serverにおいてsend_fileの呼び出し直後に、send_fileで送ったファイルを削除するとInternal serverエラーで止まる。

追記:本番環境(Apache + Passenger)の場合、Internal Serverエラーにはならないが、ダウンロードするファイルサイズが0 byteになる。

環境

  • ruby 1.9.3p327
  • rails 3.2.9
  • Thin web server (v1.5.0 codename Knife)

本文

あるページで、ボタンをクリックするとファイルのダウンロードが始まるページを作るとき、以下のようにするとWebRickではエラーとなり、Apache+passengerだとちゃんと動く。downloadsコントローラーのshowでボタンがクリックされると、downloads#csvfileが呼び出されてcsvファイルを一度作成し、それをsend_fileで送るというスクリプト。

app/views/downloads/show

<%= form_tag({:action => "csvfile"}, {:method => :get}) do -%>
  <%= submit_tag(_("ダウンロード開始")) -%>
<% end -%>

app/controllers/downloads_controller.rb

def csfvile
 
sourceAry = Array.new
sourceAry.push(["hello", "world"])
sourceAry.push(["hello", "world, world!"])

filepath = "tmp.csv"

 CSV.open(filepath, "wb") do |writer|  
      sourceAry.each do |line|
        writer << line
      end
    end

  if File.exist?(filepath)
      send_file(filepath,
                {:type => 'text/csv',
                  :filename => "examinees_#{params[:id]}.csv"})
      File.delete(filepath) # これがエラーを引き起こす
    end
  end

downloads#csvfile において、send_fileの終了後に作成したcsvファイルを削除しているのだけど、WebRickの場合エラーになる。Webブラウザ上の表示は「Internal server error」。ログのメッセージは以下のとおり。

!! Unexpected error while processing request: No such file or directory - /home/hogehoge/railsroot/tmp.csv

Happy Elements Labs:Rubyist必携 pry-railsを使いこなせば幸せになれるを参考に、pry-debuggerで逐次実行してみたところ、def csvfile の終了後にエラーが発生していることがわかった。エラーの理由は実際にファイルを送ろうとしたときに、そのファイルが存在していないため。つまり、send_fileを呼び出したときにファイルの送信が行われているのではないということ。

本番環境として使っているApache + passenger ではこのエラーは起こらない。

解決法がわからないけどとりあえずメモ。