johncorser
johncorser

Reputation: 9822

How to serve a file for download from Rails?

So this is probably a simple question, but I've never done it before.

I have a Rails action that queries a database and creates a csv string from the query result.

I'd like to take the query string, put it into a .csv file, and when the user makes the http request associated with this method, the .csv file will download onto the user's machine.

How can I do this?

UPDATE

The file is sending from rails, but my angular app on the front end (that requested the csv) is not downloading it.

Here is the angular code I'm using to request the file from the rails app

$scope.csvSubmit = function() {
  var csv = $.post('http://ip_addr:3000/api/csv', { 'input': $scope.query_box });
  csv.done(function(result){
     //empty - after the request is sent I want the csv file to download
  })
}

Upvotes: 2

Views: 6884

Answers (3)

arnep
arnep

Reputation: 6241

Use send_data to generate a downloadable file from a string in just one line:

    send_data @your_data, type: 'text/csv', disposition: 'attachment', filename: 'books.csv'

Upvotes: 0

Douglas Camata
Douglas Camata

Reputation: 597

You can use the send_file method, passing the path to the file as the first argument, as see in Rails documentation.

UPDATE

You can use a temporary file to save the CSV, like this:

require 'tempfile'

# automatically creates a file in /tmp
file = Tempfile.new('data.csv', 'w')
file.write('my csv')
file.close

send_file(file.path)

# remove the file from /tmp
file.unlink

UPDATE 2: AngularJS download

There are two ways to accomplish this: you can add a hidden href to download the file in the page and click it, or redirect the user to the Rails URL that sends the file when he clicks in the button. Note that the redirect will use parameters in the url, so it won't work well depending on the structure of query_box.

To add a hidden href to the page with the CSV:

$scope.csvSubmit = function() {
  var csv = $.post('http://ip_addr:3000/api/csv', { 'input': $scope.query_box });
  csv.done(function(result){
    var hiddenElement = document.createElement('a');

    hiddenElement.href = 'data:attachment/csv,' + encodeURI(result);
    hiddenElement.target = '_blank';
    hiddenElement.download = 'filename.csv';
    hiddenElement.click();
  })
}

To use the redirect:

$scope.csvSubmit = function() {
  var url = 'http://ip_addr:3000/api/csv/?' + 'input=' + encodeURI($scope.query_box);
  window.location = url;
}

Upvotes: 5

Dennis F. O'Connor
Dennis F. O'Connor

Reputation: 571

I've had to do this plenty of times before. You need to set the response headers to get the browser to force the download.

I like to use the comma gem for rendering csv. Using the gem all you need to do is add the following lines to your controller action.

 respond_to do |format|
   format.csv do 
     response.headers['Content-Type'] = 'text/csv'
     response.headers['Content-Disposition'] = 'attachment; filename=books.csv'
     render :csv => Book.limited(50)  
   end
 end

Then you just use the csv format and it works.

If you don't want to use comma. Just change the render line to render your csv string:

render :plain => csv_string_variable

Upvotes: 2

Related Questions