web-dev-qa-db-ja.com

HTTP経由でバイナリファイルをダウンロードするにはどうすればよいですか?

Rubyを使用してHTTP経由でバイナリファイルをダウンロードして保存するにはどうすればよいですか?

URLはhttp://somedomain.net/flv/sample/sample.flvです。

私はWindowsプラットフォームを使用していますが、外部プログラムを実行したくないです。

128
Radek

最も簡単な方法は、プラットフォーム固有のソリューションです。

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

おそらくあなたは探しています:

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."

編集:変更。ありがとうございました。

Edit2:ダウンロード中にファイルの一部を保存するソリューション:

# 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
140
Dawid

これは古い質問であることは知っていますが、Googleがここに私を投げたので、より簡単な答えを見つけたと思います。

Railscasts#179 では、Ryan BatesはRuby標準クラスを使用しました OpenURI は、このように求められたものの多くを実行しました。

警告:テストされていないコード。変更または調整が必要な場合があります。)

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
114
kikito

open(name, *rest, &block) を使用したファイルへのRuby httpです。

require "open-uri"
require "fileutils"

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

ここでの主な利点は、openが多くの面倒な作業を行うため、簡潔でシンプルなことです。 そして、メモリ内の応答全体を読み取りません

openメソッドは、1kbを超える応答をTempfileにストリーミングします。この知識を活用して、この無駄のないダウンロードファイル方式を実装できます。 OpenURI::Buffer implementation を参照してください。

ユーザー提供の入力に注意してください! open(name, *rest, &block) は、nameがユーザー入力から来ている場合は安全ではありません!

41
Overbryd

Rubyの net/http documentation の例3は、ドキュメントをHTTP経由でダウンロードし、ファイルをメモリに単にロードするのではなく出力する方法を示しています。 Dejwの答えに示されているように。

より複雑なケースは、同じドキュメントのさらに下に示されています。

28
Arkku

ワンライナーであるopen-uriを使用できます

require 'open-uri'
content = open('http://example.com').read

またはnet/httpを使用して

require 'net/http'
File.write("file_name", Net::HTTP.get(URI.parse("http://url.com")))
25
KrauseFx

Dejwの答えを拡張(edit2):

File.open(filename,'w'){ |f|
  uri = URI.parse(url)
  Net::HTTP.start(uri.Host,uri.port){ |http| 
    http.request_get(uri.path){ |res| 
      res.read_body{ |seg|
        f << seg
#hack -- adjust to suit:
        sleep 0.005 
      }
    }
  }
}

ここで、filenameurlは文字列です。

sleepコマンドは、ハックです。 劇的に ネットワークが制限要因である場合は、CPU使用率を減らします。 Net :: HTTPは、バッファ(v1.9.2では16kB)が満杯になるのを待たずに譲歩するため、CPU自体が小さなチャンクを移動しながらビジーになります。少しの間スリープすると、バッファが書き込みと書き込みの間を埋める機会を与え、CPU使用量はcurlソリューションに匹敵します。これは私のアプリケーションの4〜5倍の差です。より堅牢なソリューションでは、f.posの進行状況を調べ、タイムアウトをターゲット、たとえばバッファーサイズの95%に調整します。実際、この例では0.005の数値を取得しました。

申し訳ありませんが、バッファがいっぱいになるまでRuby待機させるよりエレガントな方法はわかりません。

編集:

これは、バッファを容量以下に保つように自動的に調整するバージョンです。これは洗練されていないソリューションですが、curlを呼び出すのと同じくらい高速で、使用するCPU時間も少ないようです。

3段階で機能します。意図的に長いスリープ時間での短い学習期間は、完全なバッファのサイズを確立します。ドロップ期間は、バッファがいっぱいになるまで、それをより大きな係数で乗算することにより、各反復でスリープ時間を迅速に短縮します。次に、通常の期間中に、より小さな係数で上下に調整します。

私のRubyは少しさびているので、これを改善できると確信しています。まず第一に、エラー処理はありません。また、ダウンロード自体から離れてオブジェクトに分離することもできます。そのため、ループでautosleep.sleep(f.pos)を呼び出すだけですか?さらに良いことには、Net :: HTTPを変更して、バッファが一杯になるのを待ってから:-)を生成できます。

def http_to_file(filename,url,opt={})
  opt = {
    :init_pause => 0.1,    #start by waiting this long each time
                           # it's deliberately long so we can see 
                           # what a full buffer looks like
    :learn_period => 0.3,  #keep the initial pause for at least this many seconds
    :drop => 1.5,          #fast reducing factor to find roughly optimized pause time
    :adjust => 1.05        #during the normal period, adjust up or down by this factor
  }.merge(opt)
  pause = opt[:init_pause]
  learn = 1 + (opt[:learn_period]/pause).to_i
  drop_period = true
  delta = 0
  max_delta = 0
  last_pos = 0
  File.open(filename,'w'){ |f|
    uri = URI.parse(url)
    Net::HTTP.start(uri.Host,uri.port){ |http|
      http.request_get(uri.path){ |res|
        res.read_body{ |seg|
          f << seg
          delta = f.pos - last_pos
          last_pos += delta
          if delta > max_delta then max_delta = delta end
          if learn <= 0 then
            learn -= 1
          elsif delta == max_delta then
            if drop_period then
              pause /= opt[:drop_factor]
            else
              pause /= opt[:adjust]
            end
          elsif delta < max_delta then
            drop_period = false
            pause *= opt[:adjust]
          end
          sleep(pause)
        }
      }
    }
  }
end
17
Isa

Net::HTTPよりもAPIに適したライブラリがあります。たとえば、 httparty

require "httparty"
File.open("/tmp/my_file.flv", "wb") do |f| 
  f.write HTTParty.get("http://somedomain.net/flv/sample/sample.flv").parsed_response
end
13
fguillen

ファイルにドイツ語のウムラウト(ä、ö、ü)が含まれている場合、問題が発生しました。私は次を使用して問題を解決できました:

ec = Encoding::Converter.new('iso-8859-1', 'utf-8')
...
f << ec.convert(seg)
...
3
Rolf

一時ファイルをダウンロードする方法を探している場合は、この宝石を試してみてください https://github.com/equivalent/pull_tempfile

require 'pull_tempfile'

PullTempfile.transaction(url: 'https://mycompany.org/stupid-csv-report.csv', original_filename: 'dont-care.csv') do |tmp_file|
  CSV.foreach(tmp_file.path) do |row|
    # ....
  end
end
0
equivalent8