用Python能否在不写入文件的情况下读取FTP文件?

39

我正在尝试使用Python的ftplib读取文件,而不写入它们。 大致相当于:

def get_page(url):
    try:
        return urllib.urlopen(url).read()
    except:
        return ""

但是使用FTP。

我尝试过:

def get_page(path):
    try:
        ftp = FTP('ftp.site.com', 'anonymous', 'passwd')
        return ftp.retrbinary('RETR '+path, open('page').read())
    except:
        return ''

但是这种方法不起作用。文档中唯一的示例都涉及使用ftp.retrbinary('RETR README', open('README', 'wb').write)格式编写文件。是否可能在不先写入的情况下读取FTP文件?

2个回答

68

其实您已经有了答案: FTP.retrbinary 方法接受第二个参数作为一个函数的引用,该函数在从FTP连接检索文件内容时被调用。

这是一个简单的例子:

#!/usr/bin/env python
from ftplib import FTP

def writeFunc(s):
  print "Read: " + s

ftp = FTP('ftp.kernel.org') 
ftp.login()
ftp.retrbinary('RETR /pub/README_ABOUT_BZ2_FILES', writeFunc)

你应该实现 writeFunc,以便它实际上将读取的数据追加到内部变量中,就像这样使用可调用对象:

#!/usr/bin/env python
from ftplib import FTP

class Reader:
  def __init__(self):
    self.data = ""
  def __call__(self,s):
     self.data += s

ftp = FTP('ftp.kernel.org') 
ftp.login()
r = Reader()
ftp.retrbinary('RETR /pub/README_ABOUT_BZ2_FILES', r)

print r.data

更新:我发现Python标准库中有一个专门处理此类事情的模块,BytesIO

#!/usr/bin/env python
from ftplib import FTP
from io import BytesIO

ftp = FTP('ftp.kernel.org') 
ftp.login()
r = BytesIO()
ftp.retrbinary('RETR /pub/README_ABOUT_BZ2_FILES', r.write)

print r.getvalue()

3
太棒了,谢谢!我之前没意识到回调函数可以是用户定义的。 - aensm
20
对于Python 3,retrbinary需要使用BytesIO,因为它返回的是字节而不是字符串。如果你想要StringIO,请尝试使用ftp.retrlines()。 - Tim Richardson
@TimRichardson 我尝试使用ftp.retelines(f'RETR {filename}'),但它返回了一个BrokenPipeError错误,回溯显示文件编码存在问题(在putline函数的self.sock.sendall(line.encode(self.encoding))处)。我正在尝试获取的文件是md5哈希值(链接:ftp://ftp.ncbi.nlm.nih.gov/pubmed/baseline/pubmed23n0003.xml.gz.md5)。 - jimmymcheung
@jimmymcheung,您打错了,应该是“retilines”。 - Tim Richardson
@TimRichardson 谢谢,但这里是个打字错误。我检查了脚本,它是正确的(尽管现在我只使用 retrbinary 并注释掉 retrlines 脚本,因为我仔细阅读了文档并发现 retrlines 不以“字符串”模式读取文件)。 - jimmymcheung

0

使用retrlines,如果检索到的文件是文本模式,则可以更加简单,您可以直接获得一个列表,就像readlines一样:

#!/usr/bin/env python
from ftplib import FTP
    
ftp = FTP('ftp.kernel.org') 
ftp.login()
r = []
ftp.retrlines('RETR /pub/README_ABOUT_BZ2_FILES', r.append)

print(r)

对于反向过程(从BytesIO发送),请参见{{link1:如何在Python 3中通过FTP发送StringIO?}}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接