ftplib如何检查一个文件是否是文件夹?

20

我该如何使用ftplib检查远程ftp上的文件是否是文件夹?

目前最好的方法是进行nlst操作,并循环调用每个文件的size函数,如果文件出错,则它就是一个文件夹?

有没有更好的方法?我不能解析list的输出,因为有大约十几个不同的ftp服务器(许多非常老旧)。

我应该怎么办?

16个回答

10

在ftplib中没有"isdir"和"isfile"的定义。如果您不必使用ftplib,我建议您使用ftputil

首先,您需要安装ftputil包。为此,请使用以下命令:python -m pip install ftputil。安装完成后,您可以将该库导入到您的代码中。我认为这已经足够解释了。那么,让我们进入实现:

import ftputil
with ftputil.FTPHost("host", "username", "password") as ftp_host:
    ftp_host.chdir("/directory/")   
    list = ftp_host.listdir(ftp_host.curdir)
    for fname in list:
        if ftp_host.path.isdir(fname):
            print(fname + " is a directory")
        else:
            print(fname + " is not a directory")

7

FTP是一种相当基础的协议,没有内置协议查询可以让您获取每个节点的类型(文件、目录),因此像您找到的这样的启发式方法是唯一的解决方案。

如果获取每个节点的大小不起作用,也许您应该考虑在这些节点上调用FTP.nlst():那些出错的将是文件而不是目录。


可能是因为服务器的原因,但当我尝试使用nlst()时,它不会在文件上出错。当调用文件时,它返回一个长度为1的列表,其中包含文件名,因此我可以查看它来确定它是文件还是目录。 - dgundersen

6
你可以使用MLST命令:
import ftplib
f = ftplib.FTP()
f.connect("localhost")
f.login()
print f.sendcmd('MLST /')

对于pyftpdlib服务器,上面的代码将打印出:

250-Listing "/":
 modify=20111212124834;perm=el;size=12288;type=dir;unique=807g100001; /
250 End MLST.

您需要做的是解析该字符串并通过正则表达式查找 "type=dir"、"type=cdir"(当前目录,即".")或"type=pdir"(父目录,即"..")。 如果匹配成功,则表示提供的路径是一个目录。

谢谢,这是正确的答案。rfc3659指定了响应的确切格式,因此可以可靠地进行机器解析。 - hasufell

4

FTP.dir 返回一个目录列表,你可以使用回调函数解析它以确定它是否为目录。例如像这样:

def parse(line):
    if line[0] == 'd':
        print(line.rpartition(' ')[2])   # gives you the name of a directory

ftp.dir(parse)

2
但输出不标准,所以它是无用的。 - UberJumper
好的,那就用另一个函数来解析它。毕竟响应类型不会有那么多。 - SilentGhost
1
目前考虑到有57个不同的远程ftp服务器,其中一些运行Linux/Windows/HPUX/Solaris/BSD ftp客户端,再加上各种第三方ftp客户端,这真是一件麻烦事。 - UberJumper
你为什么认为会有很多响应格式?只要有 drwxrwxrwx 的形式,你就可以拆分字符串并使用正则表达式或其他技术来找出包含权限的字段。 - SilentGhost

4
def is_file(filename):
    return ftp.size(filename) is not None

这能够起作用是因为如果ftp.size返回None,那么它就是一个目录。

问题是我刚刚注意到其中一个FTP服务器实际上返回了文件夹的文件大小。这不应该发生,但它确实发生了。糟糕。 - UberJumper
1
@uberjumper,我还有一个想法要尝试,很快就会发布。真的很惊讶它返回了文件夹大小。@SilentGhost,我在2.6中完成了它,但还是谢谢你的提醒。如果我与使用3.x的人分享它,我会给他们一个修改过的版本。 - Evan Fosmark
是的,我知道,它还返回MDTM日期。我见过另外一两个FTP服务器会显示文件夹大小,但不会显示文件修改日期。我迫不及待地等待您的解决方案。 - UberJumper

2
def is_file(filename):
    current = ftp.pwd()
    try:
        ftp.cwd(filename)
    except:
        ftp.cwd(current)
        return True
    ftp.cwd(current)
    return False

这里有另一个解决方案。在写了一半时我意识到它存在一个问题。如果你没有更改文件夹的权限,它会将其读取为文件。只要你有访问任何文件夹的权限,它就可以正常工作。
我仍然发布它,因为也许它会给一些想法。

我通过检查错误信息是否包含“550 Access is Denied”来解决了这个问题。这对于我连接的服务器有效,但我怀疑不是所有服务器都会以这种方式响应。也许可以使用已知的550错误消息字典?或者只需检查它是否说“550”? - Nick

1

我在处理ftplib时使用过这个:

import ftplib
from ftplib import FTP
ftp=FTP("ftp address")
ftp.login("user","password")

path = "/is/this/a/file/or/a/folder/"

try:
    ftp.cwd(path)
    print "it's a folder!"
except ftplib.error_perm:
    print "it's not a folder!"

2
就像@Evan Fosmark所说,如果用户没有足够的权限来cwd给定文件夹,那么这段代码将把该文件夹识别为文件。 - jameh

1
这是我用来查找给定目录中目录的方法。 我将目录名称作为集合返回,因为在程序后面我使用了集合,但您可以取消最后的集合创建并将目录名称作为列表、元组等返回。
def get_directories(ftp_server):
    """
    Returns a set of directories in the current directory on the FTP server

    Stdout output of ftp_server.dir() is redirected to an IO object and then
    reset, because ftp_server.dir() only prints its results to stdout.

    @param ftp_server: Open connection to FTP server
    @return: Set of directory names
    """
    # Redirect stdout
    old_stdout = sys.stdout
    sys.stdout = new_stdout = StringIO()
    # Get directory listing
    ftp_server.dir()
    # Reset stdout
    sys.stdout = old_stdout

    directory_listing = new_stdout.getvalue().split("\n")
    # Directories are listed starting with "d" for "directory"
    only_directories = (x for x in directory_listing if x.startswith("d"))
    # This only deals with directory names without spaces.
    directory_names = set(dirname.split()[-1] for dirname in only_directories)
    return directory_names

0

我找到了一个解决方案,但可能不是最好的,我认为它对你可能会有帮助。

> def isfile(remote_name):
>     try:
>         ftp.sendcmd('MDTM ' + remote_name)
>     except:
>         return False
>     else:
>         return True

如果'remote_name'是一个普通文件,此函数将返回TRUE,否则返回False。


0
ftp_conn = FTP(host=host_name, user=user_name, passwd=password, acct='', timeout=None, source_address=None)
ftp_conn.cwd(path_ftp)
for data_dir in ftp_conn.mlsd():
    print(data_dir)

输出:

('3', {'modify': '20170719134928', 'perm': 'flcdmpe', 'type': 'dir', 'unique': '804U485826E', 'unix.group': '10000', 'unix.mode': '0755', 'unix.owner': '10754'})

see 'type': 'dir'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接