2016-05-19 45 views
0

Python的完整noob,但我已經做了簡單的FTP下載和上傳,寫入塊的磁盤,而不是寫入整個文件之前填充RAM塊。如何使用ftplib Python 2.7執行多段FTP下載?

我的問題是,如何立即將它寫入磁盤而不是先填充RAM,同時下載x部分文件(多個線程下載單個文件的不同部分)?

我已經找過這個例子,但他們先填充RAM然後寫出文件。

此外,我想知道是否有可能做到這一點上傳?

感謝

回答

1

所以我想通了自己:)

from ftplib import * 
from threading import * 
from shutil import * 
import os 

num_parts = 20 
FTP_server = 'ftp.example.com' 
FTP_user = 'mark' 
FTP_password = 'password' 

FTP_directory = '/foo/bar' 
FTP_file = 'foo.bar' 


class Done(Exception): 
    pass 


def open_ftp(): 
    ftp = FTP(FTP_server, FTP_user, FTP_password) 
    ftp.cwd(FTP_directory) 
    return ftp 


def go(): 
    ftp = open_ftp() 
    filesize = ftp.size(FTP_file) 
    print 'filesize: ' + str(filesize) 
    ftp.quit() 

    chunk_size = filesize/num_parts 
    last_chunk_size = filesize - (chunk_size * (num_parts - 1)) 

    downloaders = [] 
    for i in range(num_parts): 
     if i == (num_parts - 1): 
      this_chunk_size = last_chunk_size 
     else: 
      this_chunk_size = chunk_size 
     downloaders.append(Downloader(i, chunk_size * i, this_chunk_size)) 
    for downloader in downloaders: 
     downloader.thread.join() 

    with open(FTP_file, 'w+b') as f: 
     for downloader in downloaders: 
      copyfileobj(open(downloader.part_name, 'rb'), f) 


class Downloader: 

    thread_number = 0 

    def __init__(self, part_number, part_start, part_size): 
     self.filename = FTP_file 
     self.part_number = part_number 
     self.part_name = 'part' + str(self.part_number) 
     self.part_start = part_start 
     self.part_size = part_size 
     Downloader.thread_number += 1 
     self.thread_number = Downloader.thread_number 
     self.ftp = open_ftp() 
     self.thread = Thread(target=self.receive_thread) 
     self.thread.start() 

    def receive_thread(self): 
     try: 
      self.ftp.retrbinary('RETR '+self.filename, self.on_data, 100000, self.part_start) 
     except Done: 
      pass 

    def on_data(self, data): 
     with open(self.part_name, 'a+b') as f: 
      f.write(data) 
     if os.path.getsize(self.part_name) >= self.part_size: 
      with open(self.part_name, 'r+b') as f: 
       f.truncate(self.part_size) 
      raise Done 

go() 

所以我據悉,從retrbinary回調是,它得到實際的二進制數據。因此,對於每個線程,我創建一個文件並將該回調中的二進制數據附加到該文件中,直到文件的大小大於預期的大小,然後我們截斷額外的文件。 當所有線程完成時,文件將被連接起來,並生成一個具有原始文件名的文件。文件大小和sha256完成並確認它的工作原理。 :)

代碼改編自RichieHindle

+0

對於大文件我看到下面的錯誤 文件「/python/2.7.3/lib/python2.7/ftplib.py」,線路555,在大小 resp = self.sendcmd('SIZE'+文件名) 文件「/python/2.7.3/lib/python2.7/ftplib.py」,第244行,在sendcmd中 return self.getresp() File「 /python/2.7.3/lib/python2.7/ftplib.py「,第219行,在getresp raise error_perm,resp ftplib.error_perm:550 foo.tgz:對於A型SIZE太大。 我該如何解決這個問題? ftp.sendcmd('binary')不起作用 – ghostkadost

+0

@gostkadost引發的錯誤是使用權限(550),確保你給它正確的路徑。 –