使用pycurl进行FTP上传时进度函数调用过于频繁

2 投票
1 回答
1273 浏览
提问于 2025-04-18 14:10

我在使用Ubuntu 12.0.4,pycurl版本是7.19,libcurl3版本是7.22(这两个都是通过apt-get直接从Ubuntu的仓库安装的)。我用来上传文件的代码是(“self”是我自己封装的一个对象):

self.curlTransfer = pycurl.Curl()
self.curlTransfer.setopt(pycurl.UPLOAD, 1)
self.curlTransfer.setopt(pycurl.USERPWD, '%s:%s'%(str(self.userName), str(self.password)))
self.curlTransfer.setopt(pycurl.NOPROGRESS, 0)
self.curlTransfer.setopt(pycurl.PROGRESSFUNCTION, self.__UpdateFileTransferProgress)
f = open(fileName, 'rb')
self.curlTransfer.setopt(pycurl.URL, 'ftp://' + self.ipAddress + self.path + destination)
self.curlTransfer.setopt(pycurl.INFILESIZE_LARGE, os.path.getsize(fileName))
self.curlTransfer.setopt(pycurl.READFUNCTION, f.read)
self.curlTransfer.perform()

我的回调函数“__UpdateFileTransferProgress”每秒被调用上千次,导致文件传输速度变得大约慢了3倍,如果我关闭进度回调的话。为了找到解决办法,我查了很多资料,唯一找到的相关信息是这个curl的bug报告。听起来这个bug可能已经修复了,但不清楚这个修复是否已经包含在我的版本里(或者这可能是一个完全不同的问题)。

有没有人遇到过这个问题?我考虑过手动更新到最新的libcurl和pycurl版本,但因为依赖关系的问题让我放弃了。我真的很喜欢pycurl的性能,尤其是和ftplib相比(当关闭进度回调时),但我需要这个回调函数来跟踪传输进度。

1 个回答

1

查看编辑后的更简洁的解决方案!

我决定直接下载了最新的libcurl和pycurl源代码(其实构建和安装起来还挺简单的)。这样做改善了情况,因为进度函数现在每秒只被调用几百次,而不是几千次,但使用进度回调时,性能下降还是很明显。为了绕过这个问题,我这样设置传输:

# Set transfer parameters.
self.curlTransfer.fp = open(fileName, 'rb')
self.curlTransfer.fileSize = os.path.getsize(fileName)
self.curlTransfer.setopt(pycurl.URL, 'ftp://' + self.ipAddress + self.path + destination)
self.curlTransfer.setopt(pycurl.INFILESIZE_LARGE, self.curlTransfer.fileSize)
self.curlTransfer.setopt(pycurl.READDATA, self.curlTransfer.fp)

# Store file.
self.curlTransfer.perform()

然后如果我想在另一个线程中获取进度:

def GetDataTransferred(self):
"""
Gets the amount of data transferred for the current file transfer.

@return Amount of data transferred (MB).
"""
try:

    # Try/except in case file is closed.
    try:
        return (float(self.curlTransfer.fp.tell())/float(myConstants.MB))
    except:
        if(self.curlTransfer.fileSize):
            return self.curlTransfer.fileSize

    return 0

except:
    Warning("Unable to get the amount of data transferred.")
    return 0

基本上,我是通过使用文件指针的“tell”功能来查看pycurl在传输中的位置。

编辑/解决:我最终自己修复了libcurl中的bug,方法是修改了/lib/progress.c,具体内容可以在我最初提问的bug报告线程中找到(Imgur链接)。听说他们已经在主代码中提交了修复,但在他们最新的版本(7.37.1)中并没有包含这个修复。我之所以选择这个方法,是因为停止传输的最简单方式是让进度函数返回非零值。你可以从pycurl.READFUNCTION返回非零值来停止传输,但在FTP上传时,这个函数每处理一个块(大约16KB)就会被调用一次,非常慢(使用pycurl.READDATA并提供一个文件指针会更好)。现在我可以干净利落地停止传输,使用他们预期的进度更新方法,同时保持libcurl的高性能。

撰写回答