Мое решение сильно основано на snippets.dzone.com/posts/show/2469, который появился после того, как я набрал загрузку файла ruby в адресной строке FireFox ... так что вы проводили какие-либо исследования в Интернете, прежде чем задавать этот вопрос?
Давид
@Dejw: Я провел исследование и нашел здесь ответ на вопрос. В основном с тем же кодом, который вы мне дали. resp.bodyЧасть сбивает с толку меня , я думал , что это спасло бы только «тело» часть ответа , но я хочу сохранить весь / двоичный файл. Я также обнаружил, что сайт rio.rubyforge.org может быть полезным. Более того, на мой вопрос никто не может сказать, что на этот вопрос еще нет ответа :-)
Радек
3
Часть тела - это ровно целый файл. Ответ создается из заголовков (http) и тела (файла), поэтому, когда вы сохраняете тело, вы сохраняете файл ;-)
Давид
1
еще один вопрос ... допустим, файл имеет размер 100 МБ, и процесс загрузки прерывается на середине. Будет ли что-нибудь спасено? Могу ли я сделать резюме файла?
Radek
К сожалению, нет, потому что http.get('...')call отправляет запрос и получает ответ (весь файл). Чтобы загрузить файл по частям и сохранить его одновременно, см. Мой отредактированный ответ ниже ;-) Возобновить работу непросто, возможно, вы подсчитываете сохраненные байты, а затем пропускаете их при повторной загрузке файла ( file.write(resp.body)возвращает количество записанных байтов).
Давид
Ответы:
143
Самый простой способ - это решение для конкретной платформы:
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)endend
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)endendensure
f.close()end
Да, я знаю. Вот почему я сказал, что это так a platform-specific solution.
Давид
1
Больше решений для конкретных платформ: платформы GNU / Linux предоставляют wget. OS X предоставляет curl( curl http://oh.no/its/pbjellytime.flv --output secretlylove.flv). У Windows есть эквивалент Powershell (new-object System.Net.WebClient).DownloadFile('http://oh.no/its/pbjellytime.flv','C:\tmp\secretlylove.flv'). Бинарные файлы для wget и curl существуют также для всех операционных систем через загрузку. Я по-прежнему настоятельно рекомендую использовать стандартную библиотеку, если вы не пишете код исключительно для себя.
fny
1
начало ... гарантия ... конец не требуется, если используется форма открытого блока. открыть 'sample.flv' do | f | .... f.write segment
lab419
1
Нетекстовый файл приходит поврежденным.
Пол
1
Я использую фрагментированную загрузку с использованием файлов Net::HTTP. И я получаю часть файла, но получаю ответ Net::HTTPOK. Есть ли способ убедиться, что мы полностью загрузили файл?
Николай Кондратенко
118
Я знаю, что это старый вопрос, но 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)endend
open("http://somedomain.net/flv/sample/sample.flv", 'rb')откроет URL-адрес в двоичном режиме.
zoli
1
кто-нибудь знает, умеет ли open-uri заполнять буфер, как объяснил @Isa?
gdelfino
1
@gildefino Вы получите больше ответов, если откроете для этого новый вопрос. Маловероятно, что многие люди это прочитают (и это также уместно в Stack Overflow).
FWIW некоторые люди думают, что open-uri опасен, потому что он обезьяны исправляет весь код, включая код библиотеки, который использует openновую способность, которую вызывающий код может не ожидать. В openлюбом случае вы не должны доверять пользовательскому вводу , но теперь вам нужно быть вдвойне осторожным.
Основное преимущество здесь - краткость и простота, потому что на него openложится большая часть тяжелой работы. И он не считывает весь ответ в памяти.
Этот openметод будет передавать ответы> 1 КБ в файл Tempfile. Мы можем использовать эти знания для реализации этого метода бережливой загрузки в файл. См. OpenURI::BufferРеализацию здесь.
Будьте осторожны с вводом данных пользователем!
open(name, *rest, &block)небезопасно, если nameисходит от ввода пользователя!
Это должен быть принятый ответ, поскольку он краток и прост и не загружает весь файл в память ~ + производительность (предположительно здесь).
Nikkolasg
Я согласен с Nikkolasg. Я просто попробовал им пользоваться, работает очень хорошо. Я немного изменил его, хотя, например, локальный путь будет автоматически выводиться из указанного URL, например, "path = nil", а затем проверка на nil; если он равен нулю, я использую File.basename () для URL-адреса, чтобы определить локальный путь.
@SimonPerepelitsa хе-хе. Я пересмотрел его еще раз, теперь предоставляя краткий метод загрузки в файл, который не считывает весь ответ в памяти. Моего предыдущего ответа было бы достаточно, потому что на openсамом деле он не считывает ответ в памяти, он считывает его во временный файл для любых ответов> 10240 байт. Значит, ты был прав, но нет. Исправленный ответ устраняет это недоразумение и, надеюсь, служит отличным примером силы Ruby :)
Overbryd
3
Если вы получаете сообщение EACCES: permission deniedоб ошибке при изменении имени файла с помощью mvкоманды, это связано с тем, что вам сначала нужно закрыть файл. Предлагаю изменить эту часть наTempfile then io.close;
Дэвид Дуглас
28
Пример 3 в документации Ruby net / http показывает, как загрузить документ через HTTP и вывести файл вместо того, чтобы просто загружать его в память, заменив put двоичной записью в файл, например, как показано в ответе Dejw.
Более сложные случаи показаны ниже в том же документе.
Это считывает весь файл в память перед записью на диск, так что ... это может быть плохо.
kgilpin
@kgilpin оба решения?
KrauseFx
1
Да, оба решения.
eltiare
Тем не менее, если вы согласны с этим, используйте более короткую версию (при условии, что URL-адрес и имя файла находятся в переменных urlи file, соответственно), используя open-uriкак в первом: File.write(file, open(url).read)... Очень просто для тривиального случая загрузки.
Lindes
17
Расширение ответа Дежу (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 не дожидается заполнения буфера (16 КБ в v1.9.2), прежде чем уступить, поэтому центральный процессор занят перемещением небольших фрагментов. Сон на мгновение дает буферу возможность заполняться между записями, а использование ЦП сравнимо с решением curl, разница в 4-5 раз в моем приложении. Более надежное решение могло бы проверить ход выполнения f.posи отрегулировать тайм-аут для достижения , скажем, 95% размера буфера - фактически, именно так я получил число 0,005 в моем примере.
Извините, но я не знаю более элегантного способа заставить Ruby ждать заполнения буфера.
Редактировать:
Это версия, которая автоматически настраивается, чтобы поддерживать буфер на уровне или ниже емкости. Это неэлегантное решение, но оно кажется таким же быстрым и использует столько же процессорного времени, сколько требует curl.
Работает в три этапа. Короткий период обучения с заведомо долгим временем ожидания определяет размер полного буфера. Период отбрасывания быстро сокращает время ожидания с каждой итерацией, умножая его на больший коэффициент, пока не будет обнаружен недостаточно заполненный буфер. Затем, в течение обычного периода, он регулируется вверх и вниз с меньшим коэффициентом.
Мой 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 =0File.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 endif learn <=0then
learn -=1elsif delta == max_delta thenif drop_period then
pause /= opt[:drop_factor]else
pause /= opt[:adjust]endelsif delta < max_delta then
drop_period =false
pause *= opt[:adjust]end
sleep(pause)}}}}end
resp.body
Часть сбивает с толку меня , я думал , что это спасло бы только «тело» часть ответа , но я хочу сохранить весь / двоичный файл. Я также обнаружил, что сайт rio.rubyforge.org может быть полезным. Более того, на мой вопрос никто не может сказать, что на этот вопрос еще нет ответа :-)http.get('...')
call отправляет запрос и получает ответ (весь файл). Чтобы загрузить файл по частям и сохранить его одновременно, см. Мой отредактированный ответ ниже ;-) Возобновить работу непросто, возможно, вы подсчитываете сохраненные байты, а затем пропускаете их при повторной загрузке файла (file.write(resp.body)
возвращает количество записанных байтов).Ответы:
Самый простой способ - это решение для конкретной платформы:
Вероятно, вы ищете:
Изменить: изменено. Спасибо.
Edit2: решение, которое сохраняет часть файла при загрузке:
источник
a platform-specific solution
.wget
. OS X предоставляетcurl
(curl http://oh.no/its/pbjellytime.flv --output secretlylove.flv
). У Windows есть эквивалент Powershell(new-object System.Net.WebClient).DownloadFile('http://oh.no/its/pbjellytime.flv','C:\tmp\secretlylove.flv')
. Бинарные файлы для wget и curl существуют также для всех операционных систем через загрузку. Я по-прежнему настоятельно рекомендую использовать стандартную библиотеку, если вы не пишете код исключительно для себя.Net::HTTP
. И я получаю часть файла, но получаю ответNet::HTTPOK
. Есть ли способ убедиться, что мы полностью загрузили файл?Я знаю, что это старый вопрос, но Google бросил меня сюда, и я думаю, что нашел более простой ответ.
В Railscasts # 179 Райан Бейтс использовал стандартный класс Ruby OpenURI для выполнения большей части того, о чем просили, например:
( Предупреждение : непроверенный код. Возможно, вам придется изменить / настроить его.)
источник
open("http://somedomain.net/flv/sample/sample.flv", 'rb')
откроет URL-адрес в двоичном режиме.HTTP
=>HTTPS
, и я узнал, как их решить с помощьюopen_uri_redirections
Gemopen
новую способность, которую вызывающий код может не ожидать. Вopen
любом случае вы не должны доверять пользовательскому вводу , но теперь вам нужно быть вдвойне осторожным.Вот мой Ruby http для использования файла
open(name, *rest, &block)
.Основное преимущество здесь - краткость и простота, потому что на него
open
ложится большая часть тяжелой работы. И он не считывает весь ответ в памяти.Этот
open
метод будет передавать ответы> 1 КБ в файлTempfile
. Мы можем использовать эти знания для реализации этого метода бережливой загрузки в файл. См.OpenURI::Buffer
Реализацию здесь.Будьте осторожны с вводом данных пользователем!
open(name, *rest, &block)
небезопасно, еслиname
исходит от ввода пользователя!источник
open
самом деле он не считывает ответ в памяти, он считывает его во временный файл для любых ответов> 10240 байт. Значит, ты был прав, но нет. Исправленный ответ устраняет это недоразумение и, надеюсь, служит отличным примером силы Ruby :)EACCES: permission denied
об ошибке при изменении имени файла с помощьюmv
команды, это связано с тем, что вам сначала нужно закрыть файл. Предлагаю изменить эту часть наTempfile then io.close;
Пример 3 в документации Ruby net / http показывает, как загрузить документ через HTTP и вывести файл вместо того, чтобы просто загружать его в память, заменив put двоичной записью в файл, например, как показано в ответе Dejw.
Более сложные случаи показаны ниже в том же документе.
источник
Вы можете использовать open-uri, который является однострочным
Или используя net / http
источник
url
иfile
, соответственно), используяopen-uri
как в первом:File.write(file, open(url).read)
... Очень просто для тривиального случая загрузки.Расширение ответа Дежу (edit2):
где
filename
иurl
- строки.Команда
sleep
- это хитрость, которая может значительно снизить загрузку ЦП, когда сеть является ограничивающим фактором. Net :: HTTP не дожидается заполнения буфера (16 КБ в v1.9.2), прежде чем уступить, поэтому центральный процессор занят перемещением небольших фрагментов. Сон на мгновение дает буферу возможность заполняться между записями, а использование ЦП сравнимо с решением curl, разница в 4-5 раз в моем приложении. Более надежное решение могло бы проверить ход выполненияf.pos
и отрегулировать тайм-аут для достижения , скажем, 95% размера буфера - фактически, именно так я получил число 0,005 в моем примере.Извините, но я не знаю более элегантного способа заставить Ruby ждать заполнения буфера.
Редактировать:
Это версия, которая автоматически настраивается, чтобы поддерживать буфер на уровне или ниже емкости. Это неэлегантное решение, но оно кажется таким же быстрым и использует столько же процессорного времени, сколько требует curl.
Работает в три этапа. Короткий период обучения с заведомо долгим временем ожидания определяет размер полного буфера. Период отбрасывания быстро сокращает время ожидания с каждой итерацией, умножая его на больший коэффициент, пока не будет обнаружен недостаточно заполненный буфер. Затем, в течение обычного периода, он регулируется вверх и вниз с меньшим коэффициентом.
Мой Ruby немного заржавел, так что я уверен, что это можно улучшить. Во-первых, нет обработки ошибок. Кроме того, возможно, его можно было бы разделить на объект, вдали от самой загрузки, чтобы вы просто вызывали
autosleep.sleep(f.pos)
свой цикл? Более того, Net :: HTTP можно изменить так, чтобы он ждал полного буфера перед выдачей :-)источник
sleep
Хак нравится !Есть больше api-friendly библиотек, чем
Net::HTTP
, например, httparty :источник
У меня были проблемы, если в файле были немецкие умлауты (ä, ö, ü). Я мог бы решить проблему, используя:
источник
если вы ищете способ загрузить временный файл, сделать что-нибудь и удалить его, попробуйте этот драгоценный камень https://github.com/equivalent/pull_tempfile
источник