确认Python 2.6 ftplib不支持Unicode文件名?选择?

确认Python 2.6 ftplib不支持Unicode文件名?选择?,python,file,unicode,ftp,Python,File,Unicode,Ftp,有人能确认Python 2.6 ftplib不支持Unicode文件名吗?或者Unicode文件名必须经过特殊编码才能与ftplib模块一起使用 下面的电子邮件交换似乎支持我的结论,即ftplib模块只支持ASCII文件名 ftplib应该使用UTF-8而不是拉丁-1编码吗? 对支持Unicode文件名的第三方Python FTP模块有何建议?我用谷歌搜索了这个问题,但没有成功[1],[2] 官方Python文档没有提到Unicode文件名[3] 谢谢,, 马尔科姆 [1] ftputil包装

有人能确认Python 2.6 ftplib不支持Unicode文件名吗?或者Unicode文件名必须经过特殊编码才能与ftplib模块一起使用

下面的电子邮件交换似乎支持我的结论,即ftplib模块只支持ASCII文件名

ftplib应该使用UTF-8而不是拉丁-1编码吗?

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

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

谢谢,, 马尔科姆

[1] ftputil包装ftplib并继承ftplib明显的仅支持ASCII的功能

[2] Paramiko的SFTP库确实支持Unicode文件名,但是我正在寻找与当前项目相关的ftp(vs.SFTP)支持

[3]

解决方法:

encodings.idna.ToASCII和.ToUnicode方法可用于将Unicode路径名转换为ASCII格式。如果使用这些函数包装所有远程路径名和dir/nlst方法的输出,则可以创建一种使用标准ftplib保留Unicode路径名的方法(还可以在不支持Unicode路径的文件系统上保留Unicode文件名)。这种技术的缺点是,服务器上的其他进程在引用上载到服务器的文件时也必须使用encodings.idna。顺便说一句:我知道这是对encodings.idna库的滥用


感谢Peter和Bob的评论,我觉得这些评论非常有用。

就我个人而言,我更担心的是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设备上,因此公共分母非常高,但对远程站点希望接受的代码页进行假设在我看来是相当危险的。

ftplib
对Unicode一无所知。它旨在为文件名传递字节字符串,当要求提供目录列表时,它将返回字节字符串。这些是传递到服务器或从服务器返回的字节的精确字符串

如果在Python2.x中将Unicode字符串传递给
ftplib
,那么当它被发送到底层套接字对象时,它将被强制为字节。此强制使用Python的“默认”编码,即US-ASCII以确保安全,并为非ASCII字符生成异常

您链接到的python开发消息是关于Python3.x中的
ftplib
,其中字符串默认为Unicode。这使得像
ftplib
这样的模块处于一种棘手的情况,因为尽管它们现在在前端使用Unicode字符串,但其背后的实际协议是基于字节的。因此,必须有一个额外的编码/解码级别,如果没有明确的干预来指定正在使用的编码,它将选择错误的公平更改


ftplib
在3.x中选择默认为ISO-8859-1,以便将每个字节保留为Unicode字符串中的字符。不幸的是,在目标服务器对文件名使用UTF-8排序规则的常见情况下(不管FTP守护程序本身是否知道文件名是UTF-8,而它通常不知道)。有很多这样的例子,Python标准库被残忍地攻击为Unicode字符串,并带来负面后果;Python 3的电池仍然在泄漏腐蚀性液体。

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

ftp.storbinary("STOR " + target_name.encode( "utf-8" ), open(file_name, 'rb'))
这假设FTP服务器支持RFC 2640,它允许utf-8文件名。在我的例子中,我使用了Android的SwiFTP服务器,它成功地传输了带有专有名称的文件

有人能确认Python 2.6 ftplib不支持Unicode文件名吗

没有

ftplib应该使用UTF-8而不是拉丁-1编码吗

这是有争议的。UTF-8是由指定的首选编码,但拉丁语-1通常对行为不端的实现(服务器或客户端)更友好。 如果服务器包含“UTF8”作为FEAT响应的一部分,那么您应该明确使用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')

我们得到了适用于Python2.7的FTPlib的UTF8编码文件名

注1:这里有一个背景,可以轻松解释UTF8和unicode:

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

您确实需要添加代码来(1)确定服务器是否支持UTF8,以及(2)以UTF8格式对unicode Python字符串进行编码。(3) (由于每个人获取文件列表的方式不同,因此未显示完整代码)在获取文件列表时,如果UTF8\u支持:name=name.decode('utf-8')


我希望我能把这两个回答都标记为答案,因为我发现你的回答也很有帮助。谢谢你的帮助!你好,MalcolmDaniel:我确实试过了,得到了ascii异常。我不确定这是否是出于设计,因为我未能正确编码我的文件名(例如,我想知道FTP路径是否有类似于encodings.idna的内容),或者根本原因是我连接到的特定FTP服务器。相关:
# 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