小程序支持多用户在线的FTP程序
Posted q1ang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小程序支持多用户在线的FTP程序相关的知识,希望对你有一定的参考价值。
功能:
1.用户加密认证;
2.允许同时多用户登陆;
3.每个用户有自己的家目录,并且只能访问在自己的家目录;
4.对用户进行磁盘配额,每个用户的可用空间不同;
5.允许用户在ftp server上随意切换目录;
6.允许用户查看当前目录上下文;
7.允许用户上传和下载文件,保证文件的一致性
8.文件传输过程中显示进度条;
客户端:
# Author:q1.ang import socket,os,json import hashlib import sys class FtpClient(object): def __init__(self): self.client = socket.socket() self.account=\'\' self.path=\'\' def help(self): \'\'\' 帮助 :return: none \'\'\' msg=\\ \'\'\'help: dir cd ../.. new .. del .. re .. put .. get ..\'\'\' print(msg) def connect(self,ip,port): \'\'\' 连接 :param ip: :param port: :return: \'\'\' self.client.connect((ip,port)) def authenticate(self): \'\'\' 用户登陆认证 :return:auth真假 \'\'\' auth=False try_count=0 while try_count<3: self.account=input(\'Account:\').strip() password=input(\'Password:\').strip() #获取账号密码的MD5值 m=hashlib.md5() m.update((self.account+password).encode(\'utf-8\')) #发送acc+MD5 self.client.send((self.account+\' \'+m.hexdigest()).encode(\'utf-8\')) #接收服务器的认证结果 auth_recv=json.loads(self.client.recv(1024).decode()) print(auth_recv[\'log res\']) if auth_recv[\'log res\']==\'login success\': auth=True print(\'desk size:\',auth_recv[\'desk size\']) return auth try_count+=1 if try_count==3: print(\'you have try 3 times...exit\') else: return auth def interactive(self): \'\'\' 交互模块,调用各个功能,在子功能中与服务器实现收发 :return:none \'\'\' if self.authenticate(): #认证 while True: cmd = input(\'>>>\').strip() if len(cmd)==0:continue cmd_str=cmd.split()[0] if hasattr(self,\'cmd_%s\'%cmd_str): func=getattr(self,\'cmd_%s\'%cmd_str) func(cmd) else: self.help() def cmd_dir(self,*args): \'\'\' 查看当面目录下文件 :param args: dir没使用 :return: none \'\'\' msg_dic = { \'action\':\'dir\' } self.client.send(json.dumps(msg_dic).encode(\'utf-8\')) print(self.account,\'list:\') server_respone = self.client.recv(1024).decode() for i in json.loads(server_respone): print(i) def cmd_cd(self,*args): \'\'\' 切换目录 :param args: cd 目录名 :return: none \'\'\' cmd_split=args[0].split() if len(cmd_split)>1: if cmd_split[1][0:len(self.account)]==self.account: self.path = \'\\\\\'+cmd_split[1][len(self.account)+1:] else: self.path += \'\\\\\'+cmd_split[1] msg_dic = { \'action\': \'cd\', \'path\':self.path } print(msg_dic) self.client.send(json.dumps(msg_dic).encode(\'utf-8\')) auth_path=self.client.recv(1024).decode() if auth_path== \'True\': print((self.account+\':\'+self.path).replace(\'\\\\\\\\\',\'\\\\\')) else: print(\'找不到指定的路径\') else: print(\'you not write the folder name...\') def cmd_new(self,*args): \'\'\' 新建目录 :param args: cmd :return: new 目录名 \'\'\' cmd_split=args[0].split() if len(cmd_split)>1: foldername=cmd_split[1] msg_dic={ \'action\':\'new\', \'foldername\':foldername } self.client.send(json.dumps(msg_dic).encode(\'utf-8\')) recv=self.client.recv(1024).decode() print(recv) else: print(\'no name of the new file...\') def cmd_del(self,*args): \'\'\' 删除目录或文件 :param args:del 文件名 :return: none \'\'\' cmd_split=args[0].split() if len(cmd_split)>1: msg_dic={ \'action\':\'del\', \'name\':cmd_split[1] } self.client.send(json.dumps(msg_dic).encode(\'utf-8\')) recv=self.client.recv(1024).decode() print(recv) else: print(\'no name of the file...\') def cmd_re(self,*args): \'\'\' 重命名 :param args: re 文件名 :return: none \'\'\' cmd_split=args[0].split() if len(cmd_split)>1: new_name=input(\'new name:\').strip() msg_dic={ \'action\':\'re\', \'name\':cmd_split[1], \'new name\':new_name } self.client.send(json.dumps(msg_dic).encode(\'utf-8\')) recv=self.client.recv(1024).decode() print(recv) else: print(\'the name of file or folder has not write..\') def cmd_put(self,*args): \'\'\' 上传文件 :param args: put 文件名 :return: none \'\'\' cmd_split=args[0].split() if len(cmd_split)>1: filename=cmd_split[1] if os.path.isfile(filename): filesize=os.stat(filename).st_size msg_dic = { \'action\': \'put\', \'filename\': filename, \'filesize\': filesize, \'override\': True } self.client.send(json.dumps(msg_dic).encode(\'utf-8\')) server_respone=self.client.recv(1024).decode() if server_respone==\'out of memory\': print(\'the mamory of your desk is not enough to save this file[%s(%d Mb)]\' % (filename, filesize / 1048576)) else: l=0 sys.stdout.write(\'0%\') sys.stdout.flush() with open(filename,\'rb\') as f: # f=open(filename,\'rb\') for line in f: self.client.send(line) l+=len(line) if l>(filesize/20): sys.stdout.write(\'>\') sys.stdout.flush() l-=(filesize/20) else: print(\'100%\\nfile upload success...\') save_sigle=self.client.recv(1024).decode() print(save_sigle) else: print(filename,\'is not exit\') def cmd_get(self,*args): \'\'\' 下载文件 :param args: get 文件名 :return: none \'\'\' print(\'get\') cmd_split=args[0].split() if len(cmd_split)>1: filename=cmd_split[1] msg_dic={ \'action\': \'get\', \'filename\':filename } self.client.send(json.dumps(msg_dic).encode(\'utf-8\')) auth_recv=json.loads(self.client.recv(1024).decode()) if auth_recv[\'file acc\']==\'file exist\': filesize=auth_recv[\'filesize\'] revc_size=0 l=0 sys.stdout.write(\'0%\') sys.stdout.flush() self.client.send(b\'200\') #防粘包 if os.path.isfile(filename): filename=filename+\'.new\' with open(filename,\'wb\')as f: while revc_size<filesize: recv=self.client.recv(1024) f.write(recv) revc_size+=len(recv) l+=len(recv) if l>(filesize/20): sys.stdout.write(\'>\') sys.stdout.flush() l-=(filesize/20) else: print(\'100%\\nreceive done\') else: print(auth_recv[\'file acc\']) else: print(\'error:not write filename...\') ftp=FtpClient() ftp.connect(\'localhost\',6666) ftp.interactive()
服务器端:
# Author:q1.ang import socketserver,json,os import hashlib class MyTCPHandler(socketserver.BaseRequestHandler): acc_path = \'\' account =\'\' available_size=0 data_dic={} def authenticate(self): \'\'\' 用户认证 :return: auth真假 \'\'\' auth=False try_count = 0 while try_count < 3: #接收客户端的账号密码 account,recv_md5=self.request.recv(1024).decode().split() print(account,recv_md5) BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) file_path=\'%s\\conf\\\\accounts\\%s\'%(BASE_DIR,account) if os.path.isfile(file_path): with open(file_path,\'r\') as f: # acc_data=json.load(f) self.data_dic=json.load(f) m=hashlib.md5() m.update((self.data_dic[\'account\']+self.data_dic[\'password\']).encode(\'utf-8\')) print(m.hexdigest()) if m.hexdigest()==recv_md5: print(\'login success\') self.account=account used_size = int(self.data_dic[\'desk size\'].split(\'/\')[0]) all_size = int(self.data_dic[\'desk size\'].split(\'/\')[1]) self.available_size = all_size - used_size print(\'available_size\',self.available_size) msg_dic = { \'log res\': \'login success\', \'desk size\': \'%dMb/%dMb\'%(used_size/1048576,all_size/1048576) } auth=True try_count=3 else: print(\'account or password error\') msg_dic={ \'log res\':\'account or password error\', } else: print(\'the account dose not exist..\') msg_dic = { \'log res\': \'the account dose not exist..\', } try_count+=1 self.request.send(json.dumps(msg_dic).encode(\'utf-8\')) return auth def cmd_dir(self,*args): \'\'\' 查看目录下文件 :param args: cmd_dic :return: none \'\'\' print(self.acc_path) print(os.listdir(self.acc_path)) self.request.send(json.dumps(os.listdir(self.acc_path)).encode(\'utf-8\')) def cmd_cd(self,*args): \'\'\' 切换目录 :param args: cmd_dic :return: none \'\'\' BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) self.acc_path = \'%s\\\\conf\\\\data\\\\%s\'%(BASE_DIR,self.account) auth_path=\'False_path\' #不能发空字节 path=self.acc_path+args[0][\'path\'] print(\'地址:\',path) if os.path.exists(path): self.acc_path=path auth_path=\'True\' print(\'path\',self.acc_path) else: print(\'找不到指定的路径\') self.request.send(auth_path.encode(\'utf-8\')) def cmd_new(self,*args): \'\'\' 在当前目录下新建目录 :param args: cmd_dic :return: none \'\'\' path=self.acc_path+\'\\\\\'+args[0][\'foldername\'] if os.path.exists(path): print(\'folder is exist..\') msg=\'folder is exist..\' else: os.mkdir(path) print(\'创建成功\') msg = \'folder create success..\' self.request.send(msg.encode(\'utf-8\')) def cmd_re(self,*args): \'\'\' 在当前目录下重命名文件 :param args: cmd_dic :return: none \'\'\' name = self.acc_path + \'\\\\\' + args[0][\'name\'] new_name=self.acc_path + \'\\\\\' + args[0][\'new name\'] try: os.rename(name,new_name) msg=\'rename success\' except FileNotFoundError as e: msg=\'the folder is not exist...\' print(e) self.request.send(msg.encode(\'utf-8\')) def cmd_del(self,*args): \'\'\' 在当前目录下删除目录(空)或文件 :param args: cmd_dic :return: none \'\'\' path = self.acc_path + \'\\\\\' + args[0][\'name\'] if os.path.isfile(path): os.remove(path) msg=\'del success\' elif os.path.exists(path): try: os.rmdir(path) msg = \'del success\' except OSError as e: print(e) msg=\'the folder is not empty...\' else: msg=\'file or folder is not exist...\' self.request.send(msg.encode(\'utf-8\')) def cmd_put(self,*args): \'\'\' 接收客户端文件 :return: \'\'\' cmd_dic=args[0] filename=cmd_dic[\'filename\'] filesize=cmd_dic[\'filesize\'] if filesize>self.available_size: self.request.send(\'out of memory\'.encode(\'utf-8\')) else: file_path=\'%s\\\\%s\'%(self.acc_path,filename) if os.path.isfile(file_path): f=open(file_path+\'.new\',\'wb\') else: f = open(file_path, \'wb\') self.request.send(b\'200 ok\') #防粘包 recv_size=0 while recv_size< filesize: data=self.request.recv(1024) f.write(data) recv_size+=len(data) else: f.close() print(\'file [%s] has uploaded\'%filename) self.available_size -= filesize print(\'available_size2\',self.available_size) self.request.send((\'server save success\\navailable size:\' \'%d Mb\'%(self.available_size/1048576)).encode(\'utf-8\')) #更新用户数据 used_size=self.data_dic[\'desk size\'].split(\'/\')[0] all_size=self.data_dic[\'desk size\'].split(\'/\')[1] print(\'filesize\',filesize) print(\'used_size\',used_size) print(\'all_size\',all_size) self.data_dic[\'desk size\']=\'%d/%s\'%(int(used_size)+filesize,all_size) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) file_path = \'%s\\conf\\\\accounts\\%s\' % (BASE_DIR, self.account) print(file_path) with open(file_path,\'w\') as f: json.dump(self.data_dic,f) def cmd_get(self,*args): \'\'\' 发送给客户端文件 :param args: cmd_dic :return: none \'\'\' cmd_dic=args[0] filename=cmd_dic[\'filename\'] file_path=\'%s\\\\%s\'%(self.acc_path,filename) if os.path.isfile(file_path): filesize=os.stat(file_path).st_size msg_dic={ \'file acc\':\'file exist\', \'filesize\':filesize } self.request.send(json.dumps(msg_dic).encode(\'utf-8\')) self.request.recv(1024) #防粘包 with open(file_path,\'rb\')as f: for line in f: self.request.send(line) else: print(\'file send done\') else: msg_dic = {\'file acc\': \'file[%s] is not exist...\'%filename} self.request.send(json.loads(msg_dic).encode(\'utf-8\')) def handle(self): \'\'\' 重构socketserver的handle(),用来主要的交互 :return: \'\'\' print(\'以上是关于小程序支持多用户在线的FTP程序的主要内容,如果未能解决你的问题,请参考以下文章