Open In App

Command Line File Downloader in Python

Last Updated : 24 Jan, 2021
Improve
Improve
Like Article
Like
Save
Share
Report

Python is one of the most popular general-purpose programming languages with a wide range of use cases from general coding to complex fields like AI. One of the reasons for such popularity of python as a programming language is the availability of many built-in as well as third-party libraries and packages.

In this article, we are going to build a simple command-line file downloader, which you can download a file if you have the download link.

Approach:

We are going to download files over HTTP instead of FTP. Once we have made the request for the file, we will use the response as an input stream to write or save the file to the file system. While Downloading, details like download speed, time, and amount of file downloaded will be shown.

  • Take the file URL via command line
  • Open the file input or download stream over HTTP via requests.
  • Download the file by writing the file in chunks from the download/input stream into the file system.
  • Display the details
  • Save the file.

Step-by-step implementation:

Step 1: Libraries required

Python3




import requests
import sys
import time


Step 2: a dictionary for detecting file size in proper units

While Downloading the file, parameters like speed, size, time will be shown. So let’s see how to do this. Dictionary for getting file size and download speed unit depending on the amount downloaded takes sizes in bytes.

Python3




units = {
'B':{'size':1, 'speed':'B/s'},
'KB':{'size':1024, 'speed':'KB/s'},
'MB':{'size':1024*1024, 'speed':'MB/s'},
'GB':{'size':1024*1024*1024, 'speed':'GB/s'}
}


Function for checking units of the length of the file downloaded. Length is in bytes so corresponding units are checked.

Python3




def check_unit(length): # length in bytes
  if length < units['KB']['size']:
    return 'B'
  elif length >= units['KB']['size'] and length <= units['MB']['size']:
    return 'KB'
  elif length >= units['MB']['size'] and length <= units['GB']['size']:
    return 'MB'
  elif length > units['GB']['size']:
    return 'GB'


Step 3: Downloading file over HTTP

We will open a file stream over HTTP using requests and then save the data in chunks to the local file. Let’s see how the code will look and then put it all together.

Python3




# Opening file stream
  
r = requests.get(link_to_file, stream = True)  
  
# writing file data in chunks.
# for examples, A file of 10 MB written in
# chunk size of 8096 Bytes.
with open(file_name, 'wb') as f:  
  for chunk in r.iter_content(chunk_size):  
    f.write(chunk)


Step 4: Printing output

The output will consist of a dynamic progress bar with downloading details. For this we use stdout.write() and stdout.flush() methods

Python3




sys.stdout.write(format_string % (list_of_variables))
  
# for printing details in single 
# line which updates without going to the next line
# once the details are updated.
  
sys.stdout.flush() 


Below is the full implementation:

Python3




import requests
import sys
import time
  
units = {    
'B' : {'size':1, 'speed':'B/s'},
'KB' : {'size':1024, 'speed':'KB/s'},
'MB' : {'size':1024*1024, 'speed':'MB/s'},
'GB' : {'size':1024*1024*1024, 'speed':'GB/s'}
}
  
def check_unit(length): # length in bytes
    if length < units['KB']['size']:
        return 'B'
    elif length >= units['KB']['size'] and length <= units['MB']['size']:
        return 'KB'
    elif length >= units['MB']['size'] and length <= units['GB']['size']:
        return 'MB'
    elif length > units['GB']['size']:
        return 'GB'
  
# takes download link and directory where file to be saved.
def downloadFile(url, directory) :
  
    localFilename = url.split('/')[-1] # files name
  
    with open(directory + '/' + localFilename, 'wb') as f:
        print ("Downloading . . .\n")
        start = time.time() # start time
        r = requests.get(url, stream=True)
  
        # total length in bytes of the file
        total_length = float(r.headers.get('content-length')) 
  
        d = 0 # counter for amount downloaded 
  
        # when file is not available
        if total_length is None:
            f.write(r.content)
        else:
            for chunk in r.iter_content(8192):
                  
                d += float(len(chunk))
                f.write(chunk) # writing the file in chunks of 8192 bytes
  
                # amount downloaded in proper units
                downloaded = d/units[check_unit(d)]['size']
                  
                # converting the unit of total length or size of file from bytes.
                tl = total_length / units[check_unit(total_length)]['size'
                  
                trs = d // (time.time() - start) # speed in bytes per sec
                  
                #speed in proper unit
                download_speed = trs/units[check_unit(trs)]['size']
                  
                speed_unit = units[check_unit(trs)]['speed'] # speed in proper units
  
                done = 100 * d / total_length # percentage downloaded or done.
                  
                fmt_string = "\r%6.2f %s [%s%s] %7.2f%s  /  %4.2f %s  %7.2f %s"
                  
                set_of_vars = ( float(done), '%',
                                '*' * int(done/2),  
                                '_' * int(50-done/2),  
                                downloaded, check_unit(d),  
                                tl, check_unit(total_length),  
                                download_speed, speed_unit)
  
                sys.stdout.write(fmt_string % set_of_vars)
  
                sys.stdout.flush()
  
    return (time.time() - start) # total time taken for download
  
def main() :
    directory = '.'
    if len(sys.argv) > 1 :
        url = sys.argv[1] # url from cmd line arg
        if len(sys.argv) > 2:
            directory = sys.argv[2]
          
        total_time = downloadFile(url, directory)
        print ('')
        print ("Download complete...")
        print ("\rTime Elapsed: %.2fs" % total_time)
    else :
        print("No link found!")
  
if __name__ == "__main__" :
    main()


Save the code in a python file and use it as follows

python <program_name>.py <file_link> <save_location(by default '.')>

Output:



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads