Python Forum
S3Zilla... an S3 File Transfer Client developed with Python3/Tkinter
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
S3Zilla... an S3 File Transfer Client developed with Python3/Tkinter
#1
I tried to make it like Filezilla... came out okay and works without issues.

Here's' the Github repository.

This is the Linux version:

#! /usr/bin/python3
# rootVIII
from tkinter import *
from tkinter.filedialog import askdirectory
from os import listdir, remove, execl
from shutil import rmtree, make_archive
from getpass import getuser, getpass
from os.path import isdir, basename
from sys import executable, argv
from time import sleep
try:
    import boto3
    from botocore.exceptions import ClientError
except ImportError as e:
    print("Unable to import boto3\n%s" % e)
    exit()


class S3Zilla:
    def __init__(self, master):
        error_msg = "Ensure S3 is configured on your machine:"
        try:
            self.s3 = boto3.resource('s3')
        except Exception as e:
            print("%s: %s" % (error_msg, e))
            exit(1)
        try:
            self.s3c = boto3.client('s3')
        except Exception as err:
            print("%s: %s" % (error_msg, err))
            exit(1)
        self.colors = {
            'light-grey': '#D9D9D9',
            'blue': '#2B547E',
            'black': '#000000',
            'red': '#FF3346',
            'grey': '#262626',
            'cyan': '#80DFFF'
        }
        self.master = master
        self.master.title("Amazon S3 File Transfer Client")
        self.master.configure(bg=self.colors['grey'])
        self.master.geometry("885x645")
        menu = Menu(self.master)
        menu.config(
            background=self.colors['grey'],
            fg=self.colors['light-grey']
        )
        self.master.config(menu=menu)
        file = Menu(menu)
        file.add_command(
            label="Exit",
            command=self.quit
        )
        menu.add_cascade(
            label="File",
            menu=file
        )
        refresh = Menu(menu)
        refresh.add_command(
            label="Local",
            command=self.refresh_local
        )
        refresh.add_command(
            label="S3",
            command=self.refresh_s3
        )
        menu.add_cascade(label="Refresh", menu=refresh)
        self.dir, self.drp_sel, self.bucket_name = '', '', ''
        self.folder_path = StringVar()
        self.dropdown = StringVar()
        self.dropdown_data = self.populate_dropdown()
        self.deleted = False
        self.local_sel, self.s3_sel = ([] for i in range(2))
        self.title_label = Label(
            master,
            fg=self.colors['light-grey'],
            bg=self.colors['grey'],
            font="Helvetica 10 bold",
            width=120,
        )
        self.local_label = Label(
            master,
            fg=self.colors['light-grey'],
            bg=self.colors['grey'],
            text="LOCAL FILE SYSTEM",
            font="Helvetica 10 bold underline",
            width=60
        )
        self.s3_label = Label(
            master,
            fg=self.colors['light-grey'],
            bg=self.colors['grey'],
            text="AMAZON  S3",
            font="Helvetica 10 bold underline",
            underline=True,
            width=60
        )
        self.dropdown_box = OptionMenu(
            master,
            self.dropdown,
            *self.dropdown_data,
            command=self.set_drop_val
        )
        self.dropdown_box.configure(
            fg=self.colors['light-grey'],
            bg=self.colors['blue'],
            width=27,
            highlightbackground=self.colors['black'],
            highlightthickness=2
        )
        self.browse_button = Button(
            master,
            fg=self.colors['light-grey'],
            bg=self.colors['blue'],
            text="Browse",
            width=30,
            highlightbackground=self.colors['black'],
            highlightthickness=2,
            command=self.load_dir
        )
        self.browse_label = Label(
            master,
            fg=self.colors['light-grey'],
            bg=self.colors['grey'],
            text="No directory selected",
            width=37,
            font="Helvetica 10"
        )
        self.bucket_label = Label(
            master,
            fg=self.colors['light-grey'],
            bg=self.colors['grey'],
            text="No bucket selected",
            width=37,
            font="Helvetica 10"
        )
        self.refresh_btn_local = Button(
            master,
            fg=self.colors['light-grey'],
            bg=self.colors['blue'],
            text="REFRESH",
            width=30,
            highlightbackground=self.colors['black'],
            highlightthickness=2,
            command=self.refresh_local
        )
        self.refresh_btn_s3 = Button(
            master,
            fg=self.colors['light-grey'],
            bg=self.colors['blue'],
            text="REFRESH",
            width=30,
            highlightbackground=self.colors['black'],
            highlightthickness=2,
            command=self.refresh_s3
        )
        self.explorer_label_local = Label(
            master,
            fg=self.colors['light-grey'],
            bg=self.colors['blue'],
            width=30,
            text="Local File System:  "
        )
        self.explorer_label_s3 = Label(
            master,
            fg=self.colors['light-grey'],
            bg=self.colors['black'],
            width=30,
            text="S3 File System"
        )
        self.ex_loc = Listbox(
            master,
            fg=self.colors['cyan'],
            bg=self.colors['black'],
            width=49,
            height=18,
            highlightcolor=self.colors['black'],
            selectmode="multiple"
        )
        self.ex_s3 = Listbox(
            master,
            fg=self.colors['cyan'],
            bg=self.colors['black'],
            width=49,
            height=18,
            highlightcolor=self.colors['black'],
            selectmode="multiple"
        )
        self.upload_button = Button(
            master,
            fg=self.colors['light-grey'],
            bg=self.colors['blue'],
            text="Upload ->",
            width=20,
            highlightbackground=self.colors['black'],
            highlightthickness=2,
            command=self.upload
        )
        self.download_button = Button(
            master,
            fg=self.colors['light-grey'],
            bg=self.colors['blue'],
            text="<- Download",
            width=20,
            highlightbackground=self.colors['black'],
            highlightthickness=2,
            command=self.download
        )
        self.delete_local = Button(
            master,
            fg=self.colors['light-grey'],
            bg=self.colors['red'],
            text="DELETE",
            width=20,
            highlightbackground=self.colors['black'],
            command=self.delete_local_records
        )
        self.delete_s3 = Button(
            master,
            fg=self.colors['light-grey'],
            bg=self.colors['red'],
            text="DELETE",
            width=20,
            highlightbackground=self.colors['black'],
            command=self.delete_s3_records
        )
        self.found_label_local = Label(
            master,
            fg=self.colors['light-grey'],
            bg=self.colors['grey'],
            text="found local",
            width=54
        )
        self.found_label_s3 = Label(
            master,
            fg=self.colors['light-grey'],
            bg=self.colors['grey'],
            text="found s3",
            width=54
        )
        self.status_label = Label(
            master,
            fg=self.colors['light-grey'],
            bg=self.colors['grey'],
            text="Hello " + getuser(),
            width=54
        )
        self.create_bucket_label = Label(
            master,
            fg=self.colors['light-grey'],
            bg=self.colors['grey'],
            text="New Bucket:",
        )
        self.create_bucket_name = Text(
            master,
            fg=self.colors['cyan'],
            bg=self.colors['black'],
            width=25,
            height=1
        )
        self.create_bucket_button = Button(
            master,
            fg=self.colors['light-grey'],
            bg=self.colors['blue'],
            text="Create",
            width=5,
            highlightbackground=self.colors['black'],
            highlightthickness=2,
            command=self.create_bucket
        )
        # ####### begin grid placement ####### #
        self.title_label.grid(
            row=0,
            sticky=E+W,
            padx=20,
            pady=5
        )
        self.local_label.grid(
            row=0,
            sticky=W,
            padx=8,
            pady=5
        )
        self.s3_label.grid(
            row=0,
            sticky=E,
            padx=0,
            pady=5
        )
        self.browse_button.grid(
            row=1,
            sticky=W,
            padx=86,
            pady=10
        )
        self.dropdown_box.grid(
            row=1,
            sticky=E,
            padx=86,
            pady=5
        )
        self.browse_label.grid(
            row=2,
            sticky=W,
            padx=86,
            pady=5
        )
        self.bucket_label.grid(
            row=2,
            sticky=E,
            padx=86,
            pady=5
        )
        self.refresh_btn_local.grid(
            row=3,
            sticky=W,
            padx=86,
            pady=10
        )
        self.refresh_btn_s3.grid(
            row=3,
            sticky=E,
            padx=86,
            pady=10
        )
        self.explorer_label_local.grid(
            row=4,
            sticky=W,
            padx=20
        )
        self.explorer_label_s3.grid(
            row=4,
            sticky=E,
            padx=20
        )
        self.ex_loc.grid(
            row=4,
            sticky=W,
            padx=20
        )
        self.ex_s3.grid(
            row=4,
            sticky=E,
            padx=20
        )
        self.upload_button.grid(
            row=5,
            sticky=W,
            padx=224,
            pady=0
        )
        self.download_button.grid(
            row=5,
            sticky=E,
            padx=224,
            pady=0
        )
        self.delete_local.grid(
            row=5,
            sticky=W,
            padx=20,
            pady=0
        )
        self.delete_s3.grid(
            row=5,
            sticky=E,
            padx=20,
            pady=0
        )
        self.found_label_local.grid(
            row=6,
            sticky=W,
            padx=0,
            pady=20
        )
        self.found_label_s3.grid(
            row=6,
            sticky=E,
            padx=0,
            pady=20
        )
        self.status_label.grid(
            row=7,
            sticky=W,
            padx=0,
            pady=20
        )
        self.create_bucket_label.grid(
            row=7,
            sticky=E,
            padx=330,
            pady=0
        )
        self.create_bucket_name.grid(
            row=7,
            sticky=E,
            padx=100,
            pady=0
        )
        self.create_bucket_button.grid(
            row=7,
            sticky=E,
            padx=20,
            pady=0
        )
        n1 = "%s files found" % str(self.ex_loc.size())
        self.set_found_local_label(n1)
        n2 = "%s files found" % str(self.ex_s3.size())
        self.set_found_s3_label(n2)

    def quit(self):
        exit()

    def get_local_sel(self):
        return [self.ex_loc.get(i) for i in self.ex_loc.curselection()]

    def get_s3_sel(self):
        return [self.ex_s3.get(i) for i in self.ex_s3.curselection()]

    def set_drop_val(self, selection):
        self.drp_sel = selection

    def delete_local_records(self):
        files = self.get_local_sel()
        if not files:
            message = "Please select a file(s) to delete"
            self.set_status_label(message)
        else:
            self.del_local(files)

    def del_local(self, files_remaining):
        if len(files_remaining) > 0:
            f = files_remaining.pop(0)
            if not isdir(self.dir + "/" + f):
                try:
                    remove("%s/%s" % (self.dir, f))
                except Exception as err:
                    self.set_status_label("%s" % err)
                    self.status_label.update_idletasks()
                self.del_local(files_remaining)
            else:
                try:
                    rmtree("%s/%s" % (self.dir, f))
                except Exception as e:
                    self.set_status_label("%s" % e)
                    self.status_label.update_idletasks()
                self.del_local(files_remaining)
        self.deleted = True
        self.refresh_local()

    def delete_s3_records(self):
        removal = ''
        if not self.drp_sel:
            m = "Please select a bucket..."
            self.set_status_label(m)
        else:
            removal = self.get_s3_sel()
        if not removal:
            m = "Please select at least 1 object to delete"
            self.set_status_label(m)
        else:
            bucket = self.s3.Bucket(self.drp_sel)
            for rm in removal:
                for k in bucket.objects.all():
                    if k.key != rm:
                        continue
                    k.delete()
                    break
            self.deleted = True
            self.refresh_s3()

    def load_dir(self):
        self.dir = askdirectory()
        self.set_local_browse_label(self.dir)

    def refresh_local(self):
        if not self.dir:
            m = "Use the browse button to select a directory"
            self.set_status_label(m)
        else:
            self.set_local_browse_label(self.dir)
            self.ex_loc.delete(0, 'end')
            x = self.dir + "/"
            d = [f if not isdir(x+f) else f + '/' for f in sorted(listdir(x))]
            self.ex_loc.insert('end', *d)
            if not self.deleted:
                m = "Hello %s" % getuser()
            else:
                m = "FINISHED DELETING"
                self.deleted = False
            self.set_status_label(m)
            n = "%s files found" % str(self.ex_loc.size())
            self.set_found_local_label(n)

    def refresh_s3(self):
        if not self.drp_sel:
            m = "Please select a bucket from the drop-down list"
            self.set_status_label(m)
        else:
            self.ex_s3.delete(0, 'end')
            self.ex_s3.insert('end', *self.get_bucket_contents())
            self.set_status_label("Hello %s" % getuser())
            self.set_s3_bucket_label(self.drp_sel)
            n = "%s files found" % str(self.ex_s3.size())
            self.set_found_s3_label(n)
            self.found_label_s3.update_idletasks()
            if not self.deleted:
                m = "Hello %s" % getuser()
            else:
                m = "FINISHED DELETING"
                self.deleted = False
            self.set_status_label(m)

    def finished(self, incoming_message):
        d = "FINISHED %s" % incoming_message
        for letter in enumerate(d):
            self.set_status_label(d[0:letter[0] + 1])
            self.status_label.update_idletasks()
            sleep(.1)

    def upload(self):
        if not self.drp_sel or not self.dir:
            m = "Ensure a local path and S3 bucket are selected"
            self.set_status_label(m)
        elif not self.get_local_sel():
            m = "Ensure files are selected to upload"
            self.set_status_label(m)
        else:
            for selection in self.get_local_sel():
                file_ = "%s/%s" % (self.dir, selection)
                if not isdir(file_):
                    self.s3c.upload_file(file_, self.drp_sel, basename(file_))
                else:
                    zipd = make_archive(file_, 'zip', self.dir, selection)
                    self.s3c.upload_file(zipd, self.drp_sel, basename(zipd))
                    remove(zipd)
                m = "Uploaded: %s" % selection
                self.set_status_label(m)
                self.status_label.update_idletasks()
            self.refresh_s3()
            self.finished("UPLOAD")

    def download(self):
        if not self.drp_sel or not self.dir:
            m = "Ensure a file and bucket have been selected"
            self.set_status_label(m)
        elif not self.get_s3_sel():
            m = "Ensure files are selected to download"
            self.set_status_label(m)
        else:
            for selection in self.get_s3_sel():
                file_ = "%s/%s" % (self.dir, selection)
                self.s3c.download_file(self.drp_sel, selection, file_)
            self.refresh_local()
            self.finished("DOWNLOAD")

    def get_bucket_contents(self):
        bucket = self.s3.Bucket(self.drp_sel)
        return [s3_file.key for s3_file in bucket.objects.all()]

    def populate_dropdown(self):
        return [bucket.name for bucket in self.s3.buckets.all()]

    def set_local_browse_label(self, incoming):
        if len(incoming) > 35:
            self.browse_label.config(text=basename(incoming) + '/')
        else:
            self.browse_label.config(text=incoming)

    def set_s3_bucket_label(self, incoming):
        self.bucket_label.config(text=incoming)

    def set_status_label(self, incoming):
        self.status_label.config(text=incoming)

    def set_found_local_label(self, incoming):
        self.found_label_local.config(text=incoming)

    def set_found_s3_label(self, incoming):
        self.found_label_s3.config(text=incoming)

    def create_bucket(self):
        self.bucket_name = self.create_bucket_name.get("1.0", END).strip()
        if not self.bucket_name:
            m = "Please enter a new bucket name"
            self.set_status_label(m)
        else:
            pre_exists = False
            try:
                self.s3.create_bucket(Bucket=self.bucket_name)
            except ClientError as ce:
                pre_exists = True
                m = "Bucket name already in use. "
                m += "Choose a different name."
                self.set_status_label(m)
            if not pre_exists:
                m = "%s created: restarting..." % self.bucket_name
                self.set_status_label(m)
                self.status_label.update_idletasks()
                sleep(2)
                res = executable
                execl(res, res, *argv)


if __name__ == "__main__":
    root = Tk()
    s3_zilla = S3Zilla(root)
    root.mainloop()
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Arc_AntsHunt: Game Developed In Python Arcade adt 2 4,361 Apr-24-2021, 06:05 PM
Last Post: adt
  Tkinter basic email client menator01 4 3,647 Jul-23-2020, 08:16 AM
Last Post: ndc85430

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020