Skip to content

Instantly share code, notes, and snippets.

@ippeiukai
Last active November 25, 2015 09:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ippeiukai/9e93df60fa9dd701c65c to your computer and use it in GitHub Desktop.
Save ippeiukai/9e93df60fa9dd701c65c to your computer and use it in GitHub Desktop.
Stream data as a file in Rails as they are written to IO.
class IOStreamingResponseBody
def self.render(options = {}, &block)
instance = new(options)
instance.define_singleton_method :render do |dummy_io|
block.call(dummy_io)
end
instance
end
def initialize(options = {})
@options = options
end
def each(&block)
return to_enum(__method__) unless block_given?
dummy_io = DummyIO.new(@options) do |buffer|
block.call(buffer)
end
render(dummy_io)
self
end
def render(_dummy_io)
raise 'abstract'
end
class DummyIO
def initialize(options = {}, &callback)
@chunk_size = options.fetch(:chunk_size, 100.kilobytes)
@callback = callback
@buffer = ''
end
def <<(str)
@buffer << str
flush if @buffer.bytesize > @chunk_size * 0.9
self
end
def flush
@callback.call(@buffer)
@buffer.clear
end
def close
flush
end
end
end
class StreamingCsvExamplesController < ApplicationController
def example_csv
@filename = "example-#{Time.now.utc.strftime('%Y%m%dT%H%M%SZ')}.csv"
headers['Cache-Control'] = 'no-cache'
headers['X-Accel-Buffering'] = 'no'
headers.delete('Content-Length')
send_file_headers!(type: Mime::CSV, filename: @filename)
self.response_body = IOStreamingResponseBody.render do |io|
Example.output_csv_to_io(io)
end
response.status = :ok
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment