класс Net::FTP
Этот класс реализует протокол File передачи файлов. Если вы использовали программу командной строки FTP и знакомы с её командами, вы легко освоите этот класс. В него включены дополнительные возможности, чтобы воспользоваться преимуществами стиля и сильными сторонами Ruby.
Пример
require 'net/ftp'
Пример 1
ftp = Net::FTP.new('example.com')
ftp.login
files = ftp.chdir('pub/lang/ruby/contrib')
files = ftp.list('n*')
ftp.getbinaryfile('nif.rb-0.91.gz', 'nif.gz', 1024)
ftp.close
Пример 2
Net::FTP.open('example.com') do |ftp|
ftp.login
files = ftp.chdir('pub/lang/ruby/contrib')
files = ftp.list('n*')
ftp.getbinaryfile('nif.rb-0.91.gz', 'nif.gz', 1024)
end
Основные методы
Ниже приведены методы, которые, скорее всего, будут полезны пользователям:
Константы
- CASE_DEPENDENT_PARSER
- CASE_INDEPENDENT_PARSER
- DECIMAL_PARSER
- FACT_PARSERS
- OCTAL_PARSER
- TIME_PARSER
Атрибуты
Когда true, передачи выполняются в двоичном режиме. Значение по умолчанию: true.
Когда true, весь трафик к серверу и от сервера записывается в +$stdout+. Значение по умолчанию: false.
Последний ответ сервера.
Код последнего ответа сервера.
Код последнего ответа сервера.
Количество секунд ожидания подключения. Можно использовать любое число, включая числа с плавающей точкой для дробных секунд. Если объект FTP не может открыть подключение за это время, он вызывает исключение Net::OpenTimeout. Значение по умолчанию: nil.
Когда true, подключение находится в пассивном режиме. Значение по умолчанию: true.
Количество секунд ожидания чтения одного блока (с помощью одного вызова read(2)). Можно использовать любое число, включая числа с плавающей точкой для дробных секунд. Если объект FTP не может прочитать данные за это время, он вызывает исключение Timeout::Error. Значение по умолчанию — 60 секунд.
Устанавливает или получает состояние resume, которое определяет, возобновляются ли неполные передачи или перезапускаются. Значение по умолчанию: false.
Количество секунд ожидания рукопожатия TLS. Можно использовать любое число, включая числа с плавающей точкой для дробных секунд. Если объект FTP не может завершить рукопожатие TLS за это время, он вызывает исключение Net::OpenTimeout. Значение по умолчанию: nil. Если ssl_handshake_timeout равно nil, используется open_timeout вместо него.
Приветственное сообщение сервера.
Публичные методы класса
# File lib/net/ftp.rb, line 151 def self.default_passive @@default_passive end
Подключения по умолчанию находятся в пассивном режиме. Значение по умолчанию: true.
# File lib/net/ftp.rb, line 145 def self.default_passive=(value) @@default_passive = value end
Подключения по умолчанию находятся в пассивном режиме. Значение по умолчанию: true.
# File lib/net/ftp.rb, line 211
def initialize(host = nil, user_or_options = {}, passwd = nil, acct = nil)
super()
begin
options = user_or_options.to_hash
rescue NoMethodError
# for backward compatibility
options = {}
options[:username] = user_or_options
options[:password] = passwd
options[:account] = acct
end
@host = nil
if options[:ssl]
unless defined?(OpenSSL::SSL)
raise "SSL extension not installed"
end
ssl_params = options[:ssl] == true ? {} : options[:ssl]
@ssl_context = SSLContext.new
@ssl_context.set_params(ssl_params)
if defined?(VerifyCallbackProc)
@ssl_context.verify_callback = VerifyCallbackProc
end
@ssl_context.session_cache_mode =
OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT |
OpenSSL::SSL::SSLContext::SESSION_CACHE_NO_INTERNAL_STORE
@ssl_context.session_new_cb = proc {|sock, sess| @ssl_session = sess }
@ssl_session = nil
if options[:private_data_connection].nil?
@private_data_connection = true
else
@private_data_connection = options[:private_data_connection]
end
else
@ssl_context = nil
if options[:private_data_connection]
raise ArgumentError,
"private_data_connection can be set to true only when ssl is enabled"
end
@private_data_connection = false
end
@binary = true
if options[:passive].nil?
@passive = @@default_passive
else
@passive = options[:passive]
end
if options[:debug_mode].nil?
@debug_mode = false
else
@debug_mode = options[:debug_mode]
end
@resume = false
@bare_sock = @sock = NullSocket.new
@logged_in = false
@open_timeout = options[:open_timeout]
@ssl_handshake_timeout = options[:ssl_handshake_timeout]
@read_timeout = options[:read_timeout] || 60
if host
connect(host, options[:port] || FTP_PORT)
if options[:username]
login(options[:username], options[:password], options[:account])
end
end
end Создаёт и возвращает новый объект FTP. Если задан параметр host, подключение устанавливается.
options — это хэш опций, каждый ключ которого — символ.
Доступные опции:
- port
-
Номер порта (значение по умолчанию 21)
- ssl
-
Если options равно true, то будет предпринята попытка использовать SSL (теперь TLS) для подключения к серверу. Для этого необходимо установить расширения
OpenSSL[OSSL] и RubyOpenSSL[RSSL]. Если options — это хэш, он передаётся вOpenSSL::SSL::SSLContext#set_paramsв качестве параметров. - private_data_connection
-
Если true, TLS используется для каналов передачи данных. По умолчанию:
trueкогда options равно true. - username
-
Имя пользователя для входа. Если options равно строке “anonymous” и options —
nil, в качестве пароля используется “anonymous@”. - password
-
Пароль для входа.
- account
-
Информация об аккаунте для ACCT.
- passive
-
Когда
true, подключение находится в пассивном режиме. Значение по умолчанию:true. -
open_timeout -
Количество секунд ожидания подключения. Подробнее см.
Net::FTP#open_timeout. Значение по умолчанию:nil. -
read_timeout -
Количество секунд ожидания чтения одного блока. Подробнее см.
Net::FTP#read_timeout. Значение по умолчанию:60. -
ssl_handshake_timeout -
Количество секунд ожидания рукопожатия TLS. Подробнее см.
Net::FTP#ssl_handshake_timeout. Значение по умолчанию:nil. -
debug_mode -
Когда
true, весь трафик к серверу и от сервера записывается в +$stdout+. Значение по умолчанию:false.
MonitorMixin::new # File lib/net/ftp.rb, line 161
def FTP.open(host, *args)
if block_given?
ftp = new(host, *args)
begin
yield ftp
ensure
ftp.close
end
else
new(host, *args)
end
end Синоним для FTP.new, но с обязательным параметром host.
Если задан блок, он передаётся объекту FTP , который будет закрыт по завершении блока или при возникновении исключения.
Общедоступные методы экземпляра
# File lib/net/ftp.rb, line 1226
def abort
line = "ABOR" + CRLF
print "put: ABOR\n" if @debug_mode
@sock.send(line, Socket::MSG_OOB)
resp = getmultiline
unless ["426", "226", "225"].include?(resp[0, 3])
raise FTPProtoError, resp
end
return resp
end Прерывает предыдущую команду (команда ABOR).
# File lib/net/ftp.rb, line 879 def acct(account) cmd = "ACCT " + account voidcmd(cmd) end
Отправляет команду ACCT.
Это менее распространённая FTP команда, для отправки информации об аккаунте, если это требуется хосту назначения.
# File lib/net/ftp.rb, line 278
def binary=(newmode)
if newmode != @binary
@binary = newmode
send_type_command if @logged_in
end
end Метод-сеттер для переключения передачи в двоичном режиме. newmode может быть либо true, либо false
# File lib/net/ftp.rb, line 1147
def chdir(dirname)
if dirname == ".."
begin
voidcmd("CDUP")
return
rescue FTPPermError => e
if e.message[0, 3] != "500"
raise e
end
end
end
cmd = "CWD #{dirname}"
voidcmd(cmd)
end Изменяет (удаленный) каталог.
# File lib/net/ftp.rb, line 1304
def close
if @sock and not @sock.closed?
begin
@sock.shutdown(Socket::SHUT_WR) rescue nil
orig, self.read_timeout = self.read_timeout, 3
@sock.read rescue nil
ensure
@sock.close
self.read_timeout = orig
end
end
end Закрывает соединение. Дальнейшие операции невозможны до тех пор, пока вы не откроете новое соединение с помощью connect.
# File lib/net/ftp.rb, line 1320 def closed? @sock == nil or @sock.closed? end
Возвращает true, если соединение закрыто.
# File lib/net/ftp.rb, line 366
def connect(host, port = FTP_PORT)
if @debug_mode
print "connect: ", host, ", ", port, "\n"
end
synchronize do
@host = host
@bare_sock = open_socket(host, port)
@sock = BufferedSocket.new(@bare_sock, read_timeout: @read_timeout)
voidresp
if @ssl_context
begin
voidcmd("AUTH TLS")
ssl_sock = start_tls_session(@bare_sock)
@sock = BufferedSSLSocket.new(ssl_sock, read_timeout: @read_timeout)
if @private_data_connection
voidcmd("PBSZ 0")
voidcmd("PROT P")
end
rescue OpenSSL::SSL::SSLError, OpenTimeout
@sock.close
raise
end
end
end
end Устанавливает соединение FTP с хостом, необязательно переопределяя порт по умолчанию. Если переменная окружения SOCKS_SERVER установлена, устанавливает соединение через прокси-сервер SOCKS. Вызывает исключение (обычно Errno::ECONNREFUSED), если соединение невозможно установить.
# File lib/net/ftp.rb, line 1133
def delete(filename)
resp = sendcmd("DELE #{filename}")
if resp.start_with?("250")
return
elsif resp.start_with?("5")
raise FTPPermError, resp
else
raise FTPReplyError, resp
end
end Удаляет файл на сервере.
# File lib/net/ftp.rb, line 808
def get(remotefile, localfile = File.basename(remotefile),
blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: data
if @binary
getbinaryfile(remotefile, localfile, blocksize, &block)
else
gettextfile(remotefile, localfile, &block)
end
end Извлекает remotefile в режиме, заданном сеансом (текст или двоичный). См. gettextfile и getbinaryfile.
# File lib/net/ftp.rb, line 747
def getbinaryfile(remotefile, localfile = File.basename(remotefile),
blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: data
f = nil
result = nil
if localfile
if @resume
rest_offset = File.size?(localfile)
f = File.open(localfile, "a")
else
rest_offset = nil
f = File.open(localfile, "w")
end
elsif !block_given?
result = String.new
end
begin
f&.binmode
retrbinary("RETR #{remotefile}", blocksize, rest_offset) do |data|
f&.write(data)
block&.(data)
result&.concat(data)
end
return result
ensure
f&.close
end
end Извлекает remotefile в двоичном режиме, сохраняя результат в localfile. Если localfile равно nil, возвращает извлеченные данные. Если задан блок, он получает извлеченные данные кусками размером blocksize.
# File lib/net/ftp.rb, line 782
def gettextfile(remotefile, localfile = File.basename(remotefile),
&block) # :yield: line
f = nil
result = nil
if localfile
f = File.open(localfile, "w")
elsif !block_given?
result = String.new
end
begin
retrlines("RETR #{remotefile}") do |line, newline|
l = newline ? line + "\n" : line
f&.print(l)
block&.(line, newline)
result&.concat(l)
end
return result
ensure
f&.close
end
end Извлекает remotefile в режиме ASCII (текст), сохраняя результат в localfile. Если localfile равно nil, возвращает извлеченные данные. Если задан блок, он получает извлеченные данные по одной строке за раз.
# File lib/net/ftp.rb, line 1268
def help(arg = nil)
cmd = "HELP"
if arg
cmd = cmd + " " + arg
end
sendcmd(cmd)
end Выполняет команду HELP.
# File lib/net/ftp.rb, line 903
def list(*args, &block) # :yield: line
cmd = "LIST"
args.each do |arg|
cmd = "#{cmd} #{arg}"
end
lines = []
retrlines(cmd) do |line|
lines << line
end
if block
lines.each(&block)
end
return lines
end Возвращает массив информации о файлах в каталоге (вывод похож на `ls -l`). Если задан блок, он итерирует по списку.
# File lib/net/ftp.rb, line 598
def login(user = "anonymous", passwd = nil, acct = nil)
if user == "anonymous" and passwd == nil
passwd = "anonymous@"
end
resp = ""
synchronize do
resp = sendcmd('USER ' + user)
if resp.start_with?("3")
raise FTPReplyError, resp if passwd.nil?
resp = sendcmd('PASS ' + passwd)
end
if resp.start_with?("3")
raise FTPReplyError, resp if acct.nil?
resp = sendcmd('ACCT ' + acct)
end
end
if !resp.start_with?("2")
raise FTPReplyError, resp
end
@welcome = resp
send_type_command
@logged_in = true
end Вход в удалённый хост. Сеанс должен быть предварительно подключен. Если user равно строке “anonymous”, и password равно nil, используется “anonymous@” в качестве пароля. Если параметр acct не равен nil, после успешного входа в систему отправляется команда FTP ACCT. При ошибке генерируется исключение (обычно Net::FTPPermError).
# File lib/net/ftp.rb, line 1258
def mdtm(filename)
resp = sendcmd("MDTM #{filename}")
if resp.start_with?("213")
return get_body(resp)
end
end Возвращает исходное время последнего изменения файла (удалённого) в формате “ГГГГММДДччммсс” (команда MDTM).
Используйте mtime, если вам нужен обработанный экземпляр Time.
# File lib/net/ftp.rb, line 1191
def mkdir(dirname)
resp = sendcmd("MKD #{dirname}")
return parse257(resp)
end Создаёт удалённый каталог.
# File lib/net/ftp.rb, line 1107
def mlsd(pathname = nil, &block) # :yield: entry
cmd = pathname ? "MLSD #{pathname}" : "MLSD"
entries = []
retrlines(cmd) do |line|
entries << parse_mlsx_entry(line)
end
if block
entries.each(&block)
end
return entries
end Возвращает массив записей указанного каталога, заданного pathname. Каждая запись содержит сведения (например, размер, время последнего изменения и т. д.) и путь. Если задан блок, он итерируется по списку. Если pathname опущено, предполагается текущий каталог.
# File lib/net/ftp.rb, line 1085
def mlst(pathname = nil)
cmd = pathname ? "MLST #{pathname}" : "MLST"
resp = sendcmd(cmd)
if !resp.start_with?("250")
raise FTPReplyError, resp
end
line = resp.lines[1]
unless line
raise FTPProtoError, resp
end
entry = line.sub(/\A(250-| *)/, "")
return parse_mlsx_entry(entry)
end Возвращает данные (например, размер, время последнего изменения, тип записи и т. д.) о файле или каталоге, заданном pathname. Если pathname опущено, предполагается текущий каталог.
# File lib/net/ftp.rb, line 1184 def mtime(filename, local = false) return TIME_PARSER.(mdtm(filename), local) end
Возвращает время последнего изменения файла (удаленного). Если local равно true, возвращается локальное время, в противном случае — время UTC.
# File lib/net/ftp.rb, line 887
def nlst(dir = nil)
cmd = "NLST"
if dir
cmd = "#{cmd} #{dir}"
end
files = []
retrlines(cmd) do |line|
files.push(line)
end
return files
end Возвращает массив имён файлов в удалённом каталоге.
# File lib/net/ftp.rb, line 1288
def noop
voidcmd("NOOP")
end Посылает команду NOOP.
Ничего не делает, кроме возвращения ответа.
# File lib/net/ftp.rb, line 864
def put(localfile, remotefile = File.basename(localfile),
blocksize = DEFAULT_BLOCKSIZE, &block)
if @binary
putbinaryfile(localfile, remotefile, blocksize, &block)
else
puttextfile(localfile, remotefile, &block)
end
end Переносит localfile на сервер в том режиме, в котором установлен сеанс (текстовый или двоичный). См. puttextfile и putbinaryfile.
# File lib/net/ftp.rb, line 822
def putbinaryfile(localfile, remotefile = File.basename(localfile),
blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: data
if @resume
begin
rest_offset = size(remotefile)
rescue Net::FTPPermError
rest_offset = nil
end
else
rest_offset = nil
end
f = File.open(localfile)
begin
f.binmode
if rest_offset
storbinary("APPE #{remotefile}", f, blocksize, rest_offset, &block)
else
storbinary("STOR #{remotefile}", f, blocksize, rest_offset, &block)
end
ensure
f.close
end
end Переносит localfile на сервер в двоичном режиме, сохраняя результат в remotefile. Если предоставлен блок, вызывает его, передавая переданные данные в blocksize чанках.
# File lib/net/ftp.rb, line 851
def puttextfile(localfile, remotefile = File.basename(localfile), &block) # :yield: line
f = File.open(localfile)
begin
storlines("STOR #{remotefile}", f, &block)
ensure
f.close
end
end Переносит localfile на сервер в ASCII (текстовом) режиме, сохраняя результат в remotefile. Если предоставлен обратный вызов или связанный блок, вызывает его, передавая переданные данные построчно.
# File lib/net/ftp.rb, line 1206
def pwd
resp = sendcmd("PWD")
return parse257(resp)
end Возвращает текущий удаленный каталог.
# File lib/net/ftp.rb, line 1279
def quit
voidcmd("QUIT")
end Завершает сеанс FTP.
# File lib/net/ftp.rb, line 128 def read_timeout=(sec) @sock.read_timeout = sec @read_timeout = sec end
Установщик для атрибута read_timeout.
# File lib/net/ftp.rb, line 1122
def rename(fromname, toname)
resp = sendcmd("RNFR #{fromname}")
if !resp.start_with?("3")
raise FTPReplyError, resp
end
voidcmd("RNTO #{toname}")
end Переименовывает файл на сервере.
# File lib/net/ftp.rb, line 629
def retrbinary(cmd, blocksize, rest_offset = nil) # :yield: data
synchronize do
with_binary(true) do
begin
conn = transfercmd(cmd, rest_offset)
loop do
data = conn.read(blocksize)
break if data == nil
yield(data)
end
conn.shutdown(Socket::SHUT_WR)
conn.read_timeout = 1
conn.read
ensure
conn.close if conn
end
voidresp
end
end
end Переводит соединение в двоичный (графический) режим, выполняет заданную команду и извлекает возвращенные данные, передавая их в связанный блок чанками по blocksize символов. Обратите внимание, что cmd — это команда сервера (например, «RETR myfile»).
# File lib/net/ftp.rb, line 656
def retrlines(cmd) # :yield: line
synchronize do
with_binary(false) do
begin
conn = transfercmd(cmd)
loop do
line = conn.gets
break if line == nil
yield(line.sub(/\r?\n\z/, ""), !line.match(/\n\z/).nil?)
end
conn.shutdown(Socket::SHUT_WR)
conn.read_timeout = 1
conn.read
ensure
conn.close if conn
end
voidresp
end
end
end Переводит соединение в ASCII (текстовый) режим, выполняет заданную команду и передает результирующие данные построчно в связанный блок. Если блок не задан, выводит строки. Обратите внимание, что cmd — это команда сервера (например, «RETR myfile»).
# File lib/net/ftp.rb, line 1199
def rmdir(dirname)
voidcmd("RMD #{dirname}")
end Удаляет удаленный каталог.
# File lib/net/ftp.rb, line 491
def sendcmd(cmd)
synchronize do
putline(cmd)
return getresp
end
end Отправляет команду и возвращает ответ.
# File lib/net/ftp.rb, line 1295 def site(arg) cmd = "SITE " + arg voidcmd(cmd) end
Выполняет команду SITE.
# File lib/net/ftp.rb, line 1170
def size(filename)
with_binary(true) do
resp = sendcmd("SIZE #{filename}")
if !resp.start_with?("213")
raise FTPReplyError, resp
end
return get_body(resp).to_i
end
end Возвращает размер заданного (удаленного) имени файла.
# File lib/net/ftp.rb, line 1242
def status(pathname = nil)
line = pathname ? "STAT #{pathname}" : "STAT"
if /[\r\n]/ =~ line
raise ArgumentError, "A line must not contain CR or LF"
end
print "put: #{line}\n" if @debug_mode
@sock.send(line + CRLF, Socket::MSG_OOB)
return getresp
end Возвращает статус (команда STAT). pathname - когда stat вызывается с pathname в качестве параметра, он работает как
list but alot faster and over the same tcp session.
# File lib/net/ftp.rb, line 683
def storbinary(cmd, file, blocksize, rest_offset = nil) # :yield: data
if rest_offset
file.seek(rest_offset, IO::SEEK_SET)
end
synchronize do
with_binary(true) do
conn = transfercmd(cmd)
loop do
buf = file.read(blocksize)
break if buf == nil
conn.write(buf)
yield(buf) if block_given?
end
conn.close
voidresp
end
end
rescue Errno::EPIPE
# EPIPE, in this case, means that the data connection was unexpectedly
# terminated. Rather than just raising EPIPE to the caller, check the
# response on the control connection. If getresp doesn't raise a more
# appropriate exception, re-raise the original exception.
getresp
raise
end Переводит соединение в двоичный (графический) режим, выполняет заданную команду на стороне сервера (например, «STOR myfile») и отправляет содержимое файла с именем file на сервер. Если предоставлен необязательный блок, он также передает ему данные чанками по blocksize символов.
# File lib/net/ftp.rb, line 715
def storlines(cmd, file) # :yield: line
synchronize do
with_binary(false) do
conn = transfercmd(cmd)
loop do
buf = file.gets
break if buf == nil
if buf[-2, 2] != CRLF
buf = buf.chomp + CRLF
end
conn.write(buf)
yield(buf) if block_given?
end
conn.close
voidresp
end
end
rescue Errno::EPIPE
# EPIPE, in this case, means that the data connection was unexpectedly
# terminated. Rather than just raising EPIPE to the caller, check the
# response on the control connection. If getresp doesn't raise a more
# appropriate exception, re-raise the original exception.
getresp
raise
end Переводит соединение в ASCII (текстовый) режим, выполняет заданную команду на стороне сервера (например, «STOR myfile») и отправляет содержимое файла с именем file на сервер построчно. Если предоставлен необязательный блок, он также передает ему строки.
# File lib/net/ftp.rb, line 1215
def system
resp = sendcmd("SYST")
if !resp.start_with?("215")
raise FTPReplyError, resp
end
return get_body(resp)
end Возвращает информацию о системе.
# File lib/net/ftp.rb, line 501
def voidcmd(cmd)
synchronize do
putline(cmd)
voidresp
end
end Отправляет команду и ожидает ответа, начинающегося с '2'.
Приватные методы экземпляра
# File lib/net/ftp.rb, line 1065
def parse_mlsx_entry(entry)
facts, pathname = entry.chomp.split(/ /, 2)
unless pathname
raise FTPProtoError, entry
end
return MLSxEntry.new(
facts.scan(/(.*?)=(.*?);/).each_with_object({}) {
|(factname, value), h|
name = factname.downcase
h[name] = FACT_PARSERS[name].(value)
},
pathname)
end # File lib/net/ftp.rb, line 1358
def parse_pasv_ipv4_host(s)
return s.tr(",", ".")
end # File lib/net/ftp.rb, line 1363
def parse_pasv_ipv6_host(s)
return s.split(/,/).map { |i|
"%02x" % i.to_i
}.each_slice(2).map(&:join).join(":")
end # File lib/net/ftp.rb, line 1370
def parse_pasv_port(s)
return s.split(/,/).map(&:to_i).inject { |x, y|
(x << 8) + y
}
end # File lib/net/ftp.rb, line 343
def start_tls_session(sock)
ssl_sock = SSLSocket.new(sock, @ssl_context)
ssl_sock.sync_close = true
ssl_sock.hostname = @host if ssl_sock.respond_to? :hostname=
if @ssl_session &&
Process.clock_gettime(Process::CLOCK_REALTIME) < @ssl_session.time.to_f + @ssl_session.timeout
# ProFTPD returns 425 for data connections if session is not reused.
ssl_sock.session = @ssl_session
end
ssl_socket_connect(ssl_sock, @ssl_handshake_timeout || @open_timeout)
if @ssl_context.verify_mode != VERIFY_NONE
ssl_sock.post_connection_check(@host)
end
return ssl_sock
end
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.