如何使用Python列出FTP服务器中的所有文件和目录?

2021年11月16日19:47:19 发表评论 1,559 次浏览

使用内置的 ftplib 模块在 Python 中使用 LIST、NLST 和 MLSD FTP 命令列出 FTP 服务器中的所有文件和目录,包含一些Python列出FTP文件和目录示例

Python如何列出FTP文件和目录FTPFile Transfer Protocol)是标准的网络协议,它使计算机能够传送文件通过网络。在本教程中,你将学习如何连接到 FTP 服务器并列出其上的所有文件和目录,你还将熟悉 Python 的内置ftplib模块及其一些方法。

Python列出FTP服务器所有文件和目录实例介绍:ftplib预装了 Python,所以如果你的机器上安装了 Python,你就可以开始了。打开一个新的 Python 文件并继续,让我们导入教程所需的模块:

import ftplib
import os
from datetime import datetime

在常规 FTP 服务器中,你需要凭据(用户名和密码)才能正确登录,但在本教程中,我们将使用爱丁堡大学的 FTP 服务器,它使用户能够匿名登录:

FTP_HOST = "ftp.ed.ac.uk"
FTP_USER = "anonymous"
FTP_PASS = ""

以下实用函数将帮助我们稍后打印文件和目录列表:

# some utility functions that we gonna need
def get_size_format(n, suffix="B"):
    # converts bytes to scaled format (e.g KB, MB, etc.)
    for unit in ["", "K", "M", "G", "T", "P"]:
        if n < 1024:
            return f"{n:.2f}{unit}{suffix}"
        n /= 1024

def get_datetime_format(date_time):
    # convert to datetime object
    date_time = datetime.strptime(date_time, "%Y%m%d%H%M%S")
    # convert to human readable date time string
    return date_time.strftime("%Y/%m/%d %H:%M:%S")

get_size_format()函数是从本教程中抓取的,它基本上将文件的大小从字节转换为更易读的格式,例如1.3MB、103.5KB等。该get_datetime_format()函数还将日期时间转换为更易读的格式。

现在,让我们使用FTP()客户端类连接到我们的服务器:

# initialize FTP session
ftp = ftplib.FTP(FTP_HOST, FTP_USER, FTP_PASS)

在编写本教程的代码时,我在处理非拉丁字符时遇到了一些问题,因为 Python 使用ISO-8859-1作为默认编码,因此,让我们将编码更改为UTF-8

# force UTF-8 encoding
ftp.encoding = "utf-8"

Python列出FTP文件和目录示例 - 现在我们在服务器内部,让我们打印连接后服务器发送的欢迎消息:

# print the welcome message
print(ftp.getwelcome())

这是此服务器的输出:

         Welcome to the University of Edinburgh Anonymous FTP server        
 ===========================================================================

  When requested for a username enter 'ftp' or 'anonymous'.  If you have    
  problems, try using a dash (-) as the first character of your password.   
  If you still have problems or wish to make a comment then send email to   
  ftpmaster@ed.ac.uk.

  All transfers are logged.
220 FTP Server

让我们开始调用一些命令,我​​们要使用的第一个方法是cwd(),它更改当前工作目录,因为我们在根目录中,让我们更改到某个包含一些文件的目录:

# change the current working directory to 'pub' folder and 'maps' subfolder
ftp.cwd("pub/maps")

列出文件和目录:

# LIST a directory
print("*"*50, "LIST", "*"*50)
ftp.dir()

这是输出的一部分:

************************************************** LIST **************************************************
-rw-r--r--   1 1407     bin         25175 Jul  8  1991 JIPS-map.910704-1.ps.Z
-rw-r--r--   1 1407     bin         25714 Jul 30  1991 JIPS-map.910730-1.ps.Z
-rw-r--r--   1 1407     bin         25980 Aug  2  1991 JIPS-map.910802-1.ps.Z
-rw-r--r--   1 1407     bin         26812 Aug  7  1991 JIPS-map.910806-1.ps.Z
-rw-r--r--   1 1407     bin         26673 Oct 11  1991 JIPS-map.911011-1.ps.Z
...<SNIPPED>...

Python如何列出FTP文件和目录?与ls命令提供的输出非常相似。但是,这使用了LIST现在已经过时的 FTP命令。此外,你可能已经注意到,它不返回任何值,它只是将当前工作目录中的目录和文件打印到屏幕上。

另一种选择是使用NLST命令:

# NLST command
print("*"*50, "NLST", "*"*50)
print("{:20} {}".format("File Name", "File Size"))
for file_name in ftp.nlst():
    file_size = "N/A"
    try:
        ftp.cwd(file_name)
    except Exception as e:
        ftp.voidcmd("TYPE I")
        file_size = get_size_format(ftp.size(file_name))
    print(f"{file_name:20} {file_size}")

输出:

************************************************** NLST **************************************************
File Name            File Size
backbone.t3-ps.Z     23.39KB
backbone.t1t3-ps.Z   24.56KB
ripe-map06-netnums.ps.Z 29.54KB
edlana4bw.ps.Z       63.34KB
...<SNIPPED>...

但是,正如你所看到的,该NLST命令仅返回文件和目录的名称,没有别的,我们想要一些可以为我们提供名称列表及其元数据的东西,例如权限、大小、上次修改日期等。

Python列出FTP文件和目录示例 - 在这里,我们使用MLSD进入救援的命令:

print("*"*50, "MLSD", "*"*50)
# using the MLSD command
print("{:30} {:19} {:6} {:5} {:4} {:4} {:4} {}".format("File Name", "Last Modified", "Size",
                                                    "Perm","Type", "GRP", "MODE", "OWNER"))
for file_data in ftp.mlsd():
    # extract returning data
    file_name, meta = file_data
    # i.e directory, file or link, etc
    file_type = meta.get("type")
    if file_type == "file":
        # if it is a file, change type of transfer data to IMAGE/binary
        ftp.voidcmd("TYPE I")
        # get the file size in bytes
        file_size = ftp.size(file_name)
        # convert it to human readable format (i.e in 'KB', 'MB', etc)
        file_size = get_size_format(file_size)
    else:
        # not a file, may be a directory or other types
        file_size = "N/A"
    # date of last modification of the file
    last_modified = get_datetime_format(meta.get("modify"))
    # file permissions
    permission = meta.get("perm")
    
    # get the file unique id
    unique_id = meta.get("unique")
    # user group
    unix_group = meta.get("unix.group")
    # file mode, unix permissions 
    unix_mode = meta.get("unix.mode")
    # owner of the file
    unix_owner = meta.get("unix.owner")
    # print all
    print(f"{file_name:30} {last_modified} {file_size:7} {permission:5} {file_type:4} {unix_group:4} {unix_mode:4} {unix_owner}")

我们使用调用 FTP命令的mlsd()方法MLSD,它返回一个包含文件名和文件元数据的元组,我们提取所有内容并将它们打印到屏幕上。请注意,我使用TYPE I命令将传输类型更改为二进制图像,这是因为如果不是这种情况,size()将引发异常。

Python如何列出FTP文件和目录?我们还使用我们之前定义的函数get_datetime_format()将 FTP 服务器返回的日期转换为更易读的格式,这里是上述配方的截断输出:

************************************************** MLSD **************************************************
File Name                      Last Modified       Size   Perm  Type GRP  MODE OWNER
backbone.t3-ps.Z               1991/07/30 11:28:13 23.39KB adfr  file 1    0644 1407
backbone.t1t3-ps.Z             1991/07/30 11:28:41 24.56KB adfr  file 1    0644 1407
ripe-map06-netnums.ps.Z        1991/07/08 09:57:23 29.54KB adfr  file 1    0644 1407
edlana4bw.ps.Z                 1992/06/17 13:30:40 63.34KB adfr  file 2005 0644 1407
...<SNIPPED>...

MLSD命令是格式化目录列表的当前 FTP 标准,它是在RFC 3659上引入的。

Python列出FTP服务器所有文件和目录 - 最后,在使用 FTP 服务器后,是时候退出并关闭连接了:

# quit and close the connection
ftp.quit()

好了,教程到此结束。你现在不应该使用LISTcommand (在 Python 中使用dir()方法),这MLSD是要走的路,即使某些 FTP 服务器仍然不支持MLSDNLSTcommand 仍然是一种替代方法。

木子山

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: