要求:
1. 用户加密认证
2. 多用户同时登陆
3. 每个用户有自己的家目录且只能访问自己的家目录
4. 对用户进行磁盘配额、不同用户配额可不同
5. 用户可以登陆 server 后,可切换目录
6. 查看当前目录下文件
7. 上传下载文件,保证文件一致性
8. 传输过程中现实进度条
9. 支持断点续传
路径如下
代码
client
- 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)
server
- 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()
来源: http://www.bubuko.com/infodetail-1980336.html