Как загрузить и сохранить двоичный файл через HTTP с помощью Ruby?
URL-адрес http://somedomain.net/flv/sample/sample.flv
.
Я на платформе Windows, и я бы предпочел не запускать какую-либо внешнюю программу.
Как загрузить и сохранить двоичный файл через HTTP с помощью Ruby?
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."
Изменить: Изменено. Спасибо.
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
Я знаю, что это старый вопрос, но Google бросил меня сюда, и я думаю, что нашел более простой ответ.
В Railscasts # 179 Райан Бэйтс использовал стандартный класс 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
Пример 3 в Ruby net/http documentation показывает, как загрузить документ через HTTP, и вывести файл, а не просто загружать его в память, заменяет puts на двоичную запись в файл, например как показано в ответе Дейва.
Более сложные случаи показаны ниже в том же документе.
Вот мой http файл Ruby с помощью IO::copy_stream(src, dst)
.
require "open-uri"
def download(url, path)
File.open(path, "w") do |f|
IO.copy_stream(open(url), f)
end
end
Основное преимущество здесь в том, что он читает и записывает в куски и, таким образом, не считывает весь ответ в памяти.
Я использую open(name, *rest, &block)
для этой демонстрации. Первым аргументом IO::copy_stream(src, dst)
может быть любой объект ввода-вывода, который отвечает на чтение.
Пожалуйста, будьте осторожны с введенным пользователем пользователем!
open(name, *rest, &block)
небезопасно, если name
поступает с пользовательского ввода!
Вы можете использовать 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")))
Расширение ответа 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
}
}
}
}
где filename
и url
являются строками.
Команда sleep
- это хак, который может резко уменьшить использование ЦП, когда сеть является ограничивающим фактором. Net:: HTTP не дожидается заполнения буфера (16kB в v1.9.2) до урожая, поэтому процессор сам по себе перемещает небольшие куски вокруг. Сон на мгновение дает буферу возможность заполнить между записью, а использование ЦП сопоставимо с решением завитки, разница в 4-5 раз в моем приложении. Более надежное решение может проверить прогресс f.pos
и настроить таймаут для целевого, скажем, 95% от размера буфера - на самом деле, как я получил номер 0.005 в моем примере.
Извините, но я не знаю более элегантный способ заставить Ruby ждать заполнения буфера.
Edit:
Это версия, которая автоматически настраивается, чтобы поддерживать буфер только в пределах или ниже емкости. Это неэлегантное решение, но, похоже, оно так же быстро, и для использования в качестве минимального времени процессора, поскольку оно вызывает завиток.
Он работает в три этапа. Краткий период обучения с преднамеренно длительным временем сна устанавливает размер полного буфера. Период падения быстро сокращает время сна с каждой итерацией, умножая ее на больший коэффициент, пока не найдет недозаполненный буфер. Затем, в течение нормального периода, он регулирует вверх и вниз меньшим коэффициентом.
Мой рубин немного ржавый, поэтому я уверен, что это можно улучшить. Прежде всего, нет обработки ошибок. Кроме того, может быть, он может быть разделен на объект, вдали от самой загрузки, так что вы просто вызываете 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 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
Существует больше api-дружественных библиотек, чем Net::HTTP
, например 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
У меня были проблемы, если в файле содержались немецкие Umlauts (ä, ö, ü). Я мог бы решить проблему, используя:
ec = Encoding::Converter.new('iso-8859-1', 'utf-8')
...
f << ec.convert(seg)
...
Если вы ищете способ загрузки временного файла, сделайте что-нибудь и удалите его, попробуйте этот камень 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