Python ftplib 下載大檔案會卡住

我工作時,遭遇了用 Python 程式下載 500 MBs 的檔案之後會卡住。後來找到解決辦法。討論如下。

程式環境是 Windows 10 及 Python 3.5.1 。

以下程式,出現下載之後卡住。

def _show_progress(data, fo, fsize, mod_name):
    fo.write(data)
    print_progress(fo, fize)

def _retr_callback(fo, fize, mod_name):
    return lambda data: _show_progress(data, fo, fsize, mod_name)

ftp = ftplib.FTP()
...
fsize = ftp.size(src)
...
with open(target, 'wb') as fo:
    ftp.retrbinary('RETR ' + src, _retr_callback(fo, fsize, __name__))

研判卡住的位置是 ftp.retrbinary(…) 內。經過等待之後,進度條停在 100% ,而程式卡住不走下一步,而且,按了 Ctrl-c 也無法退出程式。

我設想,應該是 ftp 做為代理人,因為 retrbinary(…) 時不與 FTP server 溝通的時間太久,後來 ftp 代理人突然要講話的時候,叫了 FTP server 但是對方不回應,於是 ftp 代理人一直在等待對方回應。

解決辦法是在 retrbinary(…) 的 callback 裡頭,加上 ftp.voidcmd(‘NOOP’) ,讓它經常去給 FTP server 點一下、點一下。

所修改的程式如下:

def _show_progress(data, ftp, fo, fsize, mod_name):
    fo.write(data)

    ftp.voidcmd('NOOP')
    print_progress(fo, fize)

def _retr_callback(ftp, fo, fize, mod_name):
    return lambda data: _show_progress(data, ftp, fo, fsize, mod_name)

...

with open(target, 'wb') as fo:
    ftp.retrbinary('RETR ' + src, _retr_callback(ftp, fo, fsize, __name__))

而在 callback 之內增加 ftp.voidcmd(‘NOOP’) 的效果,會讓進度條的更動次數變慢。 IO 拖延了,完成 FTP 下載檔案的時間也會久一點。

廣告

About 黃耀賢 (Yau-Hsien Huang)

熱愛 Erlang ,並且有相關工作經驗。喜歡程式語言。喜歡邏輯。目前用 Python 工作。
本篇發表於 Uncategorized。將永久鏈結加入書籤。