Skip to content

Instantly share code, notes, and snippets.

@johnjohndoe
Forked from leecade/download.rb
Created December 16, 2012 17:46

Revisions

  1. johnjohndoe revised this gist Dec 16, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion download.rb
    Original file line number Diff line number Diff line change
    @@ -174,7 +174,7 @@ def self.ftp_download(parsed_uri, full_path)
    if __FILE__ == $0
    usage = <<-EOU
    usage: ruby download.rb URL FILE
    usage: ruby #{File.basename($0)} URL FILE
    URL http/https/ftp location of the file to download
    FILE full local path at which to save downloaded file
  2. @jonforums jonforums revised this gist Mar 26, 2012. 1 changed file with 1 addition and 2 deletions.
    3 changes: 1 addition & 2 deletions download.rb
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@
    #
    # Author: Jon Maken
    # License: 3-clause BSD
    # Revision: 2012-03-25 21:09:01 -0600
    # Revision: 2012-03-25 21:18:59 -0600

    require 'net/http'
    require 'net/https' if RUBY_VERSION < '1.9'
    @@ -77,7 +77,6 @@ def self.http_download(url, full_path, count)
    end
    end

    message "Downloading #{filename} "
    http.request_get(uri.path) do |response|
    case response
    when Net::HTTPNotFound
  3. @jonforums jonforums created this gist Mar 26, 2012.
    192 changes: 192 additions & 0 deletions download.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,192 @@
    #!/usr/bin/env ruby
    # An HTTP/HTTPS/FTP file downloader library/CLI based upon MiniPortile's
    # HTTP implementation.
    #
    # Author: Jon Maken
    # License: 3-clause BSD
    # Revision: 2012-03-25 21:09:01 -0600

    require 'net/http'
    require 'net/https' if RUBY_VERSION < '1.9'
    require 'net/ftp'
    require 'fileutils'
    require 'tempfile'

    class Downloader

    @logger = STDOUT.binmode
    @max_ca_verify_depth = 5

    def self.download_file(url, full_path, count = 3)
    return if File.exist?(full_path)

    uri = URI.parse(url)
    case uri.scheme.downcase
    when /ftp/
    ftp_download(uri, full_path)
    when /http|https/
    http_download(url, full_path, count)
    end
    end


    private
    def self.message(text)
    @logger.print text
    @logger.flush
    end

    def self.output(text = '')
    @logger.puts text
    @logger.flush
    end

    def self.http_download(url, full_path, count)
    filename = File.basename(full_path)

    begin
    uri = URI.parse(url)

    if ENV['HTTP_PROXY']
    protocol, userinfo, proxy_host, proxy_port = URI::split(ENV['HTTP_PROXY'])
    proxy_user, proxy_pass = userinfo.split(/:/) if userinfo
    http = Net::HTTP.new(uri.host, uri.port, proxy_host, proxy_port, proxy_user, proxy_pass)
    else
    http = Net::HTTP.new(uri.host, uri.port)
    end

    if uri.scheme.downcase == 'https'
    http.use_ssl = true
    if ENV['CA_CERT_FILE']
    cert_file = ENV['CA_CERT_FILE'].dup
    cert_file.gsub!(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
    end
    if cert_file && File.exist?(cert_file)
    http.ca_file = cert_file
    http.verify_mode = OpenSSL::SSL::VERIFY_PEER
    http.verify_depth = @max_ca_verify_depth
    else
    raise <<-EOT
    To download using HTTPS you must first set the CA_CERT_FILE
    environment variable to the path of a valid CA certificate file.
    A file of bundled public CA certs may be downloaded from:
    http://curl.haxx.se/ca/cacert.pem
    EOT
    end
    end

    message "Downloading #{filename} "
    http.request_get(uri.path) do |response|
    case response
    when Net::HTTPNotFound
    output "404 - Not Found"
    return false

    when Net::HTTPClientError
    output "Error: Client Error: #{response.inspect}"
    return false

    when Net::HTTPRedirection
    raise "Too many redirections for the original URL, halting." if count <= 0
    url = response["location"]
    return download_file(url, full_path, count - 1)

    when Net::HTTPOK
    temp_file = Tempfile.new("download-#{filename}")
    temp_file.binmode

    size = 0
    progress = 0
    total = response.header["Content-Length"].to_i

    response.read_body do |chunk|
    temp_file << chunk
    size += chunk.size
    new_progress = (size * 100) / total
    unless new_progress == progress
    message "\rDownloading %s (%3d%%) " % [filename, new_progress]
    end
    progress = new_progress
    end

    output

    temp_file.close
    File.unlink full_path if File.exists?(full_path)
    FileUtils.mkdir_p File.dirname(full_path)
    FileUtils.mv temp_file.path, full_path, :force => true
    end
    end

    rescue Exception => e
    File.unlink full_path if File.exists?(full_path)
    output "ERROR: #{e.message}"
    raise "Failed to download file"
    end
    end

    def self.ftp_download(parsed_uri, full_path)
    filename = File.basename(parsed_uri.path)

    begin
    temp_file = Tempfile.new("download-#{filename}")
    temp_file.binmode

    size = 0
    progress = 0

    # TODO add user/pw support
    Net::FTP.open(parsed_uri.host) do |ftp|
    ftp.passive = true
    ftp.login
    ftp.chdir(File.dirname(parsed_uri.path))
    total = ftp.size(filename)

    ftp.getbinaryfile(filename, nil, 8192) do |chunk|
    temp_file << chunk
    size += chunk.size
    new_progress = (size * 100) / total
    unless new_progress == progress
    message "\rDownloading %s (%3d%%) " % [filename, new_progress]
    end
    progress = new_progress
    end
    end

    output

    temp_file.close
    File.unlink full_path if File.exists?(full_path)
    FileUtils.mkdir_p File.dirname(full_path)
    FileUtils.mv temp_file.path, full_path, :force => true

    rescue Exception => e
    File.unlink full_path if File.exists?(full_path)
    output "ERROR: #{e.message}"
    raise "Failed to download file"
    end
    end

    end # Downloader


    if __FILE__ == $0
    usage = <<-EOU
    usage: ruby download.rb URL FILE
    URL http/https/ftp location of the file to download
    FILE full local path at which to save downloaded file
    influential environment variables:
    HTTP_PROXY url to http proxy
    CA_CERT_FILE full path to CA certificate file
    EOU

    abort usage if ARGV.length != 2

    Downloader.download_file(ARGV[0], ARGV[1])
    end