确认Python 2.6的ftplib不支持Unicode文件名?有什么替代方案?

2 投票
5 回答
3163 浏览
提问于 2025-04-16 00:26

有人能确认一下,Python 2.6 的 ftplib 模块是否支持 Unicode 文件名吗?或者说,Unicode 文件名必须经过特殊编码才能在 ftplib 模块中使用吗?

以下的邮件交流似乎支持我的结论,即 ftplib 模块只支持 ASCII 文件名。

ftplib 应该使用 UTF-8 而不是 latin-1 编码吗?http://mail.python.org/pipermail/python-dev/2009-January/085408.html

有没有推荐的第三方 Python FTP 模块可以支持 Unicode 文件名?我在网上搜索这个问题没有找到答案 [1], [2]。

官方的 Python 文档没有提到 Unicode 文件名 [3]。

谢谢,Malcolm

[1] ftputil 是在 ftplib 的基础上封装的,它也继承了 ftplib 只支持 ASCII 的特性吗?

[2] Paramiko 的 SFTP 库支持 Unicode 文件名,但我现在具体想找的是 ftp(而不是 sftp)支持,跟我们当前的项目有关。

[3] http://docs.python.org/library/ftplib.html

解决方法:

可以使用 encodings.idna.ToASCII 和 .ToUnicode 方法将 Unicode 路径名转换为 ASCII 格式。如果你把所有的远程路径名和 dir/nlst 方法的输出都用这些函数包裹起来,就可以在使用标准 ftplib 的同时保留 Unicode 路径名(也能在不支持 Unicode 路径的文件系统上保留 Unicode 文件名)。不过,这种方法的缺点是,服务器上的其他进程在引用你上传到服务器的文件时也必须使用 encodings.idna。顺便提一下,我知道这有点滥用 encodings.idna 库。

谢谢 Peter 和 Bob 的评论,我觉得非常有帮助。

5 个回答

0

为了解决这个问题,我使用了以下代码

ftp.storbinary("STOR " + target_name.encode( "utf-8" ), open(file_name, 'rb'))

这个代码假设FTP服务器支持RFC 2640,http://www.ietf.org/rfc/rfc2640.txt,这个标准允许使用utf-8格式的文件名。在我的情况下,我使用了SwiFTP服务器在安卓设备上,它能够成功地传输带有正确名称的文件。

2

我个人觉得,跟ftp连接的另一端相比,我更担心的是那边的情况,而不是这个库的支持。ftp协议本身就不太稳定,尤其是在文件名上搞创意的时候。

根据RFC 959的说法:

     Pathname is defined to be the character string which must be
     input to a file system by a user in order to identify a file.
     Pathname normally contains device and/or directory names, and
     file name specification.  FTP does not yet specify a standard
     pathname convention.  Each user must follow the file naming
     conventions of the file systems involved in the transfer.

我理解这意味着文件名应该尽量简单,符合最低的要求。现在使用DOS服务器、Vax和IBM大型机的情况几乎没有,所以你很可能会连接到Windows或Unix系统,这样的共同要求还是比较高的。不过,去猜测远程网站希望接受哪种编码方式,我觉得这风险挺大的。

7

ftplib 这个库对 Unicode(即中文、日文等字符的编码方式)是完全不认识的。它的设计是用字节字符串来处理文件名,当你请求目录列表时,它也会返回字节字符串。这些字符串就是从服务器传来的原始字节。

如果你在 Python 2.x 中把一个 Unicode 字符串传给 ftplib,它会在发送到底层的 socket 对象时被强制转换成字节。这种转换使用的是 Python 的“默认”编码方式,也就是为了安全起见使用 US-ASCII(美国标准信息交换码),对于非 ASCII 字符会抛出异常。

你提到的 python-dev 信息是关于 Python 3.x 的,在这个版本中,字符串默认就是 Unicode。这就让像 ftplib 这样的模块处于一个棘手的境地,因为虽然它们现在在前端使用 Unicode 字符串,但实际的协议还是基于字节的。因此,必须额外进行编码和解码,如果没有明确指定使用什么编码,就有很大可能会选择错误的编码。

在 Python 3.x 中,ftplib 默认选择 ISO-8859-1 编码,以便在 Unicode 字符串中保留每个字节作为一个字符。不幸的是,这在目标服务器使用 UTF-8 编码来处理文件名的常见情况下,会导致意想不到的结果(无论 FTP 服务本身是否知道文件名是 UTF-8 编码,这通常是它不知道的)。像这样的情况有很多,Python 的标准库在处理 Unicode 字符串时被粗暴修改,导致了负面的后果;我认为 Python 3 的内置功能仍然存在一些问题。

撰写回答