写在前面:笔记还是自己整理的好!以后再自己整理属于自己的笔记
项目(思路)分析:
''' 我的网盘 程序加载……(读取用户json数据、先启动服务端、再启动客户端) 客户端作为显示页面 输入账号密码进行登录,并显示当前目录下文件(夹)列表 下载---get a.mp4 服务端检测文件是否存在名,并返回(是否存在、【文件大小、文件名称、md5值】) 客户端检测本地储存空间是否充足,充足则下载并检验md5值,【显示进度条】 否则提示空间不足 上传---set b.mp4 客户端发送文件信息【文件大小、文件名称、md5值】 服务端检测储存空间是否充足,充足则接收上传,并检验md5值,【显示进度条】 否则提示空间不足 显示当前目录下的文件列表 %¥……&& 退出当前账号---exit 登录第二个账号 。。。 管理员页面 新建账号 删除账号 管理账号 更改储存空间大小 【存储信息】 '''
具体实现步骤:
服务端(基于面向对象写的):
1 import socket 2 import json 3 import hashlib 4 import os 5 import struct 6 import sys 7 sys.path.insert(0, '..') 8 from conf import settings 9 10 class Myserver: 11 12 host = settings.get_default_conf()[0] 13 port = settings.get_default_conf()[1] 14 listen_num = settings.get_default_conf()[2] 15 code = settings.get_default_conf()[3] 16 # login_name = None 17 18 def __init__(self,host=host,port=port,listen_num=listen_num): 19 self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 20 self.socket.bind((host, port)) 21 self.socket.listen(listen_num) 22 23 def run(self): 24 user_dict = self.__data_init() 25 print('服务端初始化成功!') 26 conn, addr = self.socket.accept() 27 print('客户端:', addr) 28 while True: 29 login_state = self.__login(conn,user_dict) 30 if login_state: 31 print('%s已登录' % login_state) 32 user_path = '\\'.join(os.getcwd().split('\\')[:-1]) + '\\db\\%s' % login_state 33 size = self.__readuserinfo(login_state,user_dict,user_path) 34 conn.send(self.__tobytes(size)) 35 self.__up_down(conn,user_path,user_dict[login_state]) 36 print('当前用户已退出登录') 37 38 def __readuserinfo(self,user_name,user_dict,now_path): 39 """读取用户信息,返回剩余空间大小跟文件列表""" 40 if os.path.exists(now_path): 41 size = user_dict[user_name]['capacity'] - (self.__getfoldersize(now_path) / 1024 / 1024) 42 file_list = os.listdir(now_path) 43 return {'size':size,'file_list':file_list} 44 else: 45 '''创建用户文件,文件目录返回空列表''' 46 os.makedirs(now_path) 47 return {'size':user_dict[user_name]['capacity'],'file_list':[]} 48 49 def __up_down(self,conn,user_path,user_dict): 50 while True: 51 cmd = conn.recv(1024).decode(self.code) 52 cmd = json.loads(cmd) 53 print(cmd,type(cmd)) 54 if cmd[0] == 'get': 55 #注意粘包问题 56 file_path = user_path+'\\'+cmd[1] 57 if os.path.isfile(file_path): 58 print('文件存在,读取文件信息') 59 conn.send(b'1') 60 file_dict = {'name':cmd[1],'md5':self.__getfilemd5(file_path),'size':os.path.getsize(file_path)} 61 print(file_dict) 62 file_bytes = json.dumps(file_dict).encode(self.code) 63 conn.send(struct.pack('i',len(file_bytes))) 64 conn.send(file_bytes) 65 with open(file_path,'rb') as f: 66 recv_size = 0 67 recv_data = b'' 68 while recv_size < file_dict['size']: 69 res = f.read(1024) 70 conn.send(res) 71 recv_data += res 72 recv_size += len(res) 73 print('发送成功!') 74 continue 75 else: 76 print('文件不存在') 77 conn.send(b'0') 78 elif cmd[0] == 'set': 79 print('上传命令') 80 obj = conn.recv(4) 81 file_dict = json.loads(conn.recv(struct.unpack('i',obj)[0]).decode(self.code)) 82 if file_dict['size'] < user_dict['capacity']*1024**2: 83 conn.send(b'1') 84 file_path = user_path + '\\' + file_dict['name'] 85 with open(file_path,'wb') as f: 86 recv_size = 0 87 recv_data = b'' 88 while recv_size < file_dict['size']: 89 res = conn.recv(1024) 90 recv_data += res 91 recv_size += len(res) 92 f.write(recv_data) 93 if file_dict['md5'] == self.__getfilemd5(file_path): 94 conn.send(b'1') 95 else: 96 os.remove(file_path) 97 conn.send(b'0') 98 else: 99 conn.send(b'0') 100 elif cmd[0] == 'exit': 101 print('断开连接') 102 return 103 104 @staticmethod 105 def __md5_pwd(pwd): 106 m = hashlib.md5() 107 m.update(pwd.encode(encoding='utf-8')) 108 return m.hexdigest() 109 110 @staticmethod 111 def __getfilemd5(file_path): 112 m = hashlib.md5() 113 with open(file_path,'rb') as f: 114 m.update(f.read()) 115 return m.hexdigest() 116 117 @staticmethod 118 def __data_init(): 119 with open('../db/data.json', 'r', encoding='utf-8') as f: 120 user_dict = json.load(f) 121 return user_dict 122 123 def __login(self,conn,user_dict): 124 error_n = 0 125 while error_n<3: 126 cmd = conn.recv(1024) 127 if len(cmd) == 0: break 128 cmd = json.loads(cmd.decode('utf-8')) 129 if cmd[0] in user_dict.keys(): 130 if self.__md5_pwd(cmd[1]) == user_dict[cmd[0]]['pwd']: 131 conn.send('1'.encode('utf-8')) 132 return cmd[0] 133 error_n += 1 134 conn.send('0'.encode('utf-8')) 135 return False 136 137 @staticmethod 138 def __getfoldersize(filepath, size=0): 139 """获取文件夹的大小""" 140 for root, dirs, files in os.walk(filepath): 141 for f in files: 142 size += os.path.getsize(os.path.join(root, f)) 143 return size 144 145 def __tobytes(self,obj): 146 s = json.dumps(obj) 147 return s.encode(self.code) 148 149 150 server = Myserver() 151 server.run()
客户端(基于函数写的...):
1 import socket 2 import json 3 import struct 4 import hashlib 5 import sys 6 import os 7 sys.path.insert(0, '..') 8 from conf import settings 9 10 HOST = settings.get_default_conf()[0] 11 PORT = settings.get_default_conf()[1] 12 code = settings.get_default_conf()[3] 13 14 def check_md5(file_path): 15 m = hashlib.md5() 16 with open(file_path,'rb') as f: 17 m.update(f.read()) 18 return m.hexdigest() 19 20 def progress_bar(new_n,all_n,num=50): 21 # if new_n.isalpha(): 22 # new_n = int(new_n) 23 # if all_n.isalpha(): 24 # all_n = int(all_n) 25 r = int(new_n / all_n * num) 26 l = num - r 27 w = new_n / all_n * 100 28 s = '[' + '>' * r + '-' * l + ']' + ' %.2f' % w + '%' 29 sys.stdout.write('\r') 30 sys.stdout.write(s) 31 sys.stdout.flush() 32 33 def getnowpath(user_name): 34 now_path = os.getcwd().split('\\')[:-1] 35 now_path = '\\'.join(now_path) + '\\downloads\\%s' % user_name 36 return now_path 37 def run(): 38 client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 39 client.connect((HOST, PORT)) 40 while True: 41 user_name = input('name>>').strip() 42 user_pwd = input('password>>').strip() 43 if user_name == '' or user_pwd == '': 44 print('账号或者密码,不能为空') 45 continue 46 cmd = [user_name,user_pwd] 47 cmd = json.dumps(cmd) 48 client.send(cmd.encode(code)) 49 res = client.recv(1024) 50 if res.decode(code) == '1': 51 print('登录成功!') 52 if not os.path.isdir(getnowpath(user_name)): 53 os.makedirs(getnowpath(user_name)) 54 size = client.recv(1024).decode(code) 55 size = json.loads(size) 56 print('当前容量:%.2fMB\n' 57 '网盘文件列表:%s' %(size['size'],[x for x in size['file_list']])) 58 print('本地文件列表(downloads/%s):%s' %(user_name,[x for x in os.listdir(getnowpath(user_name))])) 59 while True: 60 cmd = input('操作指令(exit-退出)>>').strip().split() 61 if len(cmd) != 2: 62 print('指令输入有误!') 63 continue 64 if cmd[0] == 'get': 65 print('发送下载指令') 66 client.send(json.dumps(cmd).encode(code)) 67 if client.recv(1) == b'1': 68 obj = client.recv(4) 69 headers_size = struct.unpack('i',obj)[0] 70 headers_bytes = client.recv(headers_size) 71 headers_dict = json.loads(headers_bytes.decode(code)) 72 with open('%s\\%s'%(getnowpath(user_name),headers_dict['name']),'wb') as f: #这里可以优化:边下载边写入,思路:添加一个判断文件是否存在 73 recv_size = 0 74 recv_data = b'' 75 while recv_size < headers_dict['size']: 76 res = client.recv(1024) 77 recv_data+=res 78 recv_size+=len(res) 79 # print(recv_size, headers_dict['size']) 80 progress_bar(recv_size, headers_dict['size']) 81 print('\n正在写入文件……') 82 f.write(recv_data) 83 if headers_dict['md5'] == check_md5(getnowpath(user_name)+'\\'+headers_dict['name']): 84 print('MD5校验成功,下载成功!') 85 else: 86 print('文件不存在!') 87 continue 88 elif cmd[0] == 'set': 89 print('发送上传指令') 90 if os.path.isfile(getnowpath(user_name)+'\\'+cmd[1]): 91 print('文件存在') 92 client.send(json.dumps(cmd).encode(code)) 93 file_path = getnowpath(user_name)+'\\'+cmd[1] 94 file_dict = {'name':cmd[1],'md5':check_md5(file_path),'size':os.path.getsize(file_path)} 95 file_bytes = json.dumps(file_dict).encode(code) 96 client.send(struct.pack('i',len(file_bytes))) 97 client.send(file_bytes) 98 if client.recv(1) == b'1': 99 print('空间足够!') 100 with open(file_path,'rb') as f: 101 recv_size = 0 102 recv_data = b'' 103 while recv_size < file_dict['size']: 104 res = f.read(1024) 105 client.send(res) 106 recv_data += res 107 recv_size += len(res) 108 progress_bar(recv_size, file_dict['size']) 109 if client.recv(1) == b'1': 110 print('\nMD5值校验成功,文件上传成功!') 111 else: 112 print('MD5值校验失败,请重新上传!') 113 else: 114 print('云盘空间不足!') 115 else: 116 print('要上传的文件不存在!') 117 elif cmd[0] == 'exit': 118 print('发送退出指令') 119 client.send(json.dumps(cmd).encode(code)) 120 break 121 else: 122 print('指令输入有误!') 123 else: 124 print('账号或者密码输入错误!') 125 126 run()
正文开始:
下面是补充知识点时间:
sys.path.insert(0, '..') ''' sys.path是python的搜索模块的路径集,是一个list insert() #用于将指定对象插入列表的指定位置 insert(0,'..') #在列表的第一个位置插入当前目录的上级目录 '''
scoket基础知识点:移步到菜鸟教程
#判断(文件/文件夹)是否存在 os.path.exists('这里放路径信息') #文件或者文件夹都可以判断 #判断文件是否存在 os.path.isfile('这里放文件路径') #判断文件夹是否存在 os.path.isdir('这里放文件夹路径') #获取文件夹的(文件/文件夹)列表 os.listdir('path') 返回值是列表 #获取文件夹的大小 def getfoldersize(filepath, size=0): """获取文件夹的大小""" for root, dirs, files in os.walk(filepath): for f in files: size += os.path.getsize(os.path.join(root, f)) return size #创建一个新的目录 os.makedirs('path')
import sys,os,platform if platform.system() == "Windows": BASE_DIR = "\\".join(os.path.abspath(os.path.dirname(__file__)).split("\\")[:-1]) else: BASE_DIR = "/".join(os.path.abspath(os.path.dirname(__file__)).split("/")[:-1]) sys.path.insert(0,BASE_DIR) #一般放在bin/start.py去执行
1 def progress_bar(new_n,all_n,num=50): 2 """ 3 new_n(int):当前进度 4 all_n(int):总进度 5 num:进度条长度 6 """ 7 r = int(new_n / all_n * num) 8 l = num - r 9 w = new_n / all_n * 100 10 s = '[' + '>' * r + '-' * l + ']' + ' %.2f' % w + '%' 11 sys.stdout.write('\r') 12 sys.stdout.write(s) 13 sys.stdout.flush()