【问题标题】:Confirm that Python 2.6 ftplib does not support Unicode file names? Alternatives?确认 Python 2.6 ftplib 不支持 Unicode 文件名?备择方案?
【发布时间】:2011-03-08 09:51:52
【问题描述】:

有人可以确认 Python 2.6 ftplib NOT 支持 Unicode 文件名吗?还是必须对 Unicode 文件名进行特殊编码才能与 ftplib 模块一起使用?

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

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

对支持 Unicode 文件名的第 3 方 Python FTP 模块有什么建议吗?我用谷歌搜索了这个问题,但没有成功 [1]、[2]。

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

谢谢你, 马尔科姆

[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 提供的 cmets,我觉得这对我很有帮助。

【问题讨论】:

  • Daniel:我确实尝试过并得到了 ascii 异常。我不确定这是否是设计使然,因为我未能正确编码我的文件名(例如,我想知道 FTP 路径是否有类似于 encodings.idna 的东西),或者根本原因是由于特定的我正在连接的 FTP 服务器。

标签: python file unicode ftp


【解决方案1】:

我们获得了适用于 Python 2.7 的 FTPlib 的 UTF8 编码文件名。

注意 1: 这里有一个背景可以轻松解释 UTF8 和 unicode: https://code.google.com/p/iqbox-ftp/wiki/ProgrammingGuide_UnicodeVsAscii

注意 2:您可以查看我们用于 IQBox 的 AGPL 库。您也许可以使用这些(或其中的一部分),并且它们支持 UTF8 over FTP。查看filetransfer_abc.py

您确实需要添加代码以 (1) 确定服务器是否支持 UTF8,以及 (2) 将 unicode Python 字符串编码为 UTF8 格式。 (3) (完整代码不显示,因为每个人获取文件列表的方式不同)当您获取文件列表时,您还需要使用if UTF8_support: name = name.decode('utf-8')

# PART (1): DETERMINE IF SERVER HAS UTF8 SUPPORT:
# Get FTP features:
    try:
    features_string_ftp = ftp.sendcmd('FEAT')
    print features_string_ftp

    # Determine UTF8 support:
    if 'UTF8' in features_string_ftp.upper():
        print "FTP>> Server supports international characters (UTF8)"
        UTF8_support = True
    else:
        print "FTP>> Server does NOT support international (non-ASCII) characters."
        UTF8_support = False
    except:
    print "FTP>> Could not get list of features using FEAT command."
    print "FTP>> Server does NOT support international (non-ASCII) characters."
    UTF8_support = False


# Part (2): Encode FTP commands needed to be sent using UTF8 encoding, if it's supported.
    def sendFTPcommand(ftp, command_string, UTF8_support):
    # Needed for UTF8 international file names etc.
    c = None
    if UTF8_support:
        c = command_string.encode('utf-8')
    else:
        c = command_string

    # TODO: Add try-catch here and connection error retries.
    return ftp.sendcmd(c)

    # If you just want to get a string with the UTF8 command and send it yourself, then use this:
       def encodeFTPcommand(self, command_string. UTF8_support):
        # Needed for UTF8 international file names etc.
        c = None
        if UTF8_support:
            c = command_string.encode('utf-8')
        else:
            c = command_string  
        return c

【讨论】:

    【解决方案2】:

    有人可以确认 Python 2.6 ftplib 不支持 Unicode 文件名吗?

    没有。

    ftplib 是否应该使用 UTF-8 而不是 latin-1 编码?

    这是有争议的。 UTF-8 是RFC-2640 规定的首选编码,但 latin-1 通常对行为不端的实现(服务器或客户端)更友好。 如果服务器在 FEAT 响应中包含“UTF8”,那么您应该明确使用 UTF8。

     >>> utf8_server = 'UTF8' in ftp.sendcmd('FEAT')
    

    要在 python 2.x 中支持 unicode,您可以采用以下猴子补丁版本的 ftpdlib:

    class UnicodeFTP(ftplib.FTP):
        """A ftplib.FTP subclass supporting unicode file names as 
       described by RFC-2640."""
    
        def putline(self, line):
            line = line + '\r\n'
            if isinstance(line, unicode):
                line = line.encode('utf8')
            self.sock.sendall(line)
    

    ...并在使用其余 API 时传递 unicode 字符串,如下所示:

    >>> ftp = UnicodeFTP(host='ftp.site.com', user='foo', passwd='bar')
    >>> ftp.delete(u'somefile')
    

    【讨论】:

      【解决方案3】:

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

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

      这假定 FTP 服务器支持 RFC 2640 http://www.ietf.org/rfc/rfc2640.txt,它允许使用 utf-8 文件名。在我的例子中,我使用了 Android 的 SwiFTP 服务器,它成功地传输了具有正确名称的文件。

      【讨论】:

        【解决方案4】:

        ftplib 根本不了解 Unicode。它旨在为文件名传递字节字符串,当被要求提供目录列表时,它将返回字节字符串。这些是从服务器传递/返回的确切字节字符串。

        如果您在 Python 2.x 中将 Unicode 字符串传递给 ftplib,那么当它被发送到底层套接字对象时,它最终会被强制转换为字节。这种强制使用 Python 的“默认”编码,即。为安全起见,US-ASCII 为非 ASCII 字符生成异常。

        您链接到的 python-dev 消息正在谈论 Python 3.x 中的 ftplib,其中字符串默认为 Unicode。这使得像ftplib 这样的模块处于一个棘手的境地,因为尽管它们现在在前端使用Unicode 字符串,但其背后的实际协议是基于字节的。因此必须涉及额外级别的编码/解码,并且在没有明确干预来指定正在使用的编码的情况下,有一个公平的更改会选择错误。

        ftplib 在 3.x 中选择默认为 ISO-8859-1,以便将每个字节保留为 Unicode 字符串中的一个字符。不幸的是,在目标服务器对文件名使用 UTF-8 排序规则的常见情况下,这会产生意想不到的结果(无论 FTP 守护程序本身是否知道文件名是 UTF-8,但它通常不知道)。有很多这样的案例,Python 标准库被粗暴地破解为 Unicode 字符串,从而产生了负面影响; Python 3 的电池(包括在内)仍在泄漏 IMO 腐蚀性液体。

        【讨论】:

          【解决方案5】:

          我个人更担心 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 机器,因此共同点相当高,但假设远程站点想要接受哪个代码页似乎我很冒险。

          【讨论】:

          • 我希望我可以将两个回复都标记为答案,因为我发现您的回复也很有帮助。谢谢您的帮助!问候,马尔科姆
          猜你喜欢
          • 2012-09-29
          • 2010-12-23
          • 2010-10-24
          • 2016-07-03
          • 1970-01-01
          • 2019-05-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多