How do I download a binary file over HTTP?

How do I download and save a binary file over HTTP using Ruby?

The URL is http://somedomain.net/flv/sample/sample.flv.

I am on the Windows platform and I would prefer not to run any external program.


Solution 1:

The simplest way is the platform-specific solution:

 #!/usr/bin/env ruby
`wget http://somedomain.net/flv/sample/sample.flv`

Probably you are searching for:

require 'net/http'
# Must be somedomain.net instead of somedomain.net/, otherwise, it will throw exception.
Net::HTTP.start("somedomain.net") do |http|
    resp = http.get("/flv/sample/sample.flv")
    open("sample.flv", "wb") do |file|
        file.write(resp.body)
    end
end
puts "Done."

Edit: Changed. Thank You.

Edit2: The solution which saves part of a file while downloading:

# instead of http.get
f = open('sample.flv')
begin
    http.request_get('/sample.flv') do |resp|
        resp.read_body do |segment|
            f.write(segment)
        end
    end
ensure
    f.close()
end

Solution 2:

I know that this is an old question, but Google threw me here and I think I found a simpler answer.

In Railscasts #179, Ryan Bates used the Ruby standard class OpenURI to do much of what was asked like this:

(Warning: untested code. You might need to change/tweak it.)

require 'open-uri'

File.open("/my/local/path/sample.flv", "wb") do |saved_file|
  # the following "open" is provided by open-uri
  open("http://somedomain.net/flv/sample/sample.flv", "rb") do |read_file|
    saved_file.write(read_file.read)
  end
end

Solution 3:

Here is my Ruby http to file using open(name, *rest, &block).

require "open-uri"
require "fileutils"

def download(url, path)
  case io = open(url)
  when StringIO then File.open(path, 'w') { |f| f.write(io.read) }
  when Tempfile then io.close; FileUtils.mv(io.path, path)
  end
end

The main advantage here it is concise and simple, because open does much of the heavy lifting. And it does not read the whole response in memory.

The open method will stream responses > 1kb to a Tempfile. We can exploit this knowledge to implement this lean download to file method. See the OpenURI::Buffer implementation here.

Please be careful with user provided input! open(name, *rest, &block) is unsafe if name is coming from user input!

Use OpenURI::open_uri to avoid reading files from disk:

...
case io = OpenURI::open_uri(url)
...

Solution 4:

Example 3 in the Ruby's net/http documentation shows how to download a document over HTTP, and to output the file instead of just loading it into memory, substitute puts with a binary write to a file, e.g. as shown in Dejw's answer.

More complex cases are shown further down in the same document.