Mở rộng về câu trả lời của 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
}
}
}
}
ở đâu filename
và url
là chuỗi.
Các sleep
lệnh là một hack mà có thể đột ngột giảm sử dụng CPU khi mạng là yếu tố hạn chế. Net :: HTTP không đợi bộ đệm (16kB trong v1.9.2) lấp đầy trước khi mang lại, do đó, CPU tự di chuyển các khối nhỏ xung quanh. Ngủ một lát sẽ cho bộ đệm một cơ hội để điền vào giữa ghi và việc sử dụng CPU có thể so sánh với giải pháp cuộn tròn, chênh lệch 4-5 lần trong ứng dụng của tôi. Một giải pháp mạnh mẽ hơn có thể kiểm tra tiến trình f.pos
và điều chỉnh thời gian chờ để nhắm mục tiêu, giả sử, 95% kích thước bộ đệm - thực tế đó là cách tôi lấy số 0,005 trong ví dụ của mình.
Xin lỗi, nhưng tôi không biết một cách thanh lịch hơn khi Ruby đợi bộ đệm lấp đầy.
Biên tập:
Đây là phiên bản tự động điều chỉnh chính nó để giữ bộ đệm ở mức hoặc dưới công suất. Đó là một giải pháp không phù hợp, nhưng dường như nó cũng nhanh và sử dụng ít thời gian CPU, vì nó đang kêu gọi cuộn tròn.
Nó hoạt động trong ba giai đoạn. Một thời gian học ngắn với thời gian ngủ dài có chủ ý thiết lập kích thước của một bộ đệm đầy đủ. Thời gian thả làm giảm thời gian ngủ nhanh chóng với mỗi lần lặp, bằng cách nhân nó với một yếu tố lớn hơn, cho đến khi nó tìm thấy một bộ đệm đầy. Sau đó, trong thời gian bình thường, nó điều chỉnh lên xuống theo một yếu tố nhỏ hơn.
Ruby của tôi hơi rỉ sét, vì vậy tôi chắc chắn điều này có thể được cải thiện. Trước hết, không có xử lý lỗi. Ngoài ra, có lẽ nó có thể được tách thành một đối tượng, cách xa bản tải xuống, để bạn chỉ cần gọi autosleep.sleep(f.pos)
trong vòng lặp của mình? Thậm chí tốt hơn, Net :: HTTP có thể được thay đổi để chờ bộ đệm đầy đủ trước khi mang lại :-)
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