Solusi saya sangat didasarkan pada snippets.dzone.com/posts/show/2469 yang muncul setelah saya mengetik unduhan file ruby di bilah alamat FireFox ... jadi, apakah Anda melakukan riset di internet sebelum mengajukan pertanyaan ini?
Dawid
@ Djww: Saya melakukan riset dan menemukan pertanyaan yang dijawab di sini. Pada dasarnya dengan kode yang sama Anda memberi saya. Bagian resp.bodyini membingungkan saya, saya pikir itu akan menyelamatkan hanya bagian 'tubuh' dari respon tetapi saya ingin menyimpan seluruh file / biner. Saya juga menemukan bahwa rio.rubyforge.org dapat membantu. Apalagi dengan pertanyaan saya, tidak ada yang bisa mengatakan bahwa pertanyaan seperti itu belum dijawab :-)
Radek
3
Bagian tubuh persis seluruh file. Respons dibuat dari tajuk (http) dan tubuh (file), jadi ketika Anda menyimpan tubuh Anda menyimpan file ;-)
Dawid
1
satu pertanyaan lagi ... misalkan file berukuran 100MB dan proses pengunduhan terganggu di tengah. Apakah akan ada sesuatu yang diselamatkan? Bisakah saya melakukan resume file?
Radek
Sayangnya tidak, karena http.get('...')panggilan mengirim permintaan dan menerima respons (seluruh file). Untuk mengunduh file dalam potongan dan menyimpannya secara bersamaan, lihat jawaban saya yang diedit di bawah ini ;-) Memulai kembali tidak mudah, mungkin Anda menghitung byte yang Anda simpan dan kemudian lewati ketika Anda mengunduh ulang file ( file.write(resp.body)mengembalikan jumlah byte yang ditulis).
Dawid
Jawaban:
143
Cara paling sederhana adalah solusi khusus platform:
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."
Edit: Diubah. Terima kasih.
Edit2: Solusi yang menyimpan sebagian file saat mengunduh:
# 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
Ya saya tahu. Itu sebabnya saya mengatakan itu a platform-specific solution.
Dawid
1
Lebih banyak solusi spesifik platform: platform GNU / Linux menyediakan wget. OS X menyediakan curl( curl http://oh.no/its/pbjellytime.flv --output secretlylove.flv). Windows memiliki padanan Powershell (new-object System.Net.WebClient).DownloadFile('http://oh.no/its/pbjellytime.flv','C:\tmp\secretlylove.flv'). Biner untuk wget dan curl ada untuk semua sistem operasi melalui unduhan juga. Saya masih sangat merekomendasikan menggunakan perpustakaan standar kecuali jika kode tulisan Anda hanya untuk kekasih Anda sendiri.
fny
1
awal ... memastikan ... ujung tidak diperlukan jika formulir blok terbuka digunakan. buka 'sample.flv' do | f | .... f.write segment
lab419
1
File non-teks tiba rusak.
Paul
1
Saya menggunakan unduhan chunked menggunakan Net::HTTP. Dan saya menerima bagian dari file tetapi mendapat respons Net::HTTPOK. Apakah ada cara untuk memastikan kami mengunduh file sepenuhnya?
Nickolay Kondratenko
118
Saya tahu ini adalah pertanyaan lama, tetapi Google melemparkan saya ke sini dan saya pikir saya menemukan jawaban yang lebih sederhana.
Dalam Railscasts # 179 , Ryan Bates menggunakan OpenURI kelas standar Ruby untuk melakukan banyak hal seperti ini:
( Peringatan : kode yang belum diuji. Anda mungkin perlu mengubah / menyesuaikannya.)
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')akan membuka URL dalam mode biner.
zoli
1
ada yang tahu kalau open-uri pintar mengisi buffer seperti @Isa jelaskan?
gdelfino
1
@gildefino Anda akan mendapatkan lebih banyak jawaban jika Anda membuka pertanyaan baru untuk itu. Sangat tidak mungkin bahwa banyak orang akan membaca ini (dan itu juga merupakan hal yang tepat untuk dilakukan di Stack Overflow).
FWIW beberapa orang berpikir bahwa open-uri berbahaya karena monkeypatches semua kode, termasuk kode library, yang digunakan opendengan kemampuan baru yang mungkin tidak diantisipasi oleh kode panggilan. Anda seharusnya tidak mempercayai input pengguna yang diteruskan open, tetapi Anda harus berhati-hati dua kali lipat sekarang.
Keuntungan utama di sini adalah ringkas dan sederhana, karena opentidak banyak mengangkat beban. Dan itu tidak membaca seluruh respons dalam memori.
The openMetode akan streaming tanggapan> 1kb ke Tempfile. Kami dapat memanfaatkan pengetahuan ini untuk menerapkan metode unduhan lean ke file ini. Lihat OpenURI::Bufferimplementasinya di sini.
Harap berhati-hati dengan input yang diberikan pengguna!
open(name, *rest, &block)tidak aman jika nameberasal dari input pengguna!
Ini harus menjadi jawaban yang diterima karena ringkas & sederhana & tidak memuat seluruh file dalam memori ~ kinerja + (perkiraan di sini).
Nikkolasg
Saya setuju dengan Nikkolasg. Saya hanya mencoba menggunakannya dan berfungsi dengan sangat baik. Saya memodifikasinya sedikit, misalnya, jalur lokal akan dideduksi secara otomatis dari URL yang diberikan, jadi mis. "Path = nil" dan kemudian memeriksa nil; jika nil, maka saya menggunakan File.basename () pada url untuk menyimpulkan jalur lokal.
@SimonPerepelitsa hehe. Saya merevisinya lagi, sekarang menyediakan metode unduh-ke-file singkat yang tidak membaca seluruh respons dalam memori. Jawaban saya sebelumnya sudah cukup, karena opensebenarnya tidak membaca respons di memori, itu membacanya menjadi file sementara untuk setiap tanggapan> 10240 byte. Jadi Anda baik-baik saja tetapi tidak. Jawaban yang direvisi membersihkan kesalahpahaman ini dan semoga berfungsi sebagai contoh yang bagus tentang kekuatan Ruby :)
Overbryd
3
Jika Anda mendapatkan EACCES: permission deniedkesalahan saat mengubah nama file dengan mvperintah itu karena Anda harus menutup file terlebih dahulu. Sarankan mengubah bagian itu keTempfile then io.close;
David Douglas
28
Contoh 3 dalam dokumentasi / http net Ruby menunjukkan cara mengunduh dokumen melalui HTTP, dan untuk menghasilkan file alih-alih hanya memuatnya ke dalam memori, gantilah dengan biner tulis ke file, misalnya seperti yang ditunjukkan dalam jawaban Dejw.
Kasus yang lebih kompleks ditunjukkan lebih jauh ke bawah dalam dokumen yang sama.
Ini membaca seluruh file ke dalam memori sebelum menulisnya ke disk, jadi ... itu bisa buruk.
kgilpin
@ kgilpin kedua solusi?
KrauseFx
1
Ya, keduanya solusi.
eltiare
Yang mengatakan, jika Anda setuju dengan itu, versi yang lebih pendek (dengan asumsi url dan nama file dalam variabel urldan file, masing-masing), menggunakan open-uriseperti pada yang pertama: File.write(file, open(url).read)... Sangat sederhana, untuk kasus unduhan sepele.
lindes
17
Memperluas jawaban 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}}}}
dimana filenamedan urlstring.
The sleepperintah adalah hack yang dapat secara dramatis mengurangi penggunaan CPU ketika jaringan adalah faktor pembatas. Net :: HTTP tidak menunggu buffer (16kB dalam v1.9.2) untuk diisi sebelum menghasilkan, sehingga CPU sibuk memindahkan potongan-potongan kecil. Tidur sebentar memberikan buffer kesempatan untuk mengisi antara menulis, dan penggunaan CPU sebanding dengan solusi keriting, perbedaan 4-5x dalam aplikasi saya. Solusi yang lebih kuat mungkin memeriksa kemajuan f.posdan menyesuaikan batas waktu untuk menargetkan, katakanlah, 95% dari ukuran buffer - sebenarnya itulah cara saya mendapatkan angka 0,005 dalam contoh saya.
Maaf, tapi saya tidak tahu cara yang lebih elegan untuk membuat Ruby menunggu buffer untuk mengisi.
Edit:
Ini adalah versi yang secara otomatis menyesuaikan diri untuk menjaga buffer hanya pada atau di bawah kapasitas. Ini adalah solusi yang tidak bagus, tetapi tampaknya sama cepatnya, dan menggunakan waktu CPU sesedikit mungkin, karena itu memanggil untuk mengeriting.
Ini bekerja dalam tiga tahap. Suatu periode pembelajaran singkat dengan waktu tidur yang sengaja panjang menetapkan ukuran buffer penuh. Periode drop mengurangi waktu tidur dengan cepat dengan setiap iterasi, dengan mengalikannya dengan faktor yang lebih besar, sampai menemukan buffer yang kurang terisi. Kemudian, selama periode normal, itu menyesuaikan atas dan ke bawah oleh faktor yang lebih kecil.
Ruby saya agak berkarat, jadi saya yakin ini bisa diperbaiki. Pertama-tama, tidak ada penanganan kesalahan. Juga, mungkin itu bisa dipisahkan menjadi objek, jauh dari pengunduhan itu sendiri, sehingga Anda hanya perlu menelepon autosleep.sleep(f.pos)di loop Anda? Bahkan lebih baik, Net :: HTTP dapat diubah untuk menunggu buffer penuh sebelum menghasilkan :-)
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
ini membingungkan saya, saya pikir itu akan menyelamatkan hanya bagian 'tubuh' dari respon tetapi saya ingin menyimpan seluruh file / biner. Saya juga menemukan bahwa rio.rubyforge.org dapat membantu. Apalagi dengan pertanyaan saya, tidak ada yang bisa mengatakan bahwa pertanyaan seperti itu belum dijawab :-)http.get('...')
panggilan mengirim permintaan dan menerima respons (seluruh file). Untuk mengunduh file dalam potongan dan menyimpannya secara bersamaan, lihat jawaban saya yang diedit di bawah ini ;-) Memulai kembali tidak mudah, mungkin Anda menghitung byte yang Anda simpan dan kemudian lewati ketika Anda mengunduh ulang file (file.write(resp.body)
mengembalikan jumlah byte yang ditulis).Jawaban:
Cara paling sederhana adalah solusi khusus platform:
Mungkin Anda sedang mencari:
Edit: Diubah. Terima kasih.
Edit2: Solusi yang menyimpan sebagian file saat mengunduh:
sumber
a platform-specific solution
.wget
. OS X menyediakancurl
(curl http://oh.no/its/pbjellytime.flv --output secretlylove.flv
). Windows memiliki padanan Powershell(new-object System.Net.WebClient).DownloadFile('http://oh.no/its/pbjellytime.flv','C:\tmp\secretlylove.flv')
. Biner untuk wget dan curl ada untuk semua sistem operasi melalui unduhan juga. Saya masih sangat merekomendasikan menggunakan perpustakaan standar kecuali jika kode tulisan Anda hanya untuk kekasih Anda sendiri.Net::HTTP
. Dan saya menerima bagian dari file tetapi mendapat responsNet::HTTPOK
. Apakah ada cara untuk memastikan kami mengunduh file sepenuhnya?Saya tahu ini adalah pertanyaan lama, tetapi Google melemparkan saya ke sini dan saya pikir saya menemukan jawaban yang lebih sederhana.
Dalam Railscasts # 179 , Ryan Bates menggunakan OpenURI kelas standar Ruby untuk melakukan banyak hal seperti ini:
( Peringatan : kode yang belum diuji. Anda mungkin perlu mengubah / menyesuaikannya.)
sumber
open("http://somedomain.net/flv/sample/sample.flv", 'rb')
akan membuka URL dalam mode biner.HTTP
=>HTTPS
pengalihan, dan menemukan cara menyelesaikannya menggunakanopen_uri_redirections
Permataopen
dengan kemampuan baru yang mungkin tidak diantisipasi oleh kode panggilan. Anda seharusnya tidak mempercayai input pengguna yang diteruskanopen
, tetapi Anda harus berhati-hati dua kali lipat sekarang.Ini adalah http Ruby saya untuk menggunakan file
open(name, *rest, &block)
.Keuntungan utama di sini adalah ringkas dan sederhana, karena
open
tidak banyak mengangkat beban. Dan itu tidak membaca seluruh respons dalam memori.The
open
Metode akan streaming tanggapan> 1kb keTempfile
. Kami dapat memanfaatkan pengetahuan ini untuk menerapkan metode unduhan lean ke file ini. LihatOpenURI::Buffer
implementasinya di sini.Harap berhati-hati dengan input yang diberikan pengguna!
open(name, *rest, &block)
tidak aman jikaname
berasal dari input pengguna!sumber
open
sebenarnya tidak membaca respons di memori, itu membacanya menjadi file sementara untuk setiap tanggapan> 10240 byte. Jadi Anda baik-baik saja tetapi tidak. Jawaban yang direvisi membersihkan kesalahpahaman ini dan semoga berfungsi sebagai contoh yang bagus tentang kekuatan Ruby :)EACCES: permission denied
kesalahan saat mengubah nama file denganmv
perintah itu karena Anda harus menutup file terlebih dahulu. Sarankan mengubah bagian itu keTempfile then io.close;
Contoh 3 dalam dokumentasi / http net Ruby menunjukkan cara mengunduh dokumen melalui HTTP, dan untuk menghasilkan file alih-alih hanya memuatnya ke dalam memori, gantilah dengan biner tulis ke file, misalnya seperti yang ditunjukkan dalam jawaban Dejw.
Kasus yang lebih kompleks ditunjukkan lebih jauh ke bawah dalam dokumen yang sama.
sumber
Anda dapat menggunakan open-uri, yang merupakan liner satu
Atau dengan menggunakan net / http
sumber
url
danfile
, masing-masing), menggunakanopen-uri
seperti pada yang pertama:File.write(file, open(url).read)
... Sangat sederhana, untuk kasus unduhan sepele.Memperluas jawaban Dejw (edit2):
dimana
filename
danurl
string.The
sleep
perintah adalah hack yang dapat secara dramatis mengurangi penggunaan CPU ketika jaringan adalah faktor pembatas. Net :: HTTP tidak menunggu buffer (16kB dalam v1.9.2) untuk diisi sebelum menghasilkan, sehingga CPU sibuk memindahkan potongan-potongan kecil. Tidur sebentar memberikan buffer kesempatan untuk mengisi antara menulis, dan penggunaan CPU sebanding dengan solusi keriting, perbedaan 4-5x dalam aplikasi saya. Solusi yang lebih kuat mungkin memeriksa kemajuanf.pos
dan menyesuaikan batas waktu untuk menargetkan, katakanlah, 95% dari ukuran buffer - sebenarnya itulah cara saya mendapatkan angka 0,005 dalam contoh saya.Maaf, tapi saya tidak tahu cara yang lebih elegan untuk membuat Ruby menunggu buffer untuk mengisi.
Edit:
Ini adalah versi yang secara otomatis menyesuaikan diri untuk menjaga buffer hanya pada atau di bawah kapasitas. Ini adalah solusi yang tidak bagus, tetapi tampaknya sama cepatnya, dan menggunakan waktu CPU sesedikit mungkin, karena itu memanggil untuk mengeriting.
Ini bekerja dalam tiga tahap. Suatu periode pembelajaran singkat dengan waktu tidur yang sengaja panjang menetapkan ukuran buffer penuh. Periode drop mengurangi waktu tidur dengan cepat dengan setiap iterasi, dengan mengalikannya dengan faktor yang lebih besar, sampai menemukan buffer yang kurang terisi. Kemudian, selama periode normal, itu menyesuaikan atas dan ke bawah oleh faktor yang lebih kecil.
Ruby saya agak berkarat, jadi saya yakin ini bisa diperbaiki. Pertama-tama, tidak ada penanganan kesalahan. Juga, mungkin itu bisa dipisahkan menjadi objek, jauh dari pengunduhan itu sendiri, sehingga Anda hanya perlu menelepon
autosleep.sleep(f.pos)
di loop Anda? Bahkan lebih baik, Net :: HTTP dapat diubah untuk menunggu buffer penuh sebelum menghasilkan :-)sumber
sleep
retas!Ada lebih banyak perpustakaan yang ramah api daripada
Net::HTTP
, misalnya httparty :sumber
Saya punya masalah, jika file tersebut berisi Umlaut Jerman (ä, ö, ü). Saya bisa memecahkan masalah dengan menggunakan:
sumber
jika Anda mencari cara bagaimana mengunduh file sementara, lakukan sesuatu dan hapus itu coba permata ini https://github.com/equivalent/pull_tempfile
sumber