2016-05-19 9 views
0

Komplette noob zu Python, aber ich habe einfache FTP-Downloads und Uploads, die Chunks auf Festplatte schreiben, anstatt füllen RAM vor dem Schreiben der gesamten Datei getan.Wie führe ich einen FTP-Download mit mehreren Segmenten mit ftplib Python 2.7 durch?

Meine Frage ist, wie lade ich eine Datei in x Menge der Teile gleichzeitig (mehrere Threads Herunterladen verschiedener Segmente einer einzelnen Datei), während Sie es sofort auf die Festplatte schreiben anstatt RAM zuerst zu füllen?

Ich habe nach Beispielen dafür gesucht, aber sie füllen RAM zuerst dann schreibe die Datei aus.

Auch ich fragte mich, ob es überhaupt möglich ist, dies für den Upload zu tun?

Dank

Antwort

1

Also habe ich es selbst heraus :)

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() 

So erfuhr ich, dass Rückruf von retrbinary die tatsächlichen binären Daten, die es erhält. Also für jeden Thread, ich eine Datei erstellen und diese Binärdaten aus dem Rückruf anfügen, bis die Größe der Datei größer als die erwartete Größe ist, dann schneiden wir das Extra. Wenn alle Threads abgeschlossen sind, werden die Dateien verkettet und eine Datei mit dem ursprünglichen Dateinamen wird erstellt. Filesize und Sha256 sind fertig und confirmed es funktioniert. :)

Der Code von RichieHindle

+0

Für eine große Datei angepasst wurde ich folgende Fehlermeldung zu sehen Datei "/python/2.7.3/lib/python2.7/ftplib.py", Linie 555, in Größe resp = self.sendcmd ('SIZE' + Dateiname) Datei "/python/2.7.3/lib/python2.7/ftplib.py", Zeile 244, in sendcmd Rückgabe self.getresp() Datei " /python/2.7.3/lib/python2.7/ftplib.py ", Zeile 219, in getresp raise error_perm, bzw. ftplib.error_perm: 550 foo.tgz: zu groß für Typ A SIZE. Wie kann ich dieses Problem umgehen? ftp.sendcmd ('binary') funktioniert nicht – ghostkadost

+0

@gostkadost der Fehler, der ausgelöst wird, ist mit Berechtigungen (550) zu tun, stellen Sie sicher, dass Sie ihm den richtigen Pfad geben. –