ftplib-简易ftp客户端

#获取上一次的参数
try:
    with open("./FtpGUI-Python-Configure.cache","r",encoding = "utf-8") as f:
        cfglist = f.read().split("\n")
except:cfglist = ["","","",""]

import tkinter as tk
from tkinter import filedialog, messagebox,simpledialog
import ftplib
import os
from tkinter import ttk
import functools
from threading import Thread as td
ftp = False
is_upload_files = False
is_download_files = False
def getinfo(*args):
    arg = []
    for i in args:
        arg.append(i)
    arg.append("\n")
    cct.config(state = "normal")
    cct.insert(tk.END," ".join(arg))
    cct.config(state = "disabled")
    
    limit = 500 # 限制行数
    lines = cct.get("1.0", "end-1c").split("\n")
    if len(lines) >= limit:
        cct.delete("1.0","%s.0" % str(len(lines) - limit))
    cct.config(state = "disabled")
    cct.see(tk.END)
    win.update()

def connect_to_ftp():
    global ftp
    try:
        ftp = ftplib.FTP(ipe.get(),pte.get())
        ftp.login(une.get(),pwe.get())
        ftp.set_debuglevel(0)
        getinfo(f"连接成功:已连接到FTP服务器{ipe.get()}\n用户名:{une.get()},密码:{'•' * len(pwe.get())}")
        fspe.delete(0,tk.END)
        fspe.insert(0,ftp.pwd())
        if smcvar.get():messagebox.showinfo("连接成功", "已连接到FTP服务器")
    except Exception as e:
        getinfo("连接服务器:Error:",str(e))
        if smcvar.get():messagebox.showerror("错误-连接", str(e))
    list_files()
run_list_files = 0
def list_files(event = None):
    global run_list_files
    if run_list_files:return
    run_list_files = 1
    try:
        if not ftp:
            getinfo("文件列表:警告:请先连接到FTP服务器!")
            run_list_files = 0
            if smcvar.get():messagebox.showwarning("未连接", "请先连接到FTP服务器")
            return
        files = []
        for entry in ftp.nlst():
            files.append(entry)
        fsl.delete(0,tk.END)
        getinfo("文件列表:移除列表原有内容")
        for file in files:
            # 将文件名添加到列表框中
            fsl.insert(tk.END, file)
            getinfo("加载文件:",file)
        getinfo("文件列表:文件列表加载成功!")
        if smcvar.get():messagebox.showinfo("文件列表", "文件列表加载成功!")
    except Exception as e:
        getinfo("文件列表:错误:",str(e))
        if smcvar.get():messagebox.showerror("错误-列表", str(e))
    run_list_files = 0
    
def download_files(filename_in = ""):
    global down_downloaded_size,is_download_files
    def gettexts(listbox):
        # 获取当前选中项目的索引
        l = []
        for i in listbox.curselection():
            l.append(listbox.get(i))
        return l
    def callBack(data):
        global down_downloaded_size
        file.write(data)
        down_downloaded_size += len(data)
        progress = (down_downloaded_size / filesize) * 100
        getinfo("下载进度: {:.2f}%".format(progress))
        tprogress = float("{:.1f}".format(progress))
        fdp["value"] = tprogress
        fdl.config(text = str(tprogress) + "%    ")
        fda.config(text = f"{filesize}")
        fdd.config(text = f"{down_downloaded_size}")
        fdc.config(text = str(filesize - down_downloaded_size))
    if (not ftp) or (is_upload_files == True) or (is_download_files == True):
        getinfo("文件下载:警告:请先连接到FTP服务器或等待其它任务完成!")
        if smcvar.get():messagebox.showwarning("未连接", "请先连接到FTP服务器或等待其它任务完成")
        is_download_files = False
        return
    try:
        is_download_files = True
        fdp["value"] = 0
        fdf.config(text = "下载进度")
        fdl.config(text = "-%    ")
        fdn.config(text = "当前下载文件:-")
        if not filename_in:
            flist = gettexts(fsl)
        else:
            flist = [filename_in]
        n = len(flist)
        x = 0
        ax = 0
        for path in flist:
            getinfo("正在下载文件:",path)
            try:
                ax += 1
                fdn.config(text = f"当前下载文件:{path.split('/')[-1]}")
                down_downloaded_size = 0
                filesize = ftp.size(path)
                file = open(path, 'wb')
                ftp.retrbinary("RETR %s"%path, blocksize = 1024 * 64, callback = callBack)
                file.close()
                fdAv.config(text = str(n))
                fdNv.config(text = str(n - ax))
                fdFv.config(text = str(ax - x))
                win.update()
                x += 1
            except Exception as e:
                getinfo("文件下载:错误:",str(e))
                if smcvar.get():messagebox.showerror("错误", str(e))
        n = n - x
        getinfo(f"文件下载:下载完成!\n从{ipe.get()}服务器下载:已下载{x}个,未下载{n}个")
        #返回初始显示
        fdp["value"] = 0
        fdf.config(text = "下载/上传进度")
        fdl.config(text = "-%    ")
        fdn.config(text = "当前下载/上传文件:-")
        fda.config(text = "-")
        fdd.config(text = "-")
        fdc.config(text = "-")
        fdAv.config(text = "-")
        fdNv.config(text = "-")
        fdFv.config(text = "-")
        list_files()
        messagebox.showinfo("文件下载",f"文件下载:下载完成!\n从{ipe.get()}服务器下载:\n已下载{x}个,未下载{n}个")
    except Exception as e:
        getinfo("文件下载:错误:",str(e))
        if smcvar.get():messagebox.showerror("错误", str(e))
    is_download_files = False

def upload_files():
    global down_uploaded_size,down_upload_files
    def callBack(data = None):
        global down_uploaded_size
        down_uploaded_size += len(data)
        progress = (down_uploaded_size / filesize) * 100
        getinfo("上传进度: {:.2f}%".format(progress))
        tprogress = float("{:.1f}".format(progress))
        fdp["value"] = tprogress
        fdl.config(text = str(tprogress) + "%    ")
        fda.config(text = f"{filesize}")
        fdd.config(text = f"{down_uploaded_size}")
        fdc.config(text = str(filesize - down_uploaded_size))
    if (not ftp) or (is_upload_files == True) or (is_download_files == True):
        getinfo("文件上传:警告:请先连接到FTP服务器或等待其它任务完成!")
        if smcvar.get():messagebox.showwarning("未连接", "请先连接到FTP服务器或等待其它任务完成")
        down_upload_files = False
        return
    try:
        down_upload_files = True
        fdp["value"] = 0
        fdf.config(text = "上传进度")
        fdl.config(text = "-%    ")
        fdn.config(text = "当前上传文件:-")
        flist = list(tk.filedialog.askopenfilenames(title = f"请选择需要上传到服务器 {ipe.get()} 的文件"))
        n = len(flist)
        x = 0
        ax = 0
        for path in flist:
            try:
                ax += 1
                getinfo("正在上传文件:",path)
                fdn.config(text = f"当前上传文件:{path.split('/')[-1]}")
                down_uploaded_size = 0
                filesize = os.path.getsize(path)
                file = open(path,'rb')
                ftp.storbinary('STOR ' + "/{}".format(path.split("/")[-1]),fp = file,blocksize = 1024 * 64,callback = callBack)
                file.close()
                x += 1
                fdAv.config(text = str(n))
                fdNv.config(text = str(n - ax))
                fdFv.config(text = str(ax - x))
                win.update()
            except Exception as e:
                getinfo("文件上传:错误:",str(e))
                if smcvar.get():messagebox.showerror("错误", str(e))
        n = n - x
        getinfo(f"文件上传:上传完成!\n向{ipe.get()}服务器上传:已上传{x}个,未上传{n}个")
        #返回初始显示
        fdp["value"] = 0
        fdf.config(text = "下载/上传进度")
        fdl.config(text = "-%    ")
        fdn.config(text = "当前下载/上传文件:-")
        fda.config(text = "-")
        fdd.config(text = "-")
        fdc.config(text = "-")
        fdAv.config(text = "-")
        fdNv.config(text = "-")
        fdFv.config(text = "-")
        list_files()
        messagebox.showinfo("文件上传",f"文件上传:上传完成!\n向{ipe.get()}服务器上传:\n已上传{x}个,未上传{n}个")
    except Exception as e:
        getinfo("文件上传:错误:",str(e))
        if smcvar.get():messagebox.showerror("错误", str(e))
    down_upload_files = False

#删除文件函数
def delfiles():
    def gettexts(listbox):
        # 获取当前选中项目的索引
        l = []
        for i in listbox.curselection():
            l.append(listbox.get(i))
        return l
    if (not ftp) or (is_upload_files == True) or (is_download_files == True):
        getinfo("文件上传:警告:请先连接到FTP服务器或等待其它任务完成!")
        if smcvar.get():messagebox.showwarning("未连接", "请先连接到FTP服务器或等待其它任务完成")
        down_upload_files = False
        return
    dell = gettexts(fsl)
    getinfo(f"删除文件:选中的文件列表:{dell}")
    if messagebox.askyesno('删除文件-警告',message = f"确认删除以下文件?\n{dell}\n删除后无法恢复!"):
        try:
            getinfo("删除文件:开始删除文件")
            x = 0
            ax = 0
            n = len(dell)
            for i in dell:
                try:
                    ftp.cwd(i)
                    ftp.cwd('..')
                    ftp.rmd(i)
                except Exception as e:
                    getinfo(f"删除文件{i}:{str(e)}")
                    ftp.delete(i)
                getinfo(f"删除文件:已完成:{i}")
                fdAv.config(text = str(n))
                fdNv.config(text = str(n - ax))
                fdFv.config(text = str(ax - x))
                win.update()
            getinfo(f"删除文件:删除完成{dell}!")
            fdAv.config(text = "-")
            fdNv.config(text = "-")
            fdFv.config(text = "-")
            list_files()
            if smcvar.get():messagebox.showinfo(f"删除文件:删除完成{dell}!")
        except Exception as e:
            if "550 Permission denied" in str(e):
                getinfo("你没有权限删除该文件!")
                getinfo("Err:",str(e))
                if smcvar.get():messagebox.showinfo("删除文件:Err",f"你没有权限删除该文件!\nErr:{str(e)}")
            else:
                getinfo("Err:",str(e))
    else:
        getinfo("删除文件:已取消操作!")
        messagebox.showinfo("删除文件","已取消操作!")
        return

#创建文件夹函数
def mkdir():
    if (not ftp) or (is_upload_files == True) or (is_download_files == True):
        getinfo("文件上传:警告:请先连接到FTP服务器或等待其它任务完成!")
        if smcvar.get():messagebox.showwarning("未连接", "请先连接到FTP服务器或等待其它任务完成")
        down_upload_files = False
        return
    dirname = simpledialog.askstring(title = "请输入创建的文件夹名", prompt = "\n                    请输入需要创建的文件夹名                    \n")
    if dirname:
        ftp.mkd(dirname)
    list_files()
        
#设置密码显示函数
isdepwshow = False
def depwshow():
    global isdepwshow
    if not isdepwshow:
        pwb.config(text = "隐藏")
        pwe.config(show = "")
    else:
        pwb.config(text = "显示")
        pwe.config(show = "•")
    isdepwshow = not isdepwshow
    
#关闭窗口函数
def closewin():
    print("closewin")
    with open("./FtpGUI-Python-Configure.cache","w",encoding = "utf-8") as f:
        f.write(f"{ipe.get()}\n{pte.get()}\n{une.get()}\n{pwe.get()}")
    win.destroy()
    quitftp()
#退出服务器函数
def quitftp():
    try:
        ftp.quit()
    except:pass
    os._exit(0)


#双击下载文件函数
def dbfile(event = None):
    try:
        dbf = fsl.get(fsl.curselection())
    except:
        return
    try:
        ftp.cwd(dbf)
        fspe.delete(0,tk.END)
        fspe.insert(0,ftp.pwd())
        list_files()
    except:
        if messagebox.askyesno('双击下载文件-警告',message = f"确认下载该文件?\n{dbf}"):
            download_files(dbf)

#返回上一级函数
def cwdupdir(event = None):
    if not ftp:
        getinfo("文件上传:警告:请先连接到FTP服务器!")
        if smcvar.get():messagebox.showwarning("未连接", "请先连接到FTP服务器")
        down_upload_files = False
        return
    try:
        ftp.cwd("..")
        fspe.delete(0,tk.END)
        fspe.insert(0,ftp.pwd())
        list_files()
    except Exception as e:
        getinfo(str(e))
        if smcvar.get():messagebox.showwarning("返回上一级:Err",f"错误!\nErr:{str(e)}")
    
#重新打开路径函数
def reloaddir(event = None):
    getinfo(f"尝试转到该路径:{fspe.get()}")
    try:
        ftp.cwd(fspe.get())
        getinfo(f"转到路径{fspe.get()}成功!")
        list_files()
    except Exception as e:
        getinfo(f"转到路径{fspe.get()}失败!Err:{str(e)}")
        
#失去焦点重新加载路径函数
def foreloadfspe(event = None):
    fspe.delete(0,tk.END)
    fspe.insert(0,ftp.pwd())

# 创建主窗口
win = tk.Tk()
win.title("FTP客户端")
win.geometry("980x570")
win.resizable(0,0)
win.protocol('WM_DELETE_WINDOW',closewin)
#绘制组件
setFrame = tk.Frame(win)
fileFrame = tk.Frame(win)
#参数框
argf = ttk.LabelFrame(setFrame,text = "参数")

ipl = ttk.Label(argf,text = "服务器IP")
ipe = ttk.Entry(argf,width = 15)

ptl = ttk.Label(argf,text = "端口")
pte = ttk.Entry(argf,width = 15)

unl = ttk.Label(argf,text = "用户名")
une = ttk.Entry(argf,width = 13)

une = ttk.Entry(argf,width = 15)
#unt = tk.Message(argf,text = "需要显示的信息",width = 350)

pwl = ttk.Label(argf,text = "密码")
pwe = ttk.Entry(argf,show = "•",width = 9)
pwb = ttk.Button(argf,text = "显示",width = 4,command = depwshow)

argf.pack(pady = 5,fill = "both")

ipl.grid(row = 0,column = 0,padx = 4,pady = 4)
ipe.grid(row = 0,column = 1,padx = 4,pady = 4)
ptl.grid(row = 0,column = 2,padx = 4,pady = 1)
pte.grid(row = 0,column = 3,padx = 4,pady = 1,columnspan = 2)
unl.grid(row = 1,column = 0,padx = 4,pady = 1)
une.grid(row = 1,column = 1,padx = 4,pady = 4)
#unt.grid(row = 2,column = 0,padx = 4,pady = 4,columnspan = 4)
pwl.grid(row = 1,column = 2,padx = 4,pady = 4)
pwe.grid(row = 1,column = 3,padx = 4,pady = 4)
pwb.grid(row = 1,column = 4,padx = 4,pady = 4)
#导入原有参数
ipe.insert(0,cfglist[0])
pte.insert(0,cfglist[1])
une.insert(0,cfglist[2])
pwe.insert(0,cfglist[3])
#操作框
actf = ttk.LabelFrame(setFrame,text = "操作")

logins = ttk.Button(actf,text = "登录服务器",width = 10,command = connect_to_ftp)
unlogn = ttk.Button(actf,text = "退出程序",width = 10,command = quitftp)
upload = ttk.Button(actf,text = "上传文件...",width = 10,command = upload_files)
downld = ttk.Button(actf,text = "下载选中文件",width = 10,command = download_files)
delfle = ttk.Button(actf,text = "删除选中文件",width = 10,command = delfiles)
mkdirs = ttk.Button(actf,text = "创建文件夹",width = 10,command = mkdir)

actf.pack(pady = 5,fill = "both")

logins.grid(row = 0,column = 0,padx = 2,pady = 4)
unlogn.grid(row = 0,column = 1,padx = 2,pady = 4)
upload.grid(row = 0,column = 2,padx = 2,pady = 4)
downld.grid(row = 0,column = 3,padx = 2,pady = 4)
delfle.grid(row = 1,column = 0,padx = 2,pady = 4)
mkdirs.grid(row = 1,column = 1,padx = 2,pady = 4)

#配置显示
iif = ttk.LabelFrame(setFrame,text = "设置")

smcvar = tk.IntVar()
smc = ttk.Checkbutton(iif,text = "使用弹窗显示信息(关闭后请留意日志内文本)",variable = smcvar,onvalue = 1,offvalue = 0)

smc.grid(row = 0,column = 0,padx = 2,pady = 4)

iif.pack(pady = 5,fill = "both")

#日志显示
ccf = ttk.LabelFrame(setFrame,text = "日志")

cct = tk.Text(ccf,width = 45,state = "disabled",wrap = "none",height = 12)
ccsx = ttk.Scrollbar(ccf,command = cct.xview,orient = tk.HORIZONTAL)
ccsy = ttk.Scrollbar(ccf,command = cct.yview)
cct.config(xscrollcommand = ccsx.set,yscrollcommand = ccsy.set)
ccsx.pack(fill = "x",side = "bottom")
ccsy.pack(fill = "y",side = "right")
ccf.pack(pady = 5,fill = "x")

cct.pack(padx = 5,pady = 5,fill = "x")

#文件显示
fsf = ttk.LabelFrame(fileFrame,text = "文件列表")

fsbd = tk.Frame(fsf)
fsb = ttk.Button(fsbd,text = "刷新文件列表",command = list_files)
fsr = ttk.Button(fsbd,text = "返回上一级",command = cwdupdir)

fspl = ttk.Label(fsf,text = "当前目录")
fspe = ttk.Entry(fsf)

fspe.bind("<Return>",reloaddir)
fspe.bind("<FocusOut>",foreloadfspe)

fssf = ttk.Frame(fsf)
fsl = tk.Listbox(fssf,width = 54,selectmode = "extended")
fss = ttk.Scrollbar(fssf,command = fsl.yview)
fsl.config(yscrollcommand = fss.set)

fsl.bind("<F5>",list_files)
fsl.bind("<BackSpace>",cwdupdir)

fsbd.grid(row = 0,column = 0,pady = 1,sticky = "w",columnspan = 2)
fsb.pack(side = "left")
fsr.pack(side = "left")

fspl.grid(row = 1,column = 0,pady = 1,sticky = "w")
fspe.grid(row = 1,column = 1,pady = 1,sticky = "w",ipadx = 110)

fssf.grid(row = 2,column = 0,pady = 1,sticky = "w",columnspan = 2)
fss.pack(side = "right",fill = "y")
fsl.pack(padx = 3,pady = 1,side = "left")

fsl.bind("<Double-Button-1>",dbfile)

fsf.pack(pady = 5,fill = "both")

#下载进度显示
fdf = ttk.LabelFrame(fileFrame,text = "下载/上传进度")

fdp = ttk.Progressbar(fdf,length = 400)
fdl = ttk.Label(fdf,text = "-%    ")
fdn = tk.Message(fdf,text = "当前下载/上传文件:-",width = 450)

fdif = tk.Frame(fdf,bd = 1,relief = "groove")

fdal = ttk.Label(fdif,text = "文件大小:")
fddl = ttk.Label(fdif,text = "已完成大小:")
fdnl = ttk.Label(fdif,text = "未完成大小:")
fda = ttk.Label(fdif,text = "-")
fdd = ttk.Label(fdif,text = "-")
fdc = ttk.Label(fdif,text = "-")
fdad = ttk.Label(fdif,text = "KB")
fddd = ttk.Label(fdif,text = "KB")
fdnd = ttk.Label(fdif,text = "KB")

fdAl = ttk.Label(fdif,text = "总传输数:")
fdNl = ttk.Label(fdif,text = "未传输数:")
fdFl = ttk.Label(fdif,text = "无法传输:")
fdAv = ttk.Label(fdif,text = "-")
fdNv = ttk.Label(fdif,text = "-")
fdFv = ttk.Label(fdif,text = "-")
fdAt = ttk.Label(fdif,text = "个")
fdNt = ttk.Label(fdif,text = "个")
fdFt = ttk.Label(fdif,text = "个")

fdf.pack(pady = 5,fill = "both")

fdp.grid(row = 0,column = 0,padx = 10,pady = 4,columnspan = 2)
fdl.grid(row = 0,column = 2,padx = 4,pady = 4,sticky = "e")
fdn.grid(row = 1,column = 0,padx = 4,pady = 4,columnspan = 3,sticky = "nw")

fdif.grid(row = 2,column = 0,padx = 4,pady = 1,sticky = "w",ipadx = 4,columnspan = 3)

fdal.grid(row = 0,column = 0,padx = 4,pady = 1,sticky = "w")
fddl.grid(row = 1,column = 0,padx = 4,pady = 1,sticky = "w")
fdnl.grid(row = 2,column = 0,padx = 4,pady = 1,sticky = "w")
fda.grid(row = 0,column = 1,padx = 4,pady = 1,sticky = "e")
fdd.grid(row = 1,column = 1,padx = 4,pady = 1,sticky = "e")
fdc.grid(row = 2,column = 1,padx = 4,pady = 1,sticky = "e")
fdad.grid(row = 0,column = 2,padx = 4,pady = 1,sticky = "w")
fddd.grid(row = 1,column = 2,padx = 4,pady = 1,sticky = "w")
fdnd.grid(row = 2,column = 2,padx = 4,pady = 1,sticky = "w")

fdAl.grid(row = 0,column = 3,padx = 4,pady = 1,sticky = "w")
fdNl.grid(row = 1,column = 3,padx = 4,pady = 1,sticky = "w")
fdFl.grid(row = 2,column = 3,padx = 4,pady = 1,sticky = "w")
fdAv.grid(row = 0,column = 4,padx = 4,pady = 1,sticky = "e")
fdNv.grid(row = 1,column = 4,padx = 4,pady = 1,sticky = "e")
fdFv.grid(row = 2,column = 4,padx = 4,pady = 1,sticky = "e")
fdAt.grid(row = 0,column = 5,padx = 4,pady = 1,sticky = "w")
fdNt.grid(row = 1,column = 5,padx = 4,pady = 1,sticky = "w")
fdFt.grid(row = 2,column = 5,padx = 4,pady = 1,sticky = "w")

fdf.rowconfigure(1,minsize = 75,weight = 0)
fdif.columnconfigure(1,minsize = 140,weight = 0)
fdif.columnconfigure(4,minsize = 140,weight = 0)
#显示设置Frame
setFrame.pack(side = "left",fill = "y",padx = 4)
fileFrame.pack(side = "left",fill = "y",padx = 1)
tk.Label(fileFrame,text = "\n制作:ID\t\t注意事项:xx",fg = "gray",font = ("",7,"bold")).pack()

# 启动Tkinter的主循环
win.mainloop()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值