2016-04-29 17 views
3

關於檢索FTP文件並將其寫入流(如字符串緩衝區或文件,然後可以迭代)的堆棧溢出有幾個答案。Python FTP「塊」迭代器(沒有將整個文件加載到內存中)

如:Read a file in buffer from FTP python

然而,這些解決方案涉及加載整個文件到內存或將其下載到磁盤開始之前處理該內容。

我沒有足夠的內存來緩衝整個文件,我無法訪問磁盤。這可以通過處理回調函數中的數據來完成,但我想知道是否有可能用一些魔術來包裝ftp代碼,這會返回一個迭代器,而不是用回調來代替我的代碼。

I.E.而不是:

def get_ftp_data(handle_chunk): 
    ... 
    ftp.login('uesr', 'password') # authentication required 
    ftp.retrbinary('RETR etc', handle_chunk) 
    ... 

get_ftp_data(do_stuff_to_chunk) 

我想:

for chunk in get_ftp_data(): 
    do_stuff_to_chunk(chunk) 

和(與現有的答案)我想這樣做,不反覆對之前寫整個FTP文件到硬盤或內存。

+1

有類似的問題(http://stackoverflow.com/questions/9968592/turn-functions-with-a-callback-into- [按功能有回調到Python生成?] python-generators) –

回答

4

你必須把retrbinary調用在另一個線程,並有回調飼料塊到一個迭代器:

import threading, Queue 

def ftp_chunk_iterator(FTP, command): 
    # Set maxsize to limit the number of chunks kept in memory at once. 
    queue = Queue.Queue(maxsize=some_appropriate_size) 

    def ftp_thread_target(): 
     FTP.retrbinary(command, callback=queue.put) 
     queue.put(None) 

    ftp_thread = threading.Thread(target=ftp_thread_target) 
    ftp_thread.start() 

    while True: 
     chunk = queue.get() 
     if chunk is not None: 
      yield chunk 
     else: 
      return 

如果你不能使用線程,你能做的最好是寫你的回調作爲協程:

from contextlib import closing 


def process_chunks(): 
    while True: 
     try: 
      chunk = yield 
     except GeneratorExit: 
      finish_up() 
      return 
     else: 
      do_whatever_with(chunk) 

with closing(process_chunks()) as coroutine: 

    # Get the coroutine to the first yield 
    coroutine.next() 

    FTP.retrbinary(command, callback=coroutine.send) 
# coroutine.close() # called by exiting the block 
+0

我很害怕這個。直覺上,它似乎並不是絕對需要線程的東西。另外,儘管我沒有在原始問題中明確說明這一點,但我的執行環境沒有線程。我希望有更好的辦法。 – natb1

+0

@ natb1:不幸的是,它確實需要線程。如果你不能使用線程,你可以做的最好的事情就是把你的回調寫成一個協程,這樣不那麼靈活,而且更加混亂。 – user2357112

+0

感謝您向我介紹協程。不幸的是,這個例子在我看來像一個更長時間的說'FTP.retrbinary(command,callback = do_whatever_with)' – natb1

相關問題