LoginSignup
2
0

More than 5 years have passed since last update.

メモ: Ruby の Net::FTP で分割ダウンロード

Last updated at Posted at 2018-06-23

:thinking::thought_balloon: docs.ruby-lang.org か rubygems.org に良い感じの実装が転がっている気がするんだけど、みつけられてない。

  • Net::FTP#retrbinary を使ってオフセットを指定
  • IO.copy_stream
    • 良い感じの file 結合処理を思いつけず IO.copy_stream を使った
    • 並行分割ダウンロード結果を統合するロジックを分かっていない
  • 同時接続数を制限しているサーバ相手には機能しない
require 'net/ftp'
require 'tmpdir'
require 'thwait'

DEBUG = !ENV.fetch('DEBUG', '').empty?
CONCURRENT = ENV.fetch('CONCURRENT').to_i
FTP_HOST = ENV.fetch('FTP_HOST')
FTP_PORT = ENV.fetch('FTP_PORT').to_i
FTP_USER = ENV.fetch('FTP_USER')
FTP_PASS = ENV.fetch('FTP_PASS')
FTP_PATH = ENV.fetch('FTP_PATH')

def establish_connection
  Net::FTP.new(FTP_HOST, port: FTP_PORT, username: FTP_USER, password: FTP_PASS, passive: true, debug_mode: DEBUG)
end

content_size = establish_connection.size(FTP_PATH)
page_size = (content_size.to_f / CONCURRENT).ceil

Dir.mktmpdir do |dir|
  threads = []
  CONCURRENT.times do |i|
    offset = page_size * i
    next if offset >= content_size

    threads << Thread.start(i) do |t|
      File.open("#{dir}/#{t}", 'wb') do |page|
        ftp = establish_connection
        ftp.resume = true

        retrieved = 0
        ftp.retrbinary("RETR #{FTP_PATH}", Net::FTP::DEFAULT_BLOCKSIZE, offset) do |chunk|
          rest = page_size - retrieved
          retrievable = [rest, chunk.bytesize].min
          break if retrievable <= 0

          retrieved += page.write(chunk[0, retrievable])
        end
      end
    end
  end
  ThreadsWait.all_waits(*threads)

  File.open('downloaded', 'wb') do |output|
    CONCURRENT.times do |i|
      path = "#{dir}/#{i}"
      next unless File.exist?(path)
      IO.copy_stream(path, output)
    end
  end
end

__END__

### e.g. 495MB CentOS ISO file

ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17]

1: 165.29 real         5.34 user         4.74 sys
3:  99.68 real         5.23 user         6.12 sys
5:  88.19 real         5.56 user         7.43 sys
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0