FTP小程序

开发一个可以多个用户在线的FTP程序

要求:
1.用户加密认证        (完成)
    操作前需要进行登录
    需要登录与md5验证

2.允许同时多用户登录  (完成)
    使用socketserver模块进行实现

3.每个用户都有自己的家目录,且只能访问自己的家目录     (完成)
    每注册一个就创建一个文件夹

4.对用户进行磁盘配额,每个用户的可用空间不同           (完成)
    配置文件,计算自己家目录里的所有文件大小进行比较
    当上传文件的时候进行判断存储空间是否足够,不够将不进行传输


5.允许用户在ftp server  上随意切换目录   (完成)
    切换目录中还有一个功能:在当前目录下进行创建目录
    只能在自己的文件夹下进行切换
    只支持两种方式:
        1.纯点的路径:cd ...
        2.纯路径形的:cd heak\ket

6.允许用户查看当前目录下文件   (完成)
    查看当前目录下的文件与文件夹
    查看方式:
    1.dir a
    2.dir heat\hes


7.允许上传和下载文件,保证文件一致性    (完成)
    传输与上传文件的时候,用hashlib做,每传输一次update一次,
    最后将客户端加密的结果发给服务端进行校验,校验失败提示传输异常

8.文件传输过程中显示进度条    (完成)
    百分百精度条

9.附加功能:支持文件的断点续传   (完成)
    当用户上传或下载的时候,先在另一端的目录中进行查询是否有该文件,
        有:判断文件大小进行确定是否是完整文件,如果是完整文件进行重新传输并覆盖,
            如果不是完整文件就移动光标至断点处进行传输
        没有:直接进行传输

配置:

服务端:

 

 

 seeting:

 

 

 用户配置文件:

 

 

server_start:
import os
import sys

sys.path.append(os.path.dirname(os.path.dirname(__file__)))

from care.server_main import Mysocket
from conf.setting import Ip_address
import socketserver


if __name__ =='__main__':
    sever = socketserver.ThreadingTCPServer(Ip_address, Mysocket)
    sever.serve_forever()
  1 import os
  2 import json
  3 import struct
  4 import configparser
  5 import subprocess
  6 import re
  7 import hashlib
  8 import socketserver
  9 
 10 
 11 from conf.setting import user_info
 12 
 13 class Mysocket(socketserver.BaseRequestHandler):
 14 
 15 
 16 
 17     def handle(self):
 18         self.start = True
 19         self.user_login = {'user_name': '', 'login': False}
 20         self.allow_reuse_address = True
 21         self.coding = 'utf-8'
 22         self.Max_accept = 8092
 23         self.Not_modified_server_dir = r"D:\flin\网络编程\FtpProject\Server\db"
 24         self.server_dir = r"D:\flin\网络编程\FtpProject\Server\db"
 25         self.operation_list = [{'登录': 'login'}, {'注册': 'registered'}]
 26 
 27         while self.start:
 28             try:
 29                 if not self.user_login["login"]:  # 判断是否是已登录状态
 30                     head_login = self.user_login
 31                     head_login_json = json.dumps(head_login).encode(self.coding)
 32                     head_login_size = struct.pack('i', len(head_login_json))
 33 
 34                     self.request.send(head_login_size)  # 发送报头长度
 35                     self.request.send(head_login_json)  # 发送报头
 36 
 37                     user_head_size = self.request.recv(4)
 38                     user_head_unpk = struct.unpack('i', user_head_size)[0]
 39 
 40                     user_head = self.request.recv(user_head_unpk).decode(self.coding)
 41 
 42                     user_head_json = json.loads(user_head)  # 反序列化拿到报头
 43                     user_name = user_head_json['user_name']
 44                     password = user_head_json['password']
 45                     options_index = user_head_json['options'] - 1
 46 
 47                     options = list(self.operation_list[options_index].values())[0]
 48 
 49                     # ================登录 / 注册=======================
 50                     if hasattr(self, options):
 51                         func = getattr(self, options)
 52                         options_info = func(user_name, password)
 53 
 54                         if not options_info:
 55                             continue
 56 
 57                         self.server_dir = os.path.join(self.server_dir, self.user_login['user_name'])
 58 
 59                 # =========================以下为命令处理部分===================
 60                 while self.start:
 61                     conter_inp = self.request.recv(self.Max_accept).decode(self.coding)
 62                     if not conter_inp:
 63                         self.start = False
 64                         break
 65                     conter = conter_inp.split()
 66                     head = conter[0]
 67                     file_path = conter[1]
 68 
 69                     if hasattr(self, head):
 70                         func = getattr(self, head)
 71                         ret = func(file_path)
 72                         if not ret:
 73                             continue
 74 
 75             except Exception as e:
 76                 continue
 77 
 78         self.request.close()
 79         self.user_login = {'user_name': '', 'login': False}
 80         self.server_dir = r"D:\flin\网络编程\FtpProject\Server\db"
 81     def put(self, file_path):
 82         try:
 83             head_size = self.request.recv(4)
 84             head_size_unpk = struct.unpack('i', head_size)[0]
 85             head_top = self.request.recv(head_size_unpk)  # 接收头部
 86             head_json = json.loads(head_top.decode(self.coding))
 87 
 88             file_name = os.path.basename(head_json['file_path'])
 89             file_size = head_json['file_size']
 90 
 91             file_name_path = os.path.join(self.server_dir, file_name)  # 当前目录加上文件名
 92 
 93 
 94 
 95             # 计算该用户的所有文件总大小
 96             obj = configparser.ConfigParser()
 97             obj.read(user_info, encoding=self.coding)
 98             file_dir_size = self.cal_file_size(os.path.join(self.Not_modified_server_dir,
 99                                                             self.user_login['user_name']))  # 计算用户的总大小
100 
101             user_dir = os.listdir(self.server_dir)  # 获取当前文件夹下的所有文件名
102             hex_update = self.md5()
103             if file_name in user_dir:  # 目录下已有该文件
104                 self.request.send(b'1')
105                 has_file_size = os.stat(file_name_path).st_size  # 获取已有文件的大小
106                 self.request.sendall(bytes(str(has_file_size), encoding=self.coding))
107 
108                 define_has_size = int(self.request.recv(1).decode(self.coding))  # 接收是否是完整文件大小,完整接收1,不完整接收0
109 
110                 if define_has_size == 1:
111                     has_file_size = 0
112                     with open(file_name_path, 'wb') as f:
113                         f.seek(has_file_size)
114                         while has_file_size < file_size:
115                             data = self.request.recv(1024)
116                             f.write(data)
117                             hex_update.update(data)
118                             has_file_size += len(data)
119                         ret = hex_update.hexdigest()
120                         client_update = self.request.recv(1024).decode(self.coding)
121                         if ret != client_update:
122                             self.request.sendall(b'0')
123                             return
124 
125                         else:
126                             self.request.sendall(b'1')
127                             return
128 
129 
130                 elif define_has_size == 0:
131                     with open(file_name_path, 'ab') as f:
132                         f.seek(has_file_size)
133                         while has_file_size < file_size:
134                             data = self.request.recv(1024)
135                             f.write(data)
136                             hex_update.update(data)
137                             has_file_size += len(data)
138                         ret = hex_update.hexdigest()
139                         client_update = self.request.recv(1024).decode(self.coding)
140                         if ret != client_update:
141                             self.request.sendall(b'0')
142                             return
143 
144                         else:
145                             self.request.sendall(b'1')
146                             return
147 
148             else:
149                 self.request.send(b'0')  # 没有文件
150                 if file_dir_size + file_size > int(obj[self.user_login["user_name"]]['capacity']):  # 判断该用户空间是否足够
151                     self.request.send(b'0')
152                     return False
153                 self.request.send(b'1')  # 空间够
154                 number = 0
155                 with open(file_name_path, 'wb') as f:
156                     while number < file_size:
157                         line = self.request.recv(1024)
158                         f.write(line)
159                         hex_update.update(line)
160                         number += len(line)
161                         print("文件总大小:%s  已上传 %s" % (file_size, number))
162                     ret = hex_update.hexdigest()
163                     client_update = self.request.recv(1024).decode(self.coding)
164                     if ret != client_update:
165                         self.request.sendall(b'0')
166                         return
167 
168                     else:
169                         self.request.sendall(b'1')
170                         return
171 
172 
173         except Exception as e:
174             print(e.__traceback__.tb_lineno)
175 
176             return False
177 
178     def get(self, file_path):
179         """下载文件
180         只能是文件名的命令:kite.py
181         不能是路径,只能是文件名
182         支持断点续传
183         1.验证接收到的路径是否有效,若无效:返回0, 有效:返回1
184         2.发送报头长度,发送报头
185 
186         3.接收发来的文件的大小,与原文件进行比较,相等或小于源文件:调整光标 进行发送
187                 接收到'no': 从0开始发送
188         4.发送完毕"""
189         try:
190             file_path = os.path.join(self.server_dir, os.path.basename(file_path))
191             if not os.path.exists(file_path) and not os.path.isfile(file_path):  # 判断文件是否有效
192                 self.request.send(b'0')
193                 return False
194             self.request.send(b'1')
195 
196             # 拿到文件的大小
197             file_size = os.path.getsize(file_path)
198 
199             # 制作报头
200             head_top = {'file_path': file_path, 'file_size': file_size,
201                         }
202             # 发送报头的长度信息
203             head_json = json.dumps(head_top)
204             head_bytes = head_json.encode(self.coding)
205             head_size = struct.pack('i', len(head_bytes))
206 
207             self.request.send(head_size)
208             self.request.send(head_json.encode(self.coding))
209 
210 
211             # ====================================
212             define_size = self.request.recv(1).decode(self.coding)
213             # 接收是否有该文件,有为1,没有为0
214             obj = self.md5()
215             if define_size:
216                 has_file_size = int(self.request.recv(1024).decode(self.coding))
217                 if has_file_size == file_size:
218                     has_file_size = 0
219 
220                 with open(file_path, 'rb') as f:
221                     f.seek(has_file_size)
222                     while has_file_size < file_size:
223                         data = f.read(1024)
224                         self.request.sendall(data)
225                         obj.update(data)
226                         has_file_size += len(data)
227                         print("文件总大小:%s 已下载 %s" % (file_size, has_file_size))
228                 ret = obj.hexdigest()
229                 client_update = self.request.recv(1024).decode(self.coding)
230                 if ret != client_update:
231                     self.request.sendall(b'0')
232                     return
233 
234                 else:
235                     self.request.sendall(b'1')
236                     return
237 
238 
239 
240             else:
241                 number = 0
242                 with open(file_path, 'rb') as f:
243                     f.seek(0)
244                     for line in f:
245                         self.request.send(line)
246                         obj.update(line)
247                         number += len(line)
248                         print("文件总大小:%s 已下载 %s" % (file_size, number))
249                 ret = obj.hexdigest()
250                 client_update = self.request.recv(1024).decode(self.coding)
251                 if ret != client_update:
252                     self.request.sendall(b'0')
253                     return
254 
255                 else:
256                     self.request.sendall(b'1')
257                     return
258 
259         except Exception as e:
260             print(e.__traceback__.tb_lineno)
261 
262     def md5(self):
263         hash_lib = hashlib.md5(bytes('key', encoding='utf-8'))
264         return hash_lib
265 
266     def cd(self, file_path):
267         """切换路径
268         1.带点的切换 :...  ,只能用'.',后面还不能加目录名
269         2.不带点的:heat\keta
270         """
271         file_path_pattern = re.search('^\.+(\w+)?$', file_path)
272         if file_path_pattern:
273             number = file_path.count('.')  # 有几个'.'就循环几次
274             count = 0
275             res = ''
276             source_path = self.server_dir
277             while count < number:
278                 res = os.path.dirname(source_path)
279                 source_path = res
280                 if res == self.Not_modified_server_dir:
281                     return False
282                 count += 1
283             self.server_dir = res
284             self.request.send(b'1')
285 
286 
287         else:
288             dir_path = os.path.join(self.server_dir, file_path)
289             dir_path_list = []
290             ret = os.walk(self.server_dir)
291             # 拿到当前目录下的所有所有目录的路径
292             for item in ret:
293                 for sub_dir in item[1]:
294                     s = os.path.join(item[0], sub_dir)
295                     dir_path_list.append(s)
296             if dir_path in dir_path_list:
297                 self.server_dir = dir_path  # 跟换当前路径
298                 self.request.send(b'1')
299                 return
300             self.request.send(b'0')
301 
302     def mk(self, file_path):
303         """在当前目录下创建目录"""
304         if file_path not in os.listdir(self.server_dir):
305             dir_path = os.path.join(self.server_dir, os.path.basename(file_path))
306             os.makedirs(dir_path)
307             self.request.send(b'1')
308         self.request.send(b'0')
309 
310     def dir(self, file_path):
311         """查看目录下的文件夹与文件的名字
312         1.查看当前目录:dir a
313         2.查看其他目录:dir heat\hes
314         """
315 
316         if file_path == 'a':
317             obj = subprocess.Popen('dir' + ' ' + self.server_dir, shell=True,
318                                    stdout=subprocess.PIPE,
319                                    stderr=subprocess.PIPE)
320 
321             stdout = obj.stdout.read()
322             stderr = obj.stdout.read()
323 
324             stdout_size = struct.pack('i', len(stdout) + len(stderr))
325 
326             self.request.send(stdout_size)  # 发送长度
327 
328             self.request.send(stdout)
329             self.request.send(stderr)
330             return
331         else:
332             if os.path.isdir(file_path) and os.path.exists(os.path.join(self.server_dir, file_path)):
333                 obj = subprocess.Popen('dir' + ' ' + os.path.join(self.server_dir, file_path), shell=True,
334                                        stderr=subprocess.PIPE,
335                                        stdout=subprocess.PIPE)
336 
337                 stdout = obj.stdout.read()
338                 stderr = obj.stdout.read()
339 
340                 stdout_size = struct.pack('i', len(stdout) + len(stderr))
341 
342                 self.request.send(stdout_size)
343                 self.request.send(stdout)
344                 self.request.send(stderr)
345                 return
346             else:
347                 return False
348 
349     def cal_file_size(self, path):
350         """计算用户文件夹总大小"""
351         number = 0
352         ret = os.listdir(path)
353         for i in ret:
354             s = os.path.join(path, i)
355             if os.path.isdir(s):
356                 number = self.cal_file_size(s) + number
357             elif os.path.isfile(s):
358                 file_size = os.path.getsize(s)
359                 number += file_size
360         return number
361 
362     def login(self,user_name,password):
363         """登录>>>>验证用户名密码,这里有个坑,读写文件的时候一定要加上编码
364         成功,发送1
365         失败,发送0
366         """
367         obj = configparser.ConfigParser()
368         obj.read(user_info,encoding=self.coding)
369         if user_name in obj.sections() and password == obj[user_name]['password']:
370             self.user_login['user_name'] = user_name
371             self.user_login['login'] = True
372             self.request.send(b'1')
373             return True
374         else:
375             self.request.send(b'0')
376             return False
377 
378     def registered(self,user_name,password):
379         """注册>>>>验证用户名是否重复
380         重复,发送0
381         不重复,发送1,创建用户信息写入文件
382         """
383         obj = configparser.ConfigParser()
384         obj.read(user_info,encoding=self.coding)
385         if user_name in obj.sections():
386 
387             self.request.send(b'0')
388             return False
389         elif user_name not in obj.sections():
390             self.request.send(b'1')
391             #
392             os.makedirs(os.path.join(self.server_dir, user_name))
393             obj[user_name]={'password':password,
394                             'capacity':1024000,
395                             'user_filedir':os.path.join(self.server_dir, user_name)}
396             with open(user_info,'w',encoding=self.coding) as f:
397                 obj.write(f)
398 
399             return False
server_main 主要代码

客户端:

配置:

 

 setting:

 

 start:

import os
import sys

sys.path.append(os.path.dirname(os.path.dirname(__file__)))
from conf.setting import Ip_address
from care.client_main import Client


if __name__ =='__main__':
    obj = Client(Ip_address)
    obj.run()
  1 import os
  2 import struct
  3 import json
  4 from care.user_login import user_record
  5 from conf.setting import user_room
  6 import hashlib
  7 import socket
  8 
  9 
 10 class Client:
 11     address_family = socket.AF_INET
 12     socket_type = socket.SOCK_STREAM
 13     user_login = {'user_name': None, 'login': False}
 14     operation_list = [{'登录': 'login'}, {'注册': 'registered'}]
 15     coding = 'utf-8'
 16     Max_accept = 8092
 17     Client_dir = user_room # 默认下载目录
 18 
 19     def __init__(self, client_address, connect_and_active=True):
 20         self.client = client_address
 21         self.socket = socket.socket(self.address_family, self.socket_type)
 22         self.attom = False
 23 
 24 
 25         if connect_and_active:
 26             try:
 27                 print('正在连接服务端…………')
 28                 self.client_connect()
 29                 print('连接成功')
 30                 print(self.client[0])
 31                 print(self.client[1])
 32             except Exception:
 33                 self.client_close()
 34                 raise
 35 
 36     def client_connect(self):
 37         """进行链接"""
 38         self.socket.connect(self.client)
 39 
 40     def client_close(self):
 41         """关闭客户端"""
 42         self.socket.close()
 43 
 44     def run(self):
 45         """主程序"""
 46 
 47         print('———————————————————欢迎来到FTP文件上传下载中心————————————————————')
 48 
 49         while 1:   # 判断是否已登录,若未登录需登录
 50             self.cot = self.socket.getsockname()  # 获取本机IP与port
 51             self.cat = self.socket.getpeername()  # 获取远程发送数据的IP与port
 52             heard_login_size = self.socket.recv(4)
 53 
 54             heard_login_size_unpk = struct.unpack('i',heard_login_size)[0]
 55             heard_login = self.socket.recv(heard_login_size_unpk).decode(self.coding)
 56 
 57             heard_login_json = json.loads(heard_login)
 58             user_login = heard_login_json['login']
 59 
 60             if not user_login:  # 如果为False
 61                 user_name,password,number = user_record(self.attom)
 62 
 63                 user_heard = {'user_name':user_name,'password':password,'options':number}
 64                 user_heard_json = json.dumps(user_heard).encode(self.coding)
 65                 user_heard_size = struct.pack('i',len(user_heard_json))
 66 
 67                 self.socket.send(user_heard_size)
 68                 self.socket.send(user_heard_json)
 69 
 70 
 71                 if number == 1:  # 如果是登录
 72                     self.attom = number  # 改变attom的值
 73                     ret = self.socket.recv(1).decode(self.coding)   # 接收服务端发来的账号密码验证
 74                     if not int(ret):
 75                         print("用户名或密码错误,请重新输入")
 76                         continue
 77                     print('登录成功')
 78 
 79                 elif number == 2:
 80                     ret = self.socket.recv(1).decode(self.coding)
 81                     if not int(ret):
 82                         print("用户名已被注册,请重新输入一个用户名")
 83                         self.attom = number
 84                         continue
 85                     print('注册成功')
 86                     continue
 87         # =====================以下为输入命令部分=======================
 88             while 1:
 89                 user_inpt = input('<<<').strip()
 90                 if user_inpt=='exit()':
 91                     return
 92                 if not user_inpt:   # 为空跳过
 93                     continue
 94                 try:
 95                     client_cent = user_inpt.split()
 96                     client_heard = client_cent[0]
 97                     client_file = client_cent[1]
 98                     if hasattr(self, client_heard):
 99 
100                         func = getattr(self, client_heard)
101                         res = func(user_inpt, client_file)
102                         if not res:
103                             continue
104 
105                 except Exception as e :
106 
107                     print(e.__traceback__.tb_lineno)
108                     print("请输入正确的命令")
109                     continue
110 
111 
112     def get(self, args, client_file):
113         """下载文件,先发送用户输入的命令在服务端进行验证,
114         服务端会发送验证后的信息,如果文件存在就进行传输,不行就重新输入
115         断点续传:当文件传输还未传输完时中断了,
116             当用户再次登录进行输入命令进行下载的时候,会在未传输完整的文件上继续传输完
117             如果文件已完整存在就会进行重新传输,并覆盖原文件
118             如果文件不存在就从头开始传输
119         1.先发送命令,接收服务端发来的文件路径是否有效
120         2.接收报头长度,接收报
121         3.判断下载目录中是否已有该文件,已有:发送1
122             没有该文件:发送0
123         4.如果是文件是完整的会重新传输并覆盖,如不完整:调整光标到断点进行写入
124             如没有将从0开始进行传输"""
125 
126         try:
127             self.socket.send(args.encode(self.coding))   # 发送整条命令
128 
129             Validation_path = self.socket.recv(1).decode(self.coding)   # 接收是否是有效路径
130 
131             if not int(Validation_path):
132                 print('文件路径无效,请输入正确的文件路径')
133                 return False
134 
135             head_size = self.socket.recv(self.Max_accept)    # 接收报头长度
136             head_size_unpk = struct.unpack('i', head_size)
137             head_top = self.socket.recv(head_size_unpk[0])   # 接收报头
138 
139             head_json = json.loads(head_top)
140             file_size = head_json['file_size']
141             file_path = head_json['file_path']
142 
143 
144             #  ======================以下为传输部分======================
145             file_name = os.path.basename(file_path)
146             file = os.path.join(self.Client_dir,file_name)  # 将当前目录路径与文件名进行合并
147             download_dir = os.listdir(self.Client_dir)  # 拿到下载目录下的所有文件名
148             md5_update = self.md5()
149 
150             if file_name in download_dir:
151                 self.socket.send(b'1')
152                 has_file_size = os.stat(file).st_size  # 拿到已有文件的大小
153                 self.socket.sendall(bytes(str(has_file_size), encoding=self.coding))  # 发送文件大小
154                 if has_file_size==file_size:
155                     has_file_size = 0
156                     with open(file,'wb') as f:
157                         f.seek(has_file_size)
158                         while has_file_size<file_size:
159                             data = self.socket.recv(1024)
160                             f.write(data)
161                             md5_update.update(data)
162                             has_file_size+=len(data)
163                             res = '\r %.2f %s '
164                             print(res % ((has_file_size / file_size) * 100,
165                                          int((has_file_size / file_size) * 100) * '*'),
166                                             end='')
167 
168                         ret = md5_update.hexdigest()
169                         self.socket.sendall(bytes(ret,encoding=self.coding))
170                         res = self.socket.recv(1024).decode(self.coding)
171                         if res:
172                             print('')
173                             print('下载完成')
174                         else:
175                             print('下载异常,请删除后重新下载')
176 
177                 elif has_file_size<file_size:
178                     with open(file,'ab') as f:
179                         f.seek(has_file_size)   # 移动光标至断点
180                         print("断点续传")
181                         while has_file_size<file_size:
182                             line = self.socket.recv(1024)
183                             f.write(line)
184                             md5_update.update(line)
185                             has_file_size+=len(line)
186                             res = '\r %.2f %s '
187                             print(res % ((has_file_size / file_size) * 100, int((has_file_size / file_size) * 100) * '*'), end='')
188 
189 
190                         ret = md5_update.hexdigest()
191                         self.socket.sendall(bytes(ret, encoding=self.coding))
192                         res = self.socket.recv(1024).decode(self.coding)
193                         if res:
194                             print('')
195                             print('下载完成')
196                         else:
197                             print('下载异常,请删除后重新下载')
198             else:
199                 self.socket.send(b'0')  # 发送没有该文件的信息
200 
201                 self.socket.sendall(b'0')
202                 with open(file, 'wb') as f:
203                     f.seek(0)
204                     number = 0
205                     while number < file_size:
206                         ret = self.socket.recv(1024)
207                         f.write(ret)
208                         md5_update.update(ret)
209                         number += len(ret)
210                         res = '\r %.2f %s '
211                         print(res % ((number / file_size) * 100, int((number / file_size) * 100) * '*'), end='')
212 
213 
214                     ret = md5_update.hexdigest()
215                     self.socket.sendall(bytes(ret, encoding=self.coding))
216                     res = self.socket.recv(1024).decode(self.coding)
217                     if res:
218                         print('')
219                         print('下载完成')
220                     else:
221                         print('下载异常,请删除后重新下载')
222 
223         except Exception as e:
224             print(e.__traceback__.tb_lineno,e.__traceback__.tb_next)
225 
226 
227     def md5(self):
228         """
229         数据传输时加密,传输完成并返回给服务端进行验证
230         :return: 返回一个对象
231         """
232         hash_lib = hashlib.md5(bytes('key',encoding=self.coding))
233         return hash_lib
234 
235     def put(self, args, client_file):
236         """
237         上传文件,不限制文件路径
238         :param args: 用户输入的命令
239         :param client_file: 文件路径
240         :return:
241         """
242         try:
243             if not os.path.exists(client_file) and not os.path.isfile(client_file):  # 判断是否是有效路径与是否是一个文件
244                 print('未找到文件,请输入正确且有效的文件路径')
245                 return
246 
247             self.socket.send(args.encode(self.coding))   # 发送指令
248 
249             file_size = os.path.getsize(client_file)    # 获取要上传的文件的大小
250             heard_top = {'file_path': client_file,
251                          'file_size': file_size,
252 
253                          }
254             heard_json = json.dumps(heard_top)   # 序列化
255             heard_bytes = heard_json.encode(self.coding)
256             heard_size = struct.pack('i', len(heard_bytes))   # 打包报头的长度
257 
258             self.socket.send(heard_size)   # 发送报头的长度
259             self.socket.send(heard_bytes)  # 发送报头
260 
261 
262 
263     # =========================以下为传输部分,含断点续传=======================
264             mode = int(self.socket.recv(1).decode(self.coding))    # 接收 是否已有文件 有接收1 没有接收0
265             md5_update = self.md5()
266             if mode==1:
267                 has_file_size = int(self.socket.recv(1024).decode(self.coding))  # 接收已有的文件大小
268                 if has_file_size==file_size:  # 已有文件大小与要上传的文件的大小一致,重新上传将其覆盖
269                     self.socket.send(b'1')   # 发送信息告知服务端
270                     has_file_size = 0      # 从0开始进行发送
271                     with open(client_file, 'rb') as f:
272                         print('已有文件重新传输')
273                         print(has_file_size, type(has_file_size))
274                         f.seek(has_file_size)
275                         while has_file_size < file_size:
276                             data = f.read(1024)
277                             self.socket.send(data)
278                             md5_update.update(data)
279                             has_file_size += len(data)
280                             res = '\r %.2f %s '
281                             print(res % ((has_file_size / file_size) * 100, int((has_file_size / file_size) * 100) * '*'), end='')
282 
283                         ret = md5_update.hexdigest()
284                         self.socket.sendall(bytes(ret, encoding=self.coding))
285                         res = self.socket.recv(1024).decode(self.coding)
286                         if res :
287 
288                             print('')
289                             print('上传完成')
290                         else:
291                             print('传输异常')
292 
293                 else:
294                     self.socket.send(b'0')
295                     # 告知服务端不是完整文件,移动光标至断点处进行读取,发送
296                     with open(client_file, 'rb') as f:
297                         print('断点续传')
298                         print(has_file_size, type(has_file_size))
299                         f.seek(has_file_size)
300                         while has_file_size < file_size:
301                             data = f.read(1024)
302                             self.socket.send(data)
303                             md5_update.update(data)
304                             has_file_size += len(data)
305                             res = '\r %.2f %s '
306                             print(res % ((has_file_size / file_size) * 100,
307                                          int((has_file_size / file_size) * 100) * '*'), end='')
308 
309 
310                         ret = md5_update.hexdigest()
311                         self.socket.sendall(bytes(ret, encoding=self.coding))
312                         res = self.socket.recv(1024).decode(self.coding)
313                         if res:
314 
315                             print('')
316                             print('上传完成')
317                         else:
318                             print('传输异常')
319 
320             elif mode==0:
321                 #  没有该文件,先接收服务端发来的储存空间是否足够,足够进行传输
322                 defind_capacity = self.socket.recv(1).decode(self.coding)
323                 if defind_capacity==0:
324                     print('......空间不足,无法进行传输')
325                     return False
326                 print('正在传输,请等待……')
327                 number = 0
328                 res = '\r %.2f %s '
329                 with open(client_file, 'rb') as f:
330                     for line in f:
331                         self.socket.send(line)
332                         md5_update.update(line)
333                         number += len(line)
334                         print(res % ((number / file_size) * 100, int((number / file_size) * 100) * '*'), end='')
335 
336 
337                     ret = md5_update.hexdigest()
338                     self.socket.sendall(bytes(ret, encoding=self.coding))
339                     res = self.socket.recv(1024).decode(self.coding)
340                     if res:
341 
342                         print('')
343                         print('上传完成')
344                     else:
345                         print('传输异常')
346         except Exception as e:
347             print(e.__traceback__.tb_lineno,e.__traceback__.tb_lasti)
348 
349 
350 
351     def dir(self,args, client_file):
352         """查看当前目录中的所有文件与文件夹名"""
353 
354         self.socket.send(args.encode(self.coding))
355 
356         stdout_size = self.socket.recv(4)
357         stdout_size_unpk = struct.unpack('i',stdout_size)[0]
358 
359         number = 0
360         conter = b''
361         # 接收结果并打印
362         while number<stdout_size_unpk:
363             line = self.socket.recv(1024)
364             conter+=line
365             number+=len(line)
366         print(conter.decode('GBK'))
367 
368     def mk(self,args,client_file):
369         """在当前目录下创建文件夹"""
370         self.socket.send(args.encode(self.coding))  #  发送命令
371         ret = self.socket.recv(1).decode(self.coding)
372         if ret:
373             print('目录创建成功')
374             return
375         print('目录已存在')
376 
377 
378     def cd(self,args,client_file):
379         """切换目录"""
380         self.socket.send(args.encode(self.coding)) # 发送命令
381         ret = self.socket.recv(1).decode(self.coding)
382         if ret:
383             print('目录切换成功')
384             return
385         print('目录切换失败,请输入正确的路径')
386 if __name__ == '__main__':
387     obj = Client(('127.0.0.1', 8002))
388     obj.run()
client_main 主要代码

user_login:

import re
import hashlib


class Operate:
    """用户输入,返回用户名与密码
    密码用md5进行加密
    """
    def encryption(self,password):
        """MD5密码加密"""
        obj = hashlib.md5(bytes('key', encoding='utf-8'))
        obj.update(bytes(password, encoding='utf-8'))
        password = obj.hexdigest()
        return password

    def login(self,number):
        """登录"""
        while 1:
            print('登录>>>>>>>>>>>')
            user_name = input("请输入用户名").strip()
            if user_name.upper()=='Q':
                return False
            password = input("请输入密码").strip()
            if password.upper()=='Q':
                return False

            user_pattern = re.search('^\w{6,}$', user_name)
            password_pattern = re.search('^[0-9A-Za-z_@]{8,}$', password)
            if user_pattern and password_pattern:
                password = self.encryption(password)
                return user_name,password,number
            else:
                print("请输入正确的用户密码")

    def registered(self,number):
        """注册"""
        while 1:
            print('注册>>>>>>>>>>>')
            user_name = input("请输入您要注册的用户名:(至少6位数字或字母)").strip()
            if user_name.upper()=='Q':
                return False
            password = input("请输入您的密码: (至少8位数字或字母)").strip()
            if user_name.upper()=='Q':
                return False
            user_pattern = re.search('^\w{6,}$',user_name)
            password_pattern = re.search('^[0-9A-Za-z_@]{8,}$',password)
            if user_pattern and password_pattern:
                password = self.encryption(password)
                return user_name,password,number

            else:
                print("用户名或密码不合法请重新输入")

def user_record(attom):
    """循环选择选项,返回用户名与密码
    这里传入attom,默认是False,需要进行输入序号
    一旦输入序号后出现了输入错误,就不会再需要输入序号了,是直接进行循环输入"""
    while 1:
        operation_list = [{'登录': 'login'}, {'注册': 'registered'}]
        if not attom:

            print('请选择您要继续的操作:')

            for index, operation in enumerate(operation_list, 1):
                print(index, list(operation.keys())[0])
            try:
                user_inp = input('请输入序号:')
                if user_inp.upper() == 'Q':
                    return False
                user_int = int(user_inp)
                # 拿到索引字典中的value
                operation_list_value = operation_list[user_int - 1][list(operation_list[user_int - 1])[0]]
                # 反射到类里面进行执行
                if hasattr(Operate, operation_list_value):
                    func_operation = getattr(Operate(), operation_list_value)
                    ret = func_operation(int(user_int)) # 传入数字
                    if not ret:
                        continue
                    return ret

            except Exception as e:
                print("请输入正确的序号")
                continue
        else:
            operation_list_value = operation_list[attom - 1][list(operation_list[attom - 1])[0]] # 拿到索引字典中的value
            if hasattr(Operate, operation_list_value):
                func_operation = getattr(Operate(), operation_list_value)
                ret = func_operation(attom)
                return ret

 

转载于:https://www.cnblogs.com/whileke/p/11483483.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值