确认Python 2.6 ftplib不支持Unicode文件名?选择?
有人能确认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库的滥用确认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包装
感谢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