Created
January 25, 2019 07:35
-
-
Save gagoit/9a77f1aa00cdd20f239de74a0ae6dfae to your computer and use it in GitHub Desktop.
Faster CSV downloads using Enumerator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Faster CSV downloads using Enumerator | |
https://medium.com/reflektive-engineering/faster-csv-downloads-using-enumerator-7e9b94b870d3 | |
We do not have to download the full movie first to start watching it thanks to the concept of ‘media streaming’. In simple terms, we can download chunks of the movie in sequence while we’re watching the downloaded chunks. | |
Similarly, we can stream the CSV to the customer, rather than make them wait for the complete CSV to be generated. | |
This is possible because Rack, the middleware that Rails uses, requires the response object to support the #each method. Rack uses #each to send the response back in chunks. | |
In the current scenario, the CSV file that’s generated (Refer to Step#2 in the process) is a String. String does not support #each, and Rack is forced to send all of it in one shot, after it’s ready. | |
So, Rack requires the response object to support #each and ruby’s Enumerator fit this requirement. | |
def generate_csv(column_names, records) | |
Enumerator.new do |csv| | |
csv << column_names.to_csv # add headers to the CSV | |
records.each do |record| | |
csv << record.attributes.values_at(*column_names).to_csv | |
end | |
end | |
end | |
# In the controller#action | |
respond_to do |format| | |
format.csv { render_streaming_csv(generate_csv(column_names, records)) } | |
end | |
def render_streaming_csv(csv_enumerator) | |
# Delete this header so that Rack knows to stream the content. | |
headers.delete("Content-Length") | |
# Do not cache results from this action. | |
headers["Cache-Control"] = "no-cache" | |
# Let the browser know that this file is a CSV. | |
headers['Content-Type'] = 'text/csv' | |
# Do not buffer the result when using proxy servers. | |
headers['X-Accel-Buffering'] = 'no' | |
# Set the filename | |
headers['Content-Disposition'] = "attachment; filename=\"report.csv\"" | |
# Set the response body as the enumerator | |
self.response_body = csv_enumerator | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment