IT story

HTTP를 통해 바이너리 파일을 다운로드하려면 어떻게합니까?

hot-time 2020. 7. 3. 18:12
반응형

HTTP를 통해 바이너리 파일을 다운로드하려면 어떻게합니까?


Ruby를 사용하여 HTTP를 통해 바이너리 파일을 다운로드하고 저장하려면 어떻게합니까?

URL은 http://somedomain.net/flv/sample/sample.flv입니다.

저는 Windows 플랫폼을 사용하고 있으며 외부 프로그램을 실행하지 않는 것을 선호합니다.


가장 간단한 방법은 플랫폼 별 솔루션입니다.

 #!/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."

편집 : 변경되었습니다. 감사합니다.

편집 2 : 다운로드하는 동안 파일의 일부를 저장하는 솔루션 :

# 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

나는 이것이 오래된 질문이라는 것을 알고 있지만 Google에서 나를 던졌고 더 간단한 대답을 찾았습니다.

에서 Railscasts # 179 , 라이언 베이츠는 루비 표준 클래스를 사용 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

다음은 파일을 사용하는 Ruby http 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) }
  when Tempfile then io.close; FileUtils.mv(io.path, path)
  end
end

여기서 가장 큰 장점은 간결하고 간단 open합니다. 많은 양의 리프팅이 있기 때문 입니다. 그리고 메모리에서 전체 응답을 읽지 않습니다.

open방법은> 1kb 이상의 응답을 a로 스트리밍합니다 Tempfile. 이 지식을 활용하여이 린 다운로드 파일 방법을 구현할 수 있습니다. 여기 에서 OpenURI::Buffer구현을 참조 하십시오.

사용자가 제공 한 입력에주의하십시오! 사용자 입력에서 오는 open(name, *rest, &block)경우 안전하지 않습니다 name!


Ruby의 net / http 문서 에있는 예제 3은 HTTP를 통해 문서를 다운로드하고 파일을 메모리에로드하는 대신 파일을 출력하는 방법을 보여줍니다 (예 : Dejw의 답변에 표시된대로 파일에 2 진 쓰기로 대체).

더 복잡한 사례는 동일한 문서에서 더 아래에 표시됩니다.


You can use open-uri, which is a one liner

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

Or by using net/http

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

Expanding on Dejw's answer (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 
      }
    }
  }
}

where filename and url are strings.

The sleep command is a hack that can dramatically reduce CPU usage when the network is the limiting factor. Net::HTTP doesn't wait for the buffer (16kB in v1.9.2) to fill before yielding, so the CPU busies itself moving small chunks around. Sleeping for a moment gives the buffer a chance to fill between writes, and CPU usage is comparable to a curl solution, 4-5x difference in my application. A more robust solution might examine progress of f.pos and adjust the timeout to target, say, 95% of the buffer size -- in fact that's how I got the 0.005 number in my example.

Sorry, but I don't know a more elegant way of having Ruby wait for the buffer to fill.

Edit:

This is a version that automatically adjusts itself to keep the buffer just at or below capacity. It's an inelegant solution, but it seems to be just as fast, and to use as little CPU time, as it's calling out to curl.

It works in three stages. A brief learning period with a deliberately long sleep time establishes the size of a full buffer. The drop period reduces the sleep time quickly with each iteration, by multiplying it by a larger factor, until it finds an under-filled buffer. Then, during the normal period, it adjusts up and down by a smaller factor.

My Ruby's a little rusty, so I'm sure this can be improved upon. First of all, there's no error handling. Also, maybe it could be separated into an object, away from the downloading itself, so that you'd just call autosleep.sleep(f.pos) in your loop? Even better, Net::HTTP could be changed to wait for a full buffer before yielding :-)

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

There are more api-friendly libraries than Net::HTTP, for example 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

I had problems, if the file contained German Umlauts (ä,ö,ü). I could solve the problem by using:

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

if you looking for a way how to download temporary file, do stuff and delete it try this gem 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

참고URL : https://stackoverflow.com/questions/2263540/how-do-i-download-a-binary-file-over-http

반응형