Ruby 2.4

Net::FTP

Net::FTP类

Parent:ProtocolIncluded modules:MonitorMixin, OpenSSL, OpenSSL::SSL

这个类实现了文件传输协议。如果您使用了命令行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

主要方法

以下是最有可能对用户有用的方法:

  • ::open

  • getbinaryfile

  • gettextfile

  • putbinaryfile

  • puttextfile

  • chdir

  • nlst

  • size

  • rename

  • delete

常量

CASE_DEPENDENT_PARSER CASE_INDEPENDENT_PARSER DECIMAL_PARSER FACT_PARSERS OCTAL_PARSER TIME_PARSER

属性

binaryR

当为真时,传输以二进制模式执行。 默认值:true。

debug_modeRW

如果为true,所有来往于服务器的流量都将写入+ $ stdout +。 默认值:false。

last_responseR

服务器的最后一个响应。

last_response_codeR

服务器的最后一个响应代码。

lastrespR

服务器的最后一个响应代码。

open_timeoutRW

等待连接打开的秒数。可以使用任何数字,包括小数秒的浮点数。如果FTP对象在几秒钟内无法打开连接,则会引发Net :: OpenTimeout异常。默认值是nil

passiveRW

当为true时连接处于被动模式。默认:true

read_timeoutR

等待一个数据块读取的秒数(通过一次读取(2)调用)。可以使用任何数字,包括小数秒的浮点数。如果FTP对象在这几秒钟内无法读取数据,则会引发Timeout :: Error异常。默认值是60秒。

resumeRW

设置或检索恢复状态,该状态决定是否恢复或重新启动不完整的传输。 默认值:false。

ssl_handshake_timeoutRW

等待TLS握手的秒数。 可以使用任何数字,包括小数秒的浮点数。 如果FTP对象在这几秒钟内无法完成TLS握手,则会引发Net :: OpenTimeout异常。 默认值是零。 如果ssl_handshake_timeout为零,则使用open_timeout。

welcomeR

服务器的欢迎消息。

公共类方法

default_passive() Show source

如果为true,则默认情况下连接处于被动模式。 默认值:true。

# File lib/net/ftp.rb, line 150 def self.default_passive @@default_passive end

default_passive=(value) Show source

如果为true,则默认情况下连接处于被动模式。 默认值:true。

# File lib/net/ftp.rb, line 144 def self.default_passive=(value) @@default_passive = value end

Net::FTP.new(host = nil, options = {}) Show source

创建并返回一个新的FTP对象。 如果给出主机,则建立连接。

选项是一个选项散列,其中的每个键都是一个符号。

可用的选项是:

端口

端口号(默认值是21)

ssl

如果选项为真,那么将尝试使用SSL(现在称为TLS)连接到服务器。为此,需要安装OpenSSL OSSL和Ruby OpenSSL RSSL扩展。如果options是一个散列,它将作为参数传递给OpenSSL :: SSL :: SSLContext#set_params。

private_data_connection

如果为true,则TLS用于数据连接。默认值:true选项为true时。

username

用户名登录。如果选项是字符串“anonymous”并且选项是nil“anonymous @”,则用作密码。

password

登录密码。

account

ACCT的帐户信息。

passive

如果为true,则连接处于被动模式。 默认值:true。

#open_timeout

等待连接打开的秒数。 有关详细信息,请参阅#open_timeout。 默认:无。

#read_timeout

等待读取一个数据块的秒数。详情请参阅#read_timeout。默认:60

#ssl_handshake_timeout

等待TLS握手的秒数。有关详细信息,请参阅#ssl_handshake_timeout。默认:nil

#debug_mode

如果为true,所有来往于服务器的流量都将写入+ $ stdout +。 默认值:false。

调用超类方法MonitorMixin.new

# File lib/net/ftp.rb, line 210 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_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 if options[:port] connect(host, options[:port] || FTP_PORT) else # spec/rubyspec/library/net/ftp/initialize_spec.rb depends on # the number of arguments passed to connect.... connect(host) end if options[:username] login(options[:username], options[:password], options[:account]) end end end

open(host, *args) { |ftp| ... } Show source

FTP.new的同义词,但带有必需的主机参数。

如果给出了一个块,它将传递FTP对象,当块结束时或者引发异常时,它将被关闭。

# File lib/net/ftp.rb, line 160 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

公共实例方法

abort() Show source

中止前一个命令(ABOR命令)。

# File lib/net/ftp.rb, line 1228 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

acct(account) Show source

发送ACCT命令。

这是一种不常见的FTP命令,用于在目标主机需要时发送帐户信息。

# File lib/net/ftp.rb, line 881 def acct(account) cmd = "ACCT " + account voidcmd(cmd) end

binary=(newmode) Show source

二进制模式切换传输的setter。 newmode是真或假

# File lib/net/ftp.rb, line 279 def binary=(newmode) if newmode != @binary @binary = newmode send_type_command if @logged_in end end

chdir(dirname) Show source

更改(远程)目录。

# File lib/net/ftp.rb, line 1149 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

close() Show source

关闭连接。直到您打开与连接的新连接才能进行其他操作。

# File lib/net/ftp.rb, line 1306 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

closed?() Show source

如果连接关闭,则返回true

# File lib/net/ftp.rb, line 1322 def closed? @sock == nil or @sock.closed? end

connect(host, port = FTP_PORT) Show source

建立到主机的FTP连接,可选地覆盖默认端口。 如果设置了环境变量SOCKS_SERVER,则通过SOCKS代理设置连接。 如果无法建立连接,则引发异常(通常为Errno :: ECONNREFUSED)。

# File lib/net/ftp.rb, line 368 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

delete(filename) Show source

删除服务器上的文件。

# File lib/net/ftp.rb, line 1135 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

dir(*args)

别名:list

get(remotefile, localfile = File.basename(remotefile), blocksize = DEFAULT_BLOCKSIZE) { |data| ... } Show source

以会话设置的任何模式(文本或二进制)检索远程文件。 请参阅gettextfile和getbinaryfile。

# File lib/net/ftp.rb, line 810 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

getbinaryfile(remotefile, localfile = File.basename(remotefile), blocksize = DEFAULT_BLOCKSIZE) { |data| ... } Show source

以二进制模式检索远程文件,将结果存储在本地文件中。 如果localfile为零,则返回检索到的数据。 如果提供了一个块,它将以块大小的形式传递检索到的数据。

# File lib/net/ftp.rb, line 749 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 = open(localfile, "a") else rest_offset = nil f = 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

getdir()

别名为:pwd

gettextfile(remotefile, localfile = File.basename(remotefile)) { |line| ... } Show source

以ASCII(文本)模式检索远程文件,将结果存储在本地文件中。 如果localfile为零,则返回检索到的数据。 如果提供了一个块,则一次将检索到的数据传递给一行。

# File lib/net/ftp.rb, line 784 def gettextfile(remotefile, localfile = File.basename(remotefile), &block) # :yield: line f = nil result = nil if localfile f = 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

help(arg = nil) Show source

发出HELP命令。

# File lib/net/ftp.rb, line 1270 def help(arg = nil) cmd = "HELP" if arg cmd = cmd + " " + arg end sendcmd(cmd) end

list(*args) { |line| ... } Show source

返回目录中的文件信息数组(输出如ls -l)。 如果给出了一个块,它会遍历列表。

# File lib/net/ftp.rb, line 905 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,dir

login(user = "anonymous", passwd = nil, acct = nil) Show source

登录到远程主机。 会话必须先前已连接。 如果用户是字符串“匿名”,密码为零,则使用“anonymous @”作为密码。 如果acct参数不为零,则在成功登录后发送FTP ACCT命令。 引发错误时发生异常(通常为Net :: FTPPermError)。

# File lib/net/ftp.rb, line 600 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

ls(*args)

别名:list

mdtm(filename) Show source

以“YYYYMMDDhhmmss”格式(MDTM命令)返回(远程)文件的原始上次修改时间。

如果您想要解析的时间实例,请使用mtime。

# File lib/net/ftp.rb, line 1260 def mdtm(filename) resp = sendcmd("MDTM #{filename}") if resp.start_with?("213") return get_body(resp) end end

mkdir(dirname) Show source

创建一个远程目录。

# File lib/net/ftp.rb, line 1193 def mkdir(dirname) resp = sendcmd("MKD #{dirname}") return parse257(resp) end

mlsd(pathname = nil) { |entry| ... } Show source

返回由pathname指定的目录条目数组。 每个条目都有事实(例如,大小,上次修改时间等)和路径名。 如果给出了一个块,它会遍历列表。 如果省略了路径名,则假定当前目录。

# File lib/net/ftp.rb, line 1109 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

mlst(pathname = nil) Show source

返回有关由路径名指定的文件或目录的数据(例如大小,上次修改时间,条目类型等)。 如果省略了路径名,则假定当前目录。

# File lib/net/ftp.rb, line 1087 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

mtime(filename, local = false) Show source

返回(远程)文件的最后修改时间。 如果local为true,则返回当地时间,否则为UTC时间。

# File lib/net/ftp.rb, line 1186 def mtime(filename, local = false) return TIME_PARSER.(mdtm(filename), local) end

nlst(dir = nil) Show source

返回远程目录中的文件名数组。

# File lib/net/ftp.rb, line 889 def nlst(dir = nil) cmd = "NLST" if dir cmd = "#{cmd} #{dir}" end files = [] retrlines(cmd) do |line| files.push(line) end return files end

noop() Show source

发出NOOP命令。

除了返回回复外,什么也不做。

# File lib/net/ftp.rb, line 1290 def noop voidcmd("NOOP") end

put(localfile, remotefile = File.basename(localfile), blocksize = DEFAULT_BLOCKSIZE, &block) Show source

以任何模式设置会话(文本或二进制)将localfile传输到服务器。 请参阅puttextfile和putbinaryfile。

# File lib/net/ftp.rb, line 866 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

putbinaryfile(localfile, remotefile = File.basename(localfile), blocksize = DEFAULT_BLOCKSIZE) { |data| ... } Show source

以二进制模式将localfile传输到服务器,将结果存储在远程文件中。 如果提供了一个块,则调用它,以块大小传入传输的数据。

# File lib/net/ftp.rb, line 824 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 = 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

puttextfile(localfile, remotefile = File.basename(localfile)) { |line| ... } Show source

以ASCII(文本)模式将localfile传输到服务器,并将结果存储在远程文件中。 如果提供了回叫或相关的块,则调用它,一次传送一行传输的数据。

# File lib/net/ftp.rb, line 853 def puttextfile(localfile, remotefile = File.basename(localfile), &block) # :yield: line f = open(localfile) begin storlines("STOR #{remotefile}", f, &block) ensure f.close end end

pwd() Show source

返回当前的远程目录。

# File lib/net/ftp.rb, line 1208 def pwd resp = sendcmd("PWD") return parse257(resp) end

也可以是:getdir

quit() Show source

退出FTP会话。

# File lib/net/ftp.rb, line 1281 def quit voidcmd("QUIT") end

read_timeout=(sec) Show source

#read_timeout属性的setter。

# File lib/net/ftp.rb, line 127 def read_timeout=(sec) @sock.read_timeout = sec @read_timeout = sec end

rename(fromname, toname) Show source

重命名服务器上的文件。

# File lib/net/ftp.rb, line 1124 def rename(fromname, toname) resp = sendcmd("RNFR #{fromname}") if !resp.start_with?("3") raise FTPReplyError, resp end voidcmd("RNTO #{toname}") end

retrbinary(cmd, blocksize, rest_offset = nil) { |data| ... } Show source

将连接设置为二进制(图像)模式,发出给定的命令,并获取返回的数据,并以块大小字符块的形式将其传递给关联的块。 请注意,cmd是一个服务器命令(如“RETR myfile”)。

# File lib/net/ftp.rb, line 631 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

retrlines(cmd) { |line| ... } Show source

将连接设置为ASCII(文本)模式,发出给定的命令,并将结果数据一次一行地传递给关联的块。 如果没有给出块,则打印行。 请注意,cmd是一个服务器命令(如“RETR myfile”)。

# File lib/net/ftp.rb, line 658 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

rmdir(dirname) Show source

删除远程目录。

# File lib/net/ftp.rb, line 1201 def rmdir(dirname) voidcmd("RMD #{dirname}") end

sendcmd(cmd) Show source

发送一个命令并返回响应。

# File lib/net/ftp.rb, line 493 def sendcmd(cmd) synchronize do putline(cmd) return getresp end end

set_socket(sock, get_greeting = true) Show source

设置用于连接到FTP服务器的套接字。

如果get_greeting为false,可能会引发FTPReplyError。

# File lib/net/ftp.rb, line 398 def set_socket(sock, get_greeting = true) synchronize do @sock = sock if get_greeting voidresp end end end

site(arg) Show source

发出站点命令。

# File lib/net/ftp.rb, line 1297 def site(arg) cmd = "SITE " + arg voidcmd(cmd) end

size(filename) Show source

返回给定(远程)文件名的大小。

# File lib/net/ftp.rb, line 1172 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

status(pathname = nil) Show source

返回状态(STAT命令)。路径名 - 当stat以路径名作为参数调用时,它的行为就像:

list but alot faster and over the same tcp session.

# File lib/net/ftp.rb, line 1244 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

storbinary(cmd, file, blocksize, rest_offset = nil) { |data| ... } Show source

将连接设置为二进制(映像)模式,发出给定的服务器端命令(如“STOR myfile”),并将文件名为file的内容发送到服务器。 如果给出了可选块,则它还会以块块字符块的形式将数据传递给它。

# File lib/net/ftp.rb, line 685 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

storlines(cmd, file) { |line| ... } Show source

将连接设置为ASCII(文本)模式,发出给定的服务器端命令(如“STOR myfile”),并将文件名为file的内容一次发送到服务器。 如果给出可选块,它也会传递这些行。

# File lib/net/ftp.rb, line 717 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

system() Show source

返回系统信息。

# File lib/net/ftp.rb, line 1217 def system resp = sendcmd("SYST") if !resp.start_with?("215") raise FTPReplyError, resp end return get_body(resp) end

voidcmd(cmd) Show source

发送命令并期望以'2'开始的响应。

# File lib/net/ftp.rb, line 503 def voidcmd(cmd) synchronize do putline(cmd) voidresp end end

私有实例方法

parse_mlsx_entry(entry) Show source

# File lib/net/ftp.rb, line 1067 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

parse_pasv_ipv4_host(s) Show source

# File lib/net/ftp.rb, line 1360 def parse_pasv_ipv4_host(s) return s.tr(",", ".") end

parse_pasv_ipv6_host(s) Show source

# File lib/net/ftp.rb, line 1365 def parse_pasv_ipv6_host(s) return s.split(/,/).map { |i| "%02x" % i.to_i }.each_slice(2).map(&:join).join(":") end

parse_pasv_port(s) Show source

# File lib/net/ftp.rb, line 1372 def parse_pasv_port(s) return s.split(/,/).map(&:to_i).inject { |x, y| (x << 8) + y } end

start_tls_session(sock) Show source

# File lib/net/ftp.rb, line 344 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 @ssl_session = ssl_sock.session return ssl_sock end