要求:
1. 用户加密认证
2. 多用户同时登陆
3. 每个用户有自己的家目录且只能访问自己的家目录
4. 对用户进行磁盘配额、不同用户配额可不同
5. 用户可以登陆server后,可切换目录
6. 查看当前目录下文件
7. 上传下载文件,保证文件一致性
8. 传输过程中现实进度条
9. 支持断点续传
路径如下
代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
import socket import pickle import hashlib import sys import time import os A = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) class Ftp_client(object): def __init__(self): self.client = socket.socket() def help(self): ''' 帮助说明 :return: ''' print('''请输入正确的指令: ls: 查看根目录下文件 cd: 切换目录 download: 下载文件 upload:上文件 mkdir:新建立文件夹 ''') def connet(self, ip, port): ''' 链接服务器 :param ip: :param port: :return: ''' self.client.connect((ip, port)) data = self.client.recv(1024) print(data.decode()) self.main() self.ftp_main() def login(self): ''' 登录 :return: ''' name = input('请输入姓名').lower() password = input('请输入密码') dict = {'name': name, 'password': password} self.client.sendall(pickle.dumps(dict)) data = self.client.recv(1024) print(data.decode()) if data.decode()=='输入有误': return False def register(self): ''' 注册 :return: ''' while True: a = input('请输入注册哪种用户: 1: 普通用户(可用空间10M), 2: VIP用户(可用30M)') if a =='1': space = 10485760 break elif a== '2': space = 31457280 break else: print('输入有误') continue name = input('请输入姓名').lower() pd = input('请输入密码') dict = {'name': name, 'password': pd, 'space': space} self.client.sendall(pickle.dumps(dict)) data = self.client.recv(1024) print(data.decode()) if data.decode()== '用户名已存在,请重新输入': return False def main(self): while True: a = input('请输入 1. 用户登录 2. 用户注册 3.退出') if a == '1': self.client.sendall('login'.encode()) res = self.login() elif a == '2': self.client.sendall('register'.encode()) res = self.register() elif a == '3': exit() else: print('输入有误') continue if res is False: continue else: break def download(self): ''' 下载 :return: ''' while True: data = self.client.recv(1024) choose = input('文件所在路径 1 根目录 2 子目录') if choose == '1': path = '1' break elif choose =='2': path = input('请输入路径,子路径用 / 分隔隔开') # 根目录不用输入 break else: print('输入有误') continue self.client.sendall(path.encode()) data = self.client.recv(1024) filename = input('请输入下载文件名') self.client.sendall(filename.encode()) size = self.client.recv(1024).decode() if size == '该文件不存在': print ('该文件不存在') return False else: size = int(size) if os.path.exists(os.path.join(A, 'db', filename)): r_size = int(os.path.getsize(os.path.join(A, 'db', filename)))#存在文件的size while True: choose = input('文件已存在, 1 重新下载 2 停止下载 3 新起名再下载 4 继续下载') if choose == '2': dic={} dic['choose'] = choose self.client.sendall(pickle.dumps(dic)) return False elif choose == '1': f = open(os.path.join(A, 'db',filename),'wb') r_size = 0 break elif choose == '3': name = input('请输入新文件名') f = open(os.path.join(A, 'db',name),'wb') r_size = 0 break elif choose == '4': f = open(os.path.join(A, 'db',filename),'ab') break else: print('输入有误,请重新输入') dic={} dic['choose'] = choose dic['size'] = r_size self.client.sendall(pickle.dumps(dic)) else: r_size = 0 f = open(os.path.join(A, 'db', filename),'xb') if size == 0: f.close() print('接收完成') else: while r_size < size: file = self.client.recv(1024) f.write(file) f.flush() #文件强行写入file,预防中途中断 r_size += len(file) view_bar(r_size, size) time.sleep(0.1) else: print('接收完成') f.close() def upload(self): filename = input('请输入上传的文件名') if os.path.exists(os.path.join(A, 'db', filename)): size = os.path.getsize(os.path.join(A, 'db', filename)) #文件size path = input('请输入上传的路径,子路径用 / 分隔隔开, h为根目录') dic = {} dic['filename'] = filename dic['size'] = size dic['path'] = path self.client.sendall(pickle.dumps(dic)) f = open(os.path.join(A, 'db', filename), 'rb') else: print ('此文件不存在') return False data = self.client.recv(1024) ls = pickle.loads(data) #ls[2]: ; if ls[-1]=='1': #ls[-1]:检查下载的路径是否存在 print ('此路径不存在') return False if ls[0] == '0':#ls[0]:检查空间够不够; print ('空间不足,不能上传') else: if ls[1] == '0': #ls[1]:检查下载是否存在 while True: a = input('文件已存在, 1 重新下载 2 停止下载 3 新起名再下载 4 继续下载') f_ls = [] f_ls.append(a) if a == '1': break elif a == '2': return False elif a =='3': f_name = input('请输入新文件名') f_ls.append(f_name) break elif a=='4': l_size = ls[2] #ls[2]:已下载的文件大小 f.seek(l_size) #把指针放到已下载的地方,继续下载 break else: print ('输入有误') else: f_ls = [] f_ls.append('5') # 5:下载文件不存在 self.client.sendall(pickle.dumps(f_ls)) data = self.client.recv(1024).decode() print (data) for line in f: self.client.sendall(line) num = f.tell() #查看文件上传位置 view_bar(num, size) time.sleep(0.1) f.close() print ('接收完成') return False def ls(self): data = self.client.recv(1024) if data =='0'.encode(): print('此目录为空') elif data =='1'.encode(): print('此文件不存在') else: ls = pickle.loads(data) print('此文件里有:') for i in ls: print(i) def mkdir(self): name = input('请输入文件夹名') self.client.sendall(name.encode()) data = self.client.recv(1024).decode() print(data) def cd(self): name = input('请输入路径,子路径用 / 分隔隔开') # 根目录不用输入 self.client.sendall(name.encode()) path = self.client.recv(1024).decode() if path == '0': print ('此目录不存在') return False else: print ('所在文件夹路径为 %s' % path) self.client.sendall('ok'.encode()) data = self.client.recv(1024) if data =='0'.encode(): print('此目录为空') else: ls = pickle.loads(data) print('此文件里有:') for i in ls: print(i) def ftp_main(self): while True: a = input('请输入相应的指令, help:查询, exit:退出') if hasattr(self, a): self.client.sendall(a.encode()) func = getattr(self, a) func() elif a == 'exit': exit() else: self.help() continue def view_bar(num, total): '''进度条''' rate = float(num) / float(total) rate_num = int(rate * 100) r = '\r%d%%' % (rate_num, ) #\r 回到到开头 sys.stdout.write(r) sys.stdout.flush() #删除记录 ftp = Ftp_client() ftp.connet('localhost', 9999)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
import socketserver import pickle import os import time A = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) class MyTCPHandler(socketserver.BaseRequestHandler): def handle(self): while True: try: self.request.sendall('链接成功'.encode()) while True: data = self.request.recv(1024).decode() if data =='login': a = self.login() else: a = self.register() #a为 list,[0]是name,[1]是可用空间 if a is False: continue else: while True: data = self.request.recv(1024) func = getattr(self, data.decode()) func(a) except Exception: #检查客户端是否连接正常 print('客户端断开') break def login(self): db_dict = pickle.load(open(os.path.join(A, 'db', 'register'),'rb')) self.data = self.request.recv(1024) dict = pickle.loads(self.data) if dict['name'] in db_dict: if dict['password']==db_dict[dict['name']][0]: self.request.sendall('登录成功'.encode()) return [dict['name'], db_dict[dict['name']][1]] #返回用户名,用户可用空间 else: self.request.sendall('输入有误'.encode()) return False else: self.request.sendall('输入有误'.encode()) return False def register(self): dict = pickle.loads(self.request.recv(1024)) print (dict) if os.path.exists(os.path.join(A, 'db', dict['name'])): self.request.sendall('用户名已存在,请重新输入'.encode()) return False else: self.request.sendall('注册成功'.encode()) os.makedirs(os.path.join(A, 'db', dict['name'])) # n_dict ={} n_dict = pickle.load(open(os.path.join(A, 'db', 'register'), 'rb')) print (1) print (n_dict) n_dict[dict['name']]=[dict['password'],int(dict['space'])] #存储格式为{姓名:[密码,可用空间大少]} print (n_dict) pickle.dump(n_dict,open(os.path.join(A, 'db', 'register'), 'wb')) return [dict['name'], int(dict['space'])] def help(self, a): return False def upload(self, list): name = list[0] b_path = os.path.join(A, 'db', name) #自己的根目录 h_size = int(list[1]) #自己可用的空间大小 data = self.request.recv(1024) dic = pickle.loads(data) f_size = int(dic['size']) #上传文件大小 filename = dic['filename'] path = dic['path'] s_ls = [] if h_size < f_size: a = '0' #空间不足 s_ls.append(a) else: a = '1' s_ls.append(a) if path=='h': #存在根目录 l_path =os.path.join(b_path,filename) else: res = path.split('/') print (res) for i in res: b_path = os.path.join(b_path, i) #合拼成子目录 l_path = os.path.join(b_path,filename) #文件路径 if os.path.exists(l_path): b = '0' #文件已存在 file_size = os.path.getsize(l_path) s_ls.append(b) s_ls.append(file_size) else: b = '1' s_ls.append(b) if os.path.exists(b_path): c = '0' else: c='1'#文件夹不存在,可以结束 s_ls.append(c) self.request.sendall(pickle.dumps(s_ls)) return False s_ls.append(c) self.request.sendall(pickle.dumps(s_ls)) f_ls = pickle.loads(self.request.recv(1024))#文件以什么方式打开 self.request.sendall('准备开始'.encode()) if f_ls[0] =='1': f = open(l_path,'wb') file_size = 0 elif f_ls[0]=='2': return False elif f_ls[0]=='3':#文件名另起 filename = f_ls[1] l_path = os.path.join(b_path,filename) f = open(l_path,'wb') file_size = 0 elif f_ls[0]=='4': f = open(l_path,'ab') else: f = open(l_path,'xb') file_size = 0 if f_size == 0: f.close() return False else: while file_size< f_size: line = self.request.recv(1024) f.write(line) f.flush() file_size += len(line) else: f.close() l_dict = pickle.load(open(os.path.join(A, 'db', 'register'), 'rb')) l_dict[name][1] = h_size - f_size #修改已用空间 pickle.dump(l_dict, open(os.path.join(A, 'db', 'register'), 'wb')) return False def download(self, list): self.request.sendall('ok'.encode()) path = self.request.recv(1024).decode() #检查文件存在子目录或根目录 self.request.sendall('check'.encode()) filename = self.request.recv(1024).decode() name = list[0] if path == '1': l_path = os.path.join(A, 'db', name,filename) else: data = path.split('/') pathname = os.path.join(A, 'db', name) for i in data: pathname = os.path.join(pathname, i) #合拼子目录 l_path = os.path.join(pathname,filename) print (l_path) if os.path.exists(l_path): f = open(l_path, 'rb') size = os.path.getsize(l_path) #检查文件 self.request.sendall(str(size).encode()) #要以字符串格式传数字 data = self.request.recv(1024) dic = pickle.loads(data) if dic['choose']=='2': return False elif dic['choose']=='4': f.seek(int(dic['size'])) #把指针定位到已下载的地方 for line in f: self.request.sendall(line) f.close() print ('done') else: self.request.sendall('该文件不存在'.encode()) def ls(self, list): name = list[0] ls = os.listdir(os.path.join(A, 'db', name)) if len(ls)==0: self.request.sendall('0'.encode()) else: a = [] for i in ls: a.append(i) #把存在的文件放入list self.request.sendall(pickle.dumps(a)) def cd(self, list): data = self.request.recv(1024).decode() name = list[0] path = os.path.join(A, 'db', name) #根目录 path_ls = data.split('/') for i in path_ls: path = os.path.join(path, i) #合拼子目录 print (path) if os.path.exists(path) is False: print (1) path = '0' self.request.sendall(path.encode()) return False ls = os.listdir(path) self.request.sendall(path.encode()) data = self.request.recv(1024) if len(ls)==0: self.request.sendall('0'.encode()) else: a = [] for i in ls: a.append(i) self.request.sendall(pickle.dumps(a)) def mkdir(self, a): filename = self.request.recv(1024).decode() name = a[0] if os.path.exists(os.path.join(A, 'db', name,filename)): #检查路径是否存在 self.request.sendall('文件夹已存在'.encode()) else: os.makedirs(os.path.join(A, 'db', name, filename)) self.request.sendall('已建好'.encode()) host, port = 'localhost', 9999 server = socketserver.ThreadingTCPServer((host, port), MyTCPHandler) server.serve_forever()