Python案例-网络编程-FTP&断点续传&进度条&上传下载

来源:互联网 发布:js设置fontsize 编辑:程序博客网 时间:2024/06/03 18:46

本次ftpV2,完善了上次未完成的代码,实现上传下载,断点续传,客户端任务进度条等功能
废话不多说,上代码,详细解释,请查看代码注释

Server端

#!/usr/bin/env python#   --coding = utf-8#   Author Allen Leeimport socketserver,json,osimport subprocess#创建ftpserver类,并继承socktserver,借用其io多路复用的select方法,以实现多响应用户的并发class Myftp(socketserver.BaseRequestHandler):    #再次重写handle方法,由于类的继承中,self遵循自下而上,从左到右的寻址规则,因此被重写的方法将优先被调用    def handle(self):        #权限控制        while True:            if not self.checkuser():                break            else:                self.request.sendall(bytes('welcome to myftp',encoding='utf-8'))            while True:                res_data = self.request.recv(1024).strip()                if not res_data:                    break                #再次统一处理client端发出的json包,                res_msg = json.loads(str(res_data,encoding='utf-8'))                #通过反射来引导用户到对应的方法中                res_tag = res_msg.get('action')                if hasattr(self,res_tag):                    getattr(self,res_tag)(res_msg)                else:break            self.request.close()    #用户验证    def checkuser(self):        server_tag = False        res = self.request.recv(1024)        if len(res) == 0:            return error        userinfo = json.loads(str(res,encoding='utf-8'))        if userinfo[0].get('username') == 'username' and userinfo[0].get('password') == 'password':            server_tag = True            return True        else:            return False    def put(self,res_msg):        file_name = res_msg.get('file_name')        file_size = res_msg.get('file_size')        ret = os.path.isfile(file_name)        if not ret:            #此为正常传输            with open(file_name,'wb+')as f:                self.request.sendall(bytes('ok',encoding='utf-8'))                recv_data = self.request.recv(1024)                f.write(recv_data)        else:            #此为断点续传处理            with open(file_name,'ab+')as f:                file_newsize = len(file_name)                self.request.sendall(bytes('continue|%s'%(file_newsize), encoding='utf-8'))                recv_data = self.request.recv(1024)                f.write(recv_data)    def get(self,res_msg):        file_name = res_mas.get('file_name')        ret = os.path.isfile(file_name)        if not ret:            print('this file is not exists')            return False        else:            total_size =os.stat(file_name).st_size            with open(file_name,'rb')as f:                self.request.sendall(bytes('ready|%d'%(total_size),encoding='utf-8'))                reg = str(self.request.recv(1024),encoding='utf-8')                if reg.startswith('ok'):                    new_size = 0                    for line in f:                            client.sendall(bytes(line,encoding='utf-8'))                            new_size += len(line)                            if new_size >= total_size:break                if reg.startswith('continue'):                    new_size = reg.split('|')[1]                    #使用seek方法来实现将文件指针放在上次断点处,并继续传输,进而完成断点续传的功能,第一个参数表示指针偏移位数,第二个参数表示,从文件开头开偏移                    f.seek(new_size,0)                    for line in f:                        client.sendall(bytes(line,encoding='utf-8'))                        new_size +=len(line)                        if new_size >= total_size:break    #次方法为处理寻常请求,用户可以在这里实现切换目录、删除文件、创建目录、等等    def cmd(self,res_msg):        cmd = res_msg.get("cmd")        result = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE)        ret = result.stdout.read()        if not ret:            send_data = 'there is no such commons'            print(send_data)        else:            send_data = bytes(ret,encoding='utf-8')        self.request.sendall('ready|%s'%(len(send_data)))        ret = str(self.request.recv(1024),encoding=utf-8)        if ret == 'start':            self.request.sendall(send_data)if __name__ == '__main__':    #创建对象时使用ThreadindTCP,来实现多线程的处理    ftpserver = socketserver.ThreadingTCPServer(('127.0.0.1', 9909), Myftp)    ftpserver.serve_forever()

Client端

#!/usr/bin/env python#   --coding = utf-8#   Author Allen Leeimport socket, os, json,sysclass Myclient():    #在构造方法中启动client的socket对象以及连接server端    def __init__(self):        self.client = socket.socket()        self.client.connect(('127.0.0.1', 9909))    #run为client的主方法,其中通过对用户输入的命令来判断具体操作,并通过反射将其引导到对应的方法    def run(self):        while True:            #权限控制            check = self.login()            #通过while循环实现用户输入代码的复用            while True:                res_data = self.client.recv(1024).strip()                if not res_data:break                print(str(res_data, encoding='utf-8'))                send_data = input('>> : ').strip()                if not send_data:                    continue                if hasattr(self,send_data):                    getattr(self,send_data)(send_data)                #当用户输入的不是指定的put  get方法时,将被引致平常的cmd命令行,在此可以完成切换目录、创建文件、删除文件                else:                    getattr(self,'cmd')(send_data)            self.client.close()    #用户登录验证    def login(self):        self.tag = False        for i in range(3):            username = input('please input your username:')            passwd = input('please input your passwd:')            password = commons.md5(passwd)            userinfo = {"username":username,"password":password }            self.client.send(bytes(json.dumps(userinfo),encoding='utf-8'))            ret = self.client.recv(1024)            res_check = str(ret,encoding='utf-8')            if res_check == 'ok':                self.tag = True                return True            else:                return False            print("your username or passwd is wrong")            self.client.close()    def put(self,send_data):            pathh = input('input the file `s path ;')            #首先判断文件是否存在,通过os.path.isfile来实现            ret = os.path.isfile(pathh)            if not ret:                print('the file is not exists')                return False            #如果存在,则将操作类型、文件名、文件大小以字典形势格式化,再以json的方法进行格式化            else:                #通过os.stat方法来取文件的大小                file_size = os.stat(pathh).st_size                file_name = pathh.split('\\')[-1]                send_msg = {"action":send_data,                            "filename":file_name,                            "filesize":file_size,                }                self.client.sendall(bytes(json.dumps(send_msg),encoding='utf-8'))                res_tag = self.client.recv(1024)                if not res_tag:                    return False                if str(res_tag,encoding='utf-8').startswith('ok'):                    #上传文件只用r就够了                    with open(pathh,'rb') as f:                        new_size = 0                        for line in f:                            self.client.sendall(bytes(line,encoding='utf-8'))                            new_size += len(line)                            #以下为实现传输文件时,显示当前进度条                            ret = new_size/file_size                            num = int(ret*100)                            view = '\r [%-100s]%d%%'%("="*num,100,)                            sys.stdout.write(view)                            sys.stdout.flush()                            if new_size >= file_size:                                break                if str(res_tag,encoding='utf-8').startswith('continue'):                    with open(pathh, 'rb') as f:                        #此处使用seek来进行文件指针的偏移,进而完成断点续传的功能                        file_newsize=str(res_tag,encoding='utf-8').split('|')[1]                        f.seek(file_name,0)                        for line in f:                            self.client.sendall(bytes(line,encoding='utf-8'))                            new_size += len(line)                            #以下为实现传输文件时,显示当前进度条                            ret = new_size/file_size                            num = int(ret*100)                            #以下使用了sys模块的标准输出                            view = '\r [%-100s]%d%%'%("="*num,100,)                            sys.stdout.write(view)                            sys.stdout.flush()                            if new_size >= file_size:                                break    def get(self,send_data):            get_name = input('input what your want:').strip()            if not get_name:                return False            send_msg = {"action":send_data,                        "filename":get_name                        }            self.client.sendall(bytes(json.dumps(send_msg),encoding='utf-8'))            res_data = self.client.recv(1024)            res_tag = str(res_data,encoding='utf-8')            #客户端接收到服务端的确认信息ready之后开始传输,相当于tcp的三次握手中的ack包            if res_tag.startswith('ready'):                #通过判断本地是否有此文件,来确认是否启用断点续传                if not os.path.isfile(get_name):                    with open(file_name,'wb')as f:                        self.client.sendall(bytes('start', encoding='utf-8'))                        total_size = res_data.split('|')[1]                        new_size = 0                        while new_size <total_size:                            res_data = self.client.recv(1024)                            new_size += len(res_data)                            f.write(res_data)                            #以下为下载文件的进度条实现                            ret = new_size/total_size                            num = int(ret*100)                            view = '\r [%-100s]%d%%'%('='*num,100)                            sys.stdout.write(view)                            sys.stdout.flush()                else:                    #如果本地不存在此文件将给服务端发送,断点续传的请求标签                    with open(file_name,'ab')as f:                        new_size = os.stat(file_name).st_size                        self.client.sendall(bytes('continue|%d'%(new_size), encoding='utf-8'))                        total_size = res_data.split('|')[1]                        while new_size <total_size:                            res_data = self.client.recv(1024)                            new_size += len(res_data)                            f.write(res_data)                            #以下为下载文件的进度条实现                            ret = new_size/total_size                            num = int(ret*100)                            view = '\r [%-100s]%d%%'%('='*num,100)                            sys.stdout.write(view)                            sys.stdout.flush()    def cmd(self,send_data):            send_msg = {"action":'cmd',                        "cmd":send_data,                        }            self.client.sendall(bytes(json.dumps(send_msg), encoding='utf-8'))            res_data = self.client.recv(1024)            res_tag = str(res_data, encoding='utf-8')            if res_tag.startswith('ready'):                self.client.sendall(bytes('start', encoding='utf-8'))                #以下为解决发送命令过程中出现的粘报现象,思路为提前告诉对方总大小                total_size = res_data.split('|')[1]                new_size = 0                while new_size < total_size:                    res_data = self.client.recv(1024)                    new_size += len(res_data)                    print(str(res_data, encoding='utf-8'))    def client_exit(self):        self.client.close()if __name__ == '__main__':    Myclient.run()

具体一些bug还未调试,主题代码思路已经实现

0 0