Python Ftp文件传输

在使用 Python 的 FTP 支持时,所需要做的只是导入 ftplib 模块,并实例化一个 ftplib.FTP类对象。所有的 FTP 操作(如登录、传输文件和注销等)都要使用这个对象完成。

3.1 文件传输

3.1.1 文件传输因特网协议

因特网中最常见的事情就是传输文件。文件传输每时每刻都在发生。有很多协议可以用于在因特网上传输文件。最流行的包括文件传输协议(FTP)、UNIX  到  UNIX  复制协议(UUCP)、用于 Web 的超文本传输协议(HTTP)。另外,还有(UNIX 下的)远程文件复制命令 rcp(以及更安全、更灵活的 scp 和 rsync)。

在当下,HTTP、FTP、scp/rsync 的应用仍然非常广泛。HTTP 主要用于基于 Web 的文件下载以及访问 Web 服务,一般客户端无须登录就可以访问服务器上的文件和服务。大部分HTTP 文件传输请求都用于获取网页(即将网页文件下载到本地)。

而 scp 和rsync 需要用户登录到服务器主机。在传输文件之前必须验证客户端的身份,否则不能上传或下载文件。FTP 与 scp/rsync 相同,它也可以上传或下载文件,并采用了 UNIX 的多用户概念,用户需要输入有效的用户名和密码。但 FTP 也允许匿名登录。现在来深入了解 FTP。

3.1.2 文件传输协议

文件传输协议(File Transfer Protocol,FTP)由已故的 Jon Postel 和Joyce Reynolds 开发, 记录在 RFC(Request for Comment)959 号文档中,于 1985 年 10 月发布。FTP 主要用于匿名下载公共文件,也可以用于在两台计算机之间传输文件,特别是在使用 Windows 进行工作, 而文件存储系统使用 UNIX 的情况下。早在 Web 流行之前,FTP 就是在因特网上进行文件传输以及下载软件和源代码的主要手段之一。

前面提到过,FTP 要求输入用户名和密码才能访问远程 FTP 服务器,但也允许没有账号的用户匿名登录。不过管理员要先设置 FTP 服务器以允许匿名用户登录。这时,匿名用户的用户名是“anonymous”,密码一般是用户的电子邮件地址。与向特定的登录用户传输文件不同,这相当于公开某些目录让大家访问。但与登录用户相比,匿名用户只能使用有限的几个 FTP 命令。

图 3-1 展示了这个协议,其工作流程如下。

  1. 客户端连接远程主机上的 FTP 服务器。
  2. 客户端输入用户名和密码(或“anonymous”和电子邮件地址  )。
  3. 客户端进行各种文件传输和信息查询操作。
  4. 客户端从远程 FTP 服务器退出,结束传输。

当然,这只是一般情况下的流程。有时,由于网络两边计算机的崩溃或网络的问题,会导致整个传输在完成之前就中断。如果客户端超过 15 分钟(900 秒)还没有响应,FTP 连接就会超时并中断。

在底层,FTP 只使用 TCP(见第 2 章),而不使用 UDP。另外,可以将 FTP 看作客户端/服务器编程中的特殊情况。因为这里的客户端和服务器都使用两个套接字来通信:一个是控制和命令端口(21 号端口),另一个是数据端口(有时是 20 号端口),如图 3-1 所示。

图 3-1 因特网上的FTP 客户端和服务器。客户端与服务器在命令与控制端口通过 FTP 协议通信,而数据通过数据端口传输

前面说“有时”是因为  FTP  有两种模式:主动和被动。只有在主动模式下服务器才使用数据端口。在服务器把 20  号端口设置为数据端口后,它“主动”连接客户端的数据端口。而在被动模式下,服务器只是告诉客户端随机的数据端口号,客户端必须主动建立数据连接。

在这种模式下,FTP  服务器在建立数据连接时是“被动”的。最后,现在已经有了一种扩展的被动模式来支持第 6 版本的因特网协议(IPv6)地址——详见 RFC 2428。

Python 已经支持了包括 FTP 在内的大多数据因特网协议。可以在 http://docs.python.org/lib/internet.html 中找到支持各个协议的客户端模块。现在看看用 Python 创建因特网客户端程序有多么容易。

3.1.3 Python 和 FTP

那么如何用 Python 编写 FTP 客户端程序呢?其实之前已经提到过一些了,现在还要添加相应的 Python 模块导入和调用操作。再回顾一下流程。

  1. 连接到服务器。
  2. 登录。
  3. 发出服务请求(希望能得到响应)。
  4. 退出。

在使用 Python 的 FTP 支持时,所需要做的只是导入 ftplib 模块,并实例化一个 ftplib.FTP类对象。所有的 FTP 操作(如登录、传输文件和注销等)都要使用这个对象完成。下面是一段 Pytho 伪代码。

from ftplib import FTP
f = FTP('some.ftp.server') 
f.login('anonymous', 'your@email.address')
    :
f.quit()

在看真实的例子之前,先熟悉一下代码中会用到的 ftplib.FTP 类的方法。

3.1.4 ftplib.FTP 类的方法

表 3-1 列出了最常用的方法,这个表并不全面(要了解所有的方法,请参阅模块源代码), 但这里列出的方法涵盖了Python 中进行 FTP 客户端编程所需的 API。也就是说,其他方法不是必需的,因为其他方法要么提供辅助或管理功能,要么提供这些 API 使用。

表 3-1   FTP 对象的方法

(续表)

方 法描 述
retrlines(cmd [, cb])给定FTP 命令(如“RETR filename”),用于下载文本文件。可选的回调函数 cb 用于处理文件的每一行
retrbinary(cmd,cb[,bs=8192[, ra]])与 retrlines()类似,只是这个指令处理二进制文件。回调函数 cb 用于处理每一块(块大小默认为 8KB) 下载的数据
storlines(cmd, f)给定 FTP 命令(如“STOR filename”),用来上传文本文件。要给定一个文件对象 f
storbinary(cmd, f[,bs=8192])与storlines()类似,只是这个指令处理二进制文件。要给定一个文件对象 f,上传块大小 bs 默认为 8KB
rename(old, new)把远程文件 old 重命名为 new
delete(path)删除位于 path 的远程文件
mkd(directory)创建远程目录
rmd(directory)删除远程目录
quit()关闭连接并退出

在一般的 FTP 事务中,要使用到的指令有 login()、cwd()、dir()、pwd()、stor*()、retr*() 和 quit()。表 3-1 中没有列出的一些 FTP 对象方法也很有用。关于 FTP 对象的更多信息,请参阅 http://docs.python.org/library/ftplib#ftp-objects 中的Python 文档。

3.1.5 交互式 FTP 示例

在 Python 中使用 FTP 非常简单,甚至都不用写脚本,直接在交互式解释器中就能实时地看到操作步骤和输出。下面这个示例会话是在几年前python.org 还支持 FTP 服务器的时候做的。现在这个示例已经无法工作,只是用来演示与正在运行的 FTP 服务器进行交互的情形。

3.1.6 客户端 FTP 程序示例

前面提到过,如果直接在交互环境中使用 FTP 就无须编写脚本。但下面还是编写一段脚本,用来从 Mozilla 的网站下载最新的 Bugzilla 代码。示例 3-1 就用来完成这个工作。虽然这里在尝试编写一个应用程序,但读者也可以交互式地运行这段代码。这个程序使用 FTP 库下载文件,其中也包含一些错误检查。

示例 3-1 FTP 下载示例(getLatestFTP.py)
这个程序用于下载网站中最新版本的文件。读者可以修改这个程序,用来下载其他内容。

不过脚本并不会自动运行,需要手动运行才会下载代码。如果使用的是类 UNIX 系统, 可以设定一个 cron 作业来自动下载。另一个问题是,如果需要下载的文件的文件名或目录名被修改了,程序就无法正常工作。

如果运行脚本时没有出错,则会得到如下输出。

$ getLatestFTP.py
*** Connected to host "ftp.mozilla.org"
*** Logged in as "anonymous"
*** Changed to "pub/mozilla.org/webtools" folder
*** Downloaded "bugzilla-LATEST.tar.gz" to CWD
$

逐行解释

第 1~9 行
代码前几行导入要用的模块(主要用于抓取异常对象),并设置一些常量。

第 11~44 行
main()函数分为以下几步:创建一个 FTP 对象,尝试连接到 FTP 服务器(第 12~17 行), 然后返回。如果发生任何错误就退出。接着尝试用“anonymous”登录,如果不行就结束(第19~25 行)。下一步就是转到发布目录(第 27~33 行),最后下载文件(第 35~44 行)。

在第 14 行和本书中其他的异常处理程序中,需要保存异常实例 e。对于Python 2.5 或更老的版本,需要将 as 改为逗号,因为这里使用的是从Python 2.6 引入的新语法。Python 3 只会理解如第 14 行所示的新语法。

在第 35~36 行,向 retrbinary()传递了一个回调函数,每接收到一块二进制数据的时候都会调用这个回调函数。这个函数就是创建文件的本地版本时需要用到的文件对象的 write()方法。传输结束时,Python 解释器会自动关闭这个文件对象,因此不会丢失数据。虽然很方便, 但最好还是不要这样做,作为一个程序员,要尽量做到在资源不再被使用的时候就立即释放, 而不是依赖其他代码来完成释放操作。这里应该把开放的文件对象保存到一个变量(如变量loc),然后把 loc.write 传给 ftp.retrbinary()。

完成传输后,调用 loc.close()。如果由于某些原因无法保存文件,则移除空的文件来避免弄乱文件系统(第 40 行)。在os.unlink(FILE)两侧添加一些错误检查代码,以应对文件不存在的情况。最后,为了避免另外两行(第 43~44 行)关闭 FTP 连接并返回,使用了else 语句(第35~42 行)。

第 46~47 行
这是运行独立脚本的惯用方法。

3.1.7 FTP 的其他内容

Python 同时支持主动和被动模式。注意,在Python 2.0 及以前版本中,被动模式默认是关闭的;在 Python 2.1 及以后版本中,默认是打开的。

以下是一些典型的 FTP 客户端类型。

  • 命令行客户端程序:使用一些 FTP 客户端程序(如/bin/ftp 或 NcFTP)进行 FTP 传输,用户可以在命令行中交互式执行 FTP 传输。
  • GUI 客户端程序:与命令行客户端程序相似,但它是一个 GUI 程序,如WS_FTP、Filezilla、CuteFTP、Fetch、SmartFTP。
  • Web 浏览器:除了使用 HTTP 之外,大多数 Web 浏览器(也称为客户端)可以进行 FTP 传输。URL/URI 的第一部分就用来表示所使用的协议,如“http://blahblah”。这就告诉浏览器要使用 HTTP 作为与指定网站传输数据的协议。通过修改协议部分,就可以发送使用 FTP 的请求,如“ftp://blahblah”,这与使用 HTTP 的网页 URL 很像(当然,“ftp://”后面的“blahblah”可以展开为“host/path?attributes”)。如果要登录,用户可以把登录信息(以明文方式)放在URL 里,如:“ftp://user:passwd@host /path?attr1=val1&attr2=val2. . .”。
  • 自定义应用程序:自己编写的用于 FTP 文件传输的程序。这些是用于特殊目的的应用程序,一般这种程序不允许用户与服务器交互。

这 4 种客户端类型都可以用 Python 编写。前面用 ftplib 来创建了一个自定义应用,但读者也可以创建一个交互式的命令行应用程序。在命令行的基础上,还可以使用一些 GUI 工具包, 如 Tk、wxWidgets、GTK+、Qt、MFC,甚至 Swing(要导入相应的 Python 或Jython 的接口模块)来创建一个完整的 GUI 程序。最后,可以使用 Python 的urllib 模块来解析 FTP 的URL 并进行 FTP 传输。在 urllib 的内部也导入并使用了 ftplib,因此 urllib 也是 ftplib 的客户端。

FTP 不仅可以用于下载应用程序,还可以用于在不同系统之间传输文件。比如,如果读者是一个工程师或系统管理员,需要传输文件。在跨网络的时候,显然可以使用 scp 或 rsync 命令,或者把文件放到一个能从外部访问的服务器上。不过,在一个安全网络的内部机器之间移动大量的日志或数据库文件时,这种方法的开销就太大了,因为需要考虑安全性、加密、压缩、解压缩等因素。如果只是想写一个FTP 程序来在下班后自动移动文件,那么使用Python 是一个非常好的主意。

在 FTP 协议定义/规范(RFC 959)中,可以得到关于 FTP 的更多信息,地址为http://tools.ietf.org/html/rfc959 和www.network sorcery.com/enp/protocol/ftp.htm。其他相关的 RFC 包括 2228、2389、2428、2577、2640、4217。关于 Python 的更多 FTP 支持,可以访问这个页面:http://docs.python.org/library/ftplib

发表评论

邮箱地址不会被公开。 必填项已用*标注