Ruby FTP从文件夹中分离文件

时间:2012-03-04 09:59:14

标签: ruby ftp

我正在尝试抓取FTP并递归下拉所有文件。

到目前为止,我试图用

下拉目录
   ftp.list.each do |entry|
    if entry.split(/\s+/)[0][0, 1] == "d"
      out[:dirs] << entry.split.last unless black_dirs.include? entry.split.last
    else
      out[:files] << entry.split.last unless black_files.include? entry.split.last
    end

但事实证明,如果将列表拆分到最后一个空格,则带有空格的文件名和目录将被取错。 在这里需要一点逻辑帮助。

6 个答案:

答案 0 :(得分:4)

如果您一次列出所有文件,则可以避免递归

files = ftp.nlst('**/*.*')

目录不包含在列表中,但完整的ftp路径仍然可以在名称中使用。

修改

我假设每个文件名都包含一个点,目录名称不包含在内。谢谢你提到@Niklas B.

答案 1 :(得分:3)

周围有各种各样的FTP服务器。

我们的客户使用一些不起眼的专有的基于Windows的服务器,并且它们返回的文件列表与Linux版本完全不同。

所以我最终做的是每个文件/目录条目我尝试将目录更改为它,如果这不起作用 - 将其视为文件:)

以下方法是“防弹”:

# Checks if the give file_name is actually a file.
def is_ftp_file?(ftp, file_name)
  ftp.chdir(file_name)
  ftp.chdir('..')
  false
rescue
  true
end

file_names = ftp.nlst.select {|fname| is_ftp_file?(ftp, fname)}

像魅力一样工作,但请注意:如果FTP目录中包含大量文件 - 此方法需要一段时间来遍历所有文件。

答案 2 :(得分:2)

您还可以使用正则表达式。我把它放在一起。请验证它是否适合您,以及我不知道您的目录列表看起来不同。你必须使用Ruby 1.9 btw。

reg = /^(?<type>.{1})(?<mode>\S+)\s+(?<number>\d+)\s+(?<owner>\S+)\s+(?<group>\S+)\s+(?<size>\d+)\s+(?<mod_time>.{12})\s+(?<path>.+)$/

match = entry.match(reg)

您可以按名称访问元素

match[:type]如果是目录则包含'd',如果是文件,则包含空格。

所有其他元素也在那里。最重要的是match[:path]

答案 3 :(得分:2)

假设FTP服务器返回类Unix 文件列表,则以下代码有效。至少对我而言。

regex = /^d[r|w|x|-]+\s+[0-9]\s+\S+\s+\S+\s+\d+\s+\w+\s+\d+\s+[\d|:]+\s(.+)/
ftp.ls.each do |line|
    if dir = line.match(regex)
        puts dir[1]
    end
end

dir[1]包含目录的名称(假设被检查的行实际上代表一个目录)。

答案 4 :(得分:0)

正如@Alex指出的那样,在文件名中使用模式并不可靠。目录可以在其名称中包含点(例如.ssh),并且列表在不同的服务器上可以非常不同。

他的方法有效,但正如他自己指出的那样,需要太长时间。 我更喜欢使用Net :: FTP中的.size方法。 它返回文件的大小,如果文件是目录则抛出错误。

def item_is_file? (item)
    ftp = Net::FTP.new(host, username, password)
    begin 
    if ftp.size(item).is_a? Numeric
        true
    end
    rescue Net::FTPPermError
        return false
    end
end

答案 5 :(得分:0)

我会将我的解决方案添加到组合中...

使用 ftp.nlst('**/*.*') 对我不起作用...服务器似乎不支持 ** 语法。

带有 rescue 的 chdir 技巧看起来很昂贵而且很黑。

假设所有文件至少有一个字符、一个句点和一个扩展名,我做了一个简单的递归。

  def list_all_files(ftp, folder)
    entries = ftp.nlst(folder)
    file_regex = /.+\.{1}.*/
    files = entries.select{|e| e.match(file_regex)}
    subfolders = entries.reject{|e| e.match(file_regex)}
    subfolders.each do |subfolder|
      files += list_all_files(ftp, subfolder)
    end
    files
  end

nlst 似乎以非递归方式返回其找到的任何内容的完整路径...因此每次获得列表时,将文件与文件夹分开,然后处理任何您递归找到的文件夹。收集所有文件结果。

要调用,可以传入一个起始文件夹

files = list_all_files(ftp, "my_starting_folder/my_sub_folder")
files = list_all_files(ftp, ".")
files = list_all_files(ftp, "")
files = list_all_files(ftp, nil)