scoket网络编程之文件上传下载篇

时间:2022-12-17 22:27:40

写在前面:笔记还是自己整理的好!以后再自己整理属于自己的笔记


项目(思路)分析:  

scoket网络编程之文件上传下载篇scoket网络编程之文件上传下载篇
'''
我的网盘
    程序加载……(读取用户json数据、先启动服务端、再启动客户端)
        客户端作为显示页面
            输入账号密码进行登录,并显示当前目录下文件(夹)列表
                下载---get a.mp4
                    服务端检测文件是否存在名,并返回(是否存在、【文件大小、文件名称、md5值】)
                    客户端检测本地储存空间是否充足,充足则下载并检验md5值,【显示进度条】
                        否则提示空间不足
                上传---set b.mp4
                    客户端发送文件信息【文件大小、文件名称、md5值】
                    服务端检测储存空间是否充足,充足则接收上传,并检验md5值,【显示进度条】
                        否则提示空间不足
                显示当前目录下的文件列表   %¥……&&
                退出当前账号---exit
            登录第二个账号  。。。
管理员页面
    新建账号
    删除账号
    管理账号
        更改储存空间大小
    【存储信息】
'''
实现思路

具体实现步骤:

服务端(基于面向对象写的):

scoket网络编程之文件上传下载篇scoket网络编程之文件上传下载篇
  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()
server.py

客户端(基于函数写的...):

scoket网络编程之文件上传下载篇scoket网络编程之文件上传下载篇
  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()
client.py

正文开始:

  下面是补充知识点时间:

scoket网络编程之文件上传下载篇scoket网络编程之文件上传下载篇
sys.path.insert(0, '..')
'''
sys.path是python的搜索模块的路径集,是一个list
insert() #用于将指定对象插入列表的指定位置
insert(0,'..') #在列表的第一个位置插入当前目录的上级目录
'''
sys.path.insert()的应用

scoket基础知识点:移步到菜鸟教程

scoket网络编程之文件上传下载篇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')
OS模块的(文件/文件夹)读写操作
scoket网络编程之文件上传下载篇scoket网络编程之文件上传下载篇
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去执行
把当前项目路径添加到全局搜索的首个位置

 

scoket网络编程之文件上传下载篇scoket网络编程之文件上传下载篇
 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()    
打印进度条