10

I am uploading a large file with FTP using Python 3.4.

I would like to be able to show the progress percentage while uploading the file. Here's my code:

from ftplib import FTP
import os.path

# Init
sizeWritten = 0
totalSize = os.path.getsize('test.zip')
print('Total file size : ' + str(round(totalSize / 1024 / 1024 ,1)) + ' Mb')

# Define a handle to print the percentage uploaded
def handle(block):
    sizeWritten += 1024 # this line fail because sizeWritten is not initialized.
    percentComplete = sizeWritten / totalSize
    print(str(percentComplete) + " percent complete")

# Open FTP connection
ftp = FTP('website.com')
ftp.login('user','password')

# Open the file and upload it
file = open('test.zip', 'rb')
ftp.storbinary('STOR test.zip', file, 1024, handle)

# Close the connection and the file
ftp.quit()
file.close()

How to have the number of blocks already read in the handle function?

update

After reading cmd's answer, I added this to my code:

class FtpUploadTracker:
    sizeWritten = 0
    totalSize = 0
    lastShownPercent = 0
    
    def __init__(self, totalSize):
        self.totalSize = totalSize
    
    def handle(self, block):
        self.sizeWritten += 1024
        percentComplete = round((self.sizeWritten / self.totalSize) * 100)
        
        if (self.lastShownPercent != percentComplete):
            self.lastShownPercent = percentComplete
            print(str(percentComplete) + " percent complete")

And I call the FTP upload like this :

uploadTracker = FtpUploadTracker(int(totalSize))
ftp.storbinary('STOR test.zip', file, 1024, uploadTracker.handle)
Community
  • 1
  • 1
Gab
  • 5,604
  • 6
  • 36
  • 52
  • 1
    Creating progress bars with Python: http://thelivingpearl.com/2012/12/31/creating-progress-bars-with-python/ – Jeff Bauer Feb 24 '14 at 21:30
  • 1
    For Python 2 you'll need to change the `percentComplete` line to be: `percentComplete = round((float(self.sizeWritten) / float(self.totalSize)) * 100)` – JeffThompson Jul 19 '15 at 14:56
  • There is a module called [progressbar](https://pypi.python.org/pypi/progressbar). I haven't checked if it can work with ftplib, but in any case it is a pretty complete module to render progress bars – Fnord Oct 12 '16 at 17:49
  • Hey thanks, this is what I needed! One question: This prints every time in a new line, new number. Can I have only one number updating in the same place? @cmd Never mind, I found here https://stackoverflow.com/questions/517127/how-do-i-write-output-in-same-place-on-the-console – Hrvoje T Feb 27 '18 at 08:09
  • Is your block hardcoded 1024? What for do you use block in `def handle(self, block):` ? – Hrvoje T Feb 27 '18 at 10:10
  • 1
    Ok, now I get it. `session.storbinary('STOR '+upload_file, file, 1024, uploadTrack.handler)` here handler takes 2 arguments, instance name and block size which is 1024. Then I made like this: `def handler(self, chunk): self.size_written += len(chunk) – Hrvoje T Feb 27 '18 at 10:31

1 Answers1

5

There are three non-hacky ways I can think of. All of then shift the "ownwership" of the variable:

  1. have the value passed in and return the result (basically means its stored in the caller)
  2. have the value be global, and initialize it to 0 and the top of your file. (read up on the global keyword)
  3. have this function as a member function of a class to handle upload tracking. Then make sizeWritten a instance variable of that class.
cmd
  • 5,754
  • 16
  • 30
  • 1
    I used solution #3 and it worked, I will update my question with the working code. – Gab Feb 24 '14 at 21:01
  • 1
    @Gab: option: `3*` make a closure with `nonlocal sent_bytes` e.g., [`make_counter()`](http://stackoverflow.com/q/13857/4279) – jfs Feb 24 '14 at 22:46
  • I have done this using nonlocal, as @J.F.Sebastian suggested. You can see it (look for the print_transfer_status methods) here: https://bitbucket.org/dkurth/ftp_connection.py – Derek Kurth Oct 08 '14 at 17:09