Skip to content

Instantly share code, notes, and snippets.

@julik
Created August 6, 2010 17:45
Show Gist options
  • Save julik/511676 to your computer and use it in GitHub Desktop.
Save julik/511676 to your computer and use it in GitHub Desktop.
# We send this as a response object. Rack can use any object that responds to each() as
# response body. When each() is called, the object
# will activate the FTP connection and couple the FTP receive loop with the HTTP send loop.
# This will probably go totally kaput on shitservers like Webrick that still insist on buffering
# any output, but someone who uses Webrick can better shoot himself on the spot. Since
# we provide range support, we need to pass it downstream to the FTP server and we need to send
# exactly the amount of bytes requested to satisfy range.
class Fatpete::FTPStream
CHUNK_SIZE = 128 * 1024
SLEEP_BETWEEN_CHUNKS = 0.1
def initialize(server, login, password, path, start_at_offset, send_bytes)
@server, @login, @password, @path = server, login, password, path
@start_at = start_at_offset
@send_bytes = send_bytes
end
def each(&blk)
Net::FTP.open(@server) do | conn |
conn.resume = true # CRUCIAL!
conn.login(@login, @password)
conn.chdir(File.dirname(@path))
total_sent = 0
catch(:stop_transfer) do
# We use retrbinary since all default get methods in Net::FTP want to also buffer
# to a local file. We ain' doin' that.
conn.retrbinary("RETR " + File.basename(@path), CHUNK_SIZE, @start_at) do |data|
if (total_sent + Rack::Utils.bytesize(data)) > @send_bytes
# Chop off the response and close shop
blk.call(data[0...(@send_bytes - total_sent)])
throw :stop_transfer
else
blk.call(data)
total_sent += Rack::Utils.bytesize(data)
end
# Give others some room to breathe and do not peg the processor.
# To prevent a delay when starting the transfer send data first, then sleep
sleep(SLEEP_BETWEEN_CHUNKS)
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment