4

I have a method export_csv in controller.

def export_csv
   if params[:from_date].present? && params[:to_date].present?
   @users = User.where("created_at between ? and ?", params[:from_date], params[:to_date])
        if !@users.blank?
            users_csv = User.to_excel(@users)
            send_data(users_csv, :type => 'text/csv', :filename => 'users.csv')
            flash.now[:success] = "Successfully downloaded the report!"
       else
        flash.now[:notice] = "No records over selected duration!"
       end
   else
    flash.now[:notice] = "Select from and to date.."
   end
end

The file is downloaded but the page is not refreshed or reloaded. Due to this, the flash message stays as it was on the page even after the file is downloaded.

I have gone through few sites and found that send_data automatically renders the view and so other redirect or render can't be used.

Is there a way to reload the page after send_data.?

Maninder Reddy
  • 107
  • 1
  • 11

1 Answers1

2

send_data sets the whole server response, so the browser is just receiving a CSV file, not a web page. This is why your flash message is not displaying. An alternative could be to generate a temporary CSV file (with random name) and provide back a link to it:

def export_csv
   if params[:from_date].present? && params[:to_date].present?
   @users = User.where("created_at between ? and ?", params[:from_date], params[:to_date])
        if !@users.blank?
            #Create temporary CSV report file and get the path to it.
            csv_file_path = create_csv_file(User.to_excel(@users))

            #Change the flash message a bit for requesting the user
            #to click on a link to download the file.                
            flash.now[:success] = "Your report has been successfully generated! Click <a href='#{csv_file_path}'>here</a> to download".html_safe
       else
        flash.now[:notice] = "No records over selected duration!"
       end
   else
    flash.now[:notice] = "Select from and to date.."
   end
end

Of course you should implement the function create_csv_file. To avoid keeping old files in your server, you could implement a new method, say download_report which would read the file, send back to the client with send_data and finally delete it.

EDIT

Pseudocode for the functions mentioned above:

require 'tempfile'

def create_csv_file(data)       
    #Create a temporary file. If you omit the second argument of Tempfile.new
    #then the OS's temp directory will be used.
    tmp = Tempfile.new('report', 'my/temp/dir')
    tmp.write(data)
    tmp.close

    return tmp.path
end


#Method in controller for downloading the file. I omit checks and/or security issues.
def download_report
    #Warning: a mechanism should be implemented to prevent the remote 
    #client from faking the path and download other files. 
    #A possible solution would be providing not only the file path but also a
    #hash with a secret key to validate the path. Function create_csv_file()
    #should, then, return a hash in addition of a path.

    path = params[:report] 
    file = File.open(path, "rb")
    contents = file.read
    file.close

    send_data(contents , :type => 'text/csv', :filename => 'users.csv')       
    file.unlink #Delete file
end
Claudi
  • 5,224
  • 17
  • 30
  • Thanks for the reply. Isn't there any other method to do it? Because, deleting the files using background task will be a hectic thing. – Maninder Reddy Oct 14 '14 at 06:21
  • Well, you could create a new method `download_csv` which would use `send_data` (reading the file contents first) and then delete the file. – Claudi Oct 14 '14 at 06:23
  • Can you help with pseudo code of what to written in create _ csv _ file and download _ report? – Maninder Reddy Oct 14 '14 at 06:56
  • OK, I've written some code for you. Please, if security is an issue, read my warning in the code comments. – Claudi Oct 14 '14 at 07:36