功能:
1、用户加密认证
2、允许同时多用户登录
3、每个用户有自己的家目录 ,且只能访问自己的家目录
4、对用户进行磁盘配额,每个用户的可用空间不同
5、允许用户在ftp server上随意切换目录
6、允许用户查看当前目录下文件
7、允许上传和下载文件,保证文件一致性
8、文件传输过程中显示进度条
附加功能:支持文件的断点续传
环境:
python 3.5
特性:
用socketserver实现FTP
主要功能实现:
1、用户加密认证
ConfigParser 是Python自带的模块, 用来读写配置文件,将用户信息以下边的格式存入account.py文件,读出后进行判断
[DEFAULT] [alex] Password = 123456 Quotation = 100 [jack] Password = 123456 Quotation = 100
2、允许同时多用户登录
从scokerserver 继承 socketserver.ThreadingTCPServer即可实现
3、每个用户有自己的家目录 ,且只能访问自己的家目录
将所有路径封装在home目录下
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) USER_HOME = '%shome' % BASE_DIR LOG_DIR = '%slog' % BASE_DIR LOG_LEVEL = 'DEBUG'
4、允许用户在ftp server上随意切换目录
用os模块改变工作目录
os.chdir(func_val)
5、允许用户查看当前目录下文件
用os模块的os.listdir,下为server端代码
def _ls(self,*args,**kwargs): '''显示当前目录下的所有文件''' if os.getcwd() == '%s\bin'% settings.BASE_DIR: user_home_dir = "%s\%s" % (settings.USER_HOME, self.user["Username"]) self.request.send(json.dumps(os.listdir(user_home_dir)).encode()) else:self.request.send(json.dumps(os.listdir()).encode())
6、允许上传和下载文件,保证文件一致性
判断上传和下载的文件在传输前后的大小,用以判断文件是否保持一致
1 #server端 2 # 3 def _put(self,*args,**kwargs): 4 '''上传文件命令''' 5 data = args[0] 6 7 response = self.get_response() 8 if response["status_code"] == 257: # ready to receive 9 self.request.send(b'1') # send confirmation to server 10 user_home_dir = "%s\%s" % (settings.USER_HOME, self.user["Username"]) 11 base_filename = "%s\%s" % (user_home_dir, data.get('filename')) 12 received_size = 0 13 file_obj = open(base_filename, 'wb') 14 if data.get('md5'): 15 md5_obj = hashlib.md5() 16 while received_size < response['file_size']: 17 line = self.request.recv(4096) 18 received_size += len(line) 19 file_obj.write(line) 20 md5_obj.update(line) 21 else: 22 file_obj.close() 23 md5_val = md5_obj.hexdigest() 24 self.send_response(258, {'md5': md5_val}) 25 print("-----file rece done-----") 26 27 else: 28 while received_size < response['file_size']: 29 data = self.request.recv(4096) 30 received_size += len(data) 31 file_obj.write(data) 32 else: 33 print("-----file rece done-----") 34 file_obj.close() 35 else: 36 print(STATUS_CODE[response["status_code"]]) 37 38 39 def _get(self,*args,**kwargs): 40 '''下载文件命令''' 41 data = args[0] 42 if data.get('filename') is None: 43 self.send_response(255) 44 user_home_dir = "%s\%s" %(settings.USER_HOME,self.user["Username"]) 45 file_abs_path = "%s\%s" %(user_home_dir,data.get('filename')) 46 print("file abs path",file_abs_path) 47 48 if os.path.isfile(file_abs_path): 49 file_obj = open(file_abs_path,'rb') 50 file_size = os.path.getsize(file_abs_path) 51 self.send_response(257,data = {'file_size':file_size}) 52 self.request.recv(1) #等待客户端确认,防粘包 53 54 if data.get('md5'): 55 md5_obj = hashlib.md5() 56 for line in file_obj: 57 self.request.send(line) 58 md5_obj.update(line) 59 else: 60 file_obj.close() 61 md5_val = md5_obj.hexdigest() 62 self.send_response(258,{'md5':md5_val}) 63 print("send file done...") 64 else: 65 for line in file_obj: 66 self.request.send(line) 67 else: 68 file_obj.close() 69 print("send file done...") 70 else: 71 self.send_response(256) 72 73 74 75 76 # client端 77 # 78 def _get(self,cmd_list): 79 '''下载文件''' 80 print("get--",cmd_list) 81 if len(cmd_list) == 1: 82 print("no filename follows...") 83 return 84 data_header = { 85 'action':'get', 86 'filename':cmd_list[1] 87 } 88 if self.__md5_required(cmd_list): 89 data_header['md5'] = True 90 91 self.sock.send(json.dumps(data_header).encode()) 92 response = self.get_response() 93 print(response) 94 try: 95 if response["status_code"] == 257:#ready to receive 96 self.sock.send(b'1') #send confirmation to server 97 base_filename = cmd_list[1].split('/')[-1] 98 received_size = 0 99 file_obj = open(base_filename,'wb') 100 if self.__md5_required(cmd_list): 101 md5_obj = hashlib.md5() 102 progress = self.show_progress(response['file_size']) #generator 103 progress.__next__() 104 while received_size < response['file_size']: 105 data = self.sock.recv(4096) 106 received_size += len(data) 107 try: 108 progress.send(len(data)) 109 except StopIteration as e: 110 print("100%") 111 file_obj.write(data) 112 md5_obj.update(data) 113 else: 114 print("-----file rece done-----") 115 file_obj.close() 116 md5_val = md5_obj.hexdigest() 117 md5_from_server = self.get_response() 118 if md5_from_server['status_code'] == 258: 119 if md5_from_server['md5'] == md5_val: 120 print("%s 文件一致性校验成功!"% base_filename) 121 print(md5_val,md5_from_server) 122 123 else: 124 progress = self.show_progress(response['file_size']) # generator 125 progress.__next__() 126 while received_size < response['file_size']: 127 data = self.sock.recv(4096) 128 received_size += len(data) 129 file_obj.write(data) 130 try: 131 progress.send(len(data)) 132 except StopIteration as e: 133 print("100%") 134 else: 135 print("-----file rece done-----") 136 file_obj.close() 137 else: 138 print(STATUS_CODE[response["status_code"]]) 139 except Exception as e: 140 base_file_size = os.path.getsize(base_filename) 141 with open('data\breakpoint', 'wb') as br_po: 142 data_header['action'] = 'breakpoint' 143 data_header['breakpoint'] = base_file_size 144 br_po.write(json.dumps(data_header).encode()) 145 146 147 def _put(self,cmd_list): 148 '''上传文件''' 149 print("put--", cmd_list) 150 if len(cmd_list) == 1: 151 print("no filename follows...") 152 return 153 data_header = { 154 'action': 'put', 155 'filename': cmd_list[1] 156 } 157 if self.__md5_required(cmd_list): 158 data_header['md5'] = True 159 160 self.sock.send(json.dumps(data_header).encode()) 161 if os.path.isfile(cmd_list[1]): 162 file_obj = open(cmd_list[1], 'rb') 163 file_size = os.path.getsize(cmd_list[1]) 164 self.send_response(257, data={'file_size': file_size}) 165 self.sock.recv(1) # 等待服务器端确认 166 if self.__md5_required(cmd_list): 167 md5_obj = hashlib.md5() 168 progress = self.show_progress(file_size) # generator 169 progress.__next__() 170 for line in file_obj: 171 self.sock.send(line) 172 try: 173 progress.send(len(line)) 174 except StopIteration as e: 175 print("100%") 176 md5_obj.update(line) 177 else: 178 file_obj.close() 179 md5_val = md5_obj.hexdigest() 180 md5_from_server = self.get_response() 181 if md5_from_server['md5'] == md5_val: 182 print("%s 文件一致性校验成功!"% cmd_list[1]) 183 self.send_response(258, {'md5': md5_val}) 184 print("send file done...") 185 else: 186 progress = self.show_progress(file_size) # generator 187 progress.__next__() 188 for line in file_obj: 189 self.sock.send(line) 190 try: 191 progress.send(len(line)) 192 except StopIteration as e: 193 print("100%") 194 else: 195 file_obj.close() 196 print("send file done...") 197 else: 198 print(256)
7、文件传输过程中显示进度条
根据已传文件大小和总文件大小比对判断,输出符号
1 def show_progress(self,total): 2 '''显示进度条 3 total: 文件大小''' 4 received_size = 0 5 current_percent = 0 6 while received_size < total: 7 if int((received_size / total) * 100) > current_percent: 8 print(">",end='',flush=True) 9 current_percent = int((received_size / total) * 100) 10 new_size = yield 11 received_size += new_size
附加功能:支持文件的断点续传
将上次断点信息记录下来,也就是记录已传文件大小,再次启用时,将目标文件光标移到上次断点处,然后进行续传
1 def _breakpoint(self,*args,**kwargs): 2 '''断点续传''' 3 data = args[0] 4 if data.get('filename') is None: 5 self.send_response(255) 6 user_home_dir = "%s\%s" % (settings.USER_HOME, self.user["Username"]) 7 file_abs_path = "%s\%s" % (user_home_dir, data.get('filename')) 8 print("file abs path", file_abs_path) 9 print(data.get('breakpoint')) 10 11 if os.path.isfile(file_abs_path): 12 file_obj = open(file_abs_path, 'rb') 13 file_obj.seek(data.get('breakpoint')) 14 file_size = os.path.getsize(file_abs_path) 15 self.send_response(257, data={'file_size': file_size}) 16 self.request.recv(1) # 等待客户端确认 17 18 if data.get('md5'): 19 md5_obj = hashlib.md5() 20 for line in file_obj: 21 self.request.send(line) 22 md5_obj.update(line) 23 else: 24 file_obj.close() 25 md5_val = md5_obj.hexdigest() 26 self.send_response(258, {'md5': md5_val}) 27 print("send file done...") 28 else: 29 for line in file_obj: 30 self.request.send(line) 31 else: 32 file_obj.close() 33 print("send file done...") 34 else: 35 self.send_response(256) 36 pass
主要知识点:
类的继承
os模块的应用
json模块的应用
类方法的运用
md5加密方法
scoket链接
scoketserver链接
反射
异常处理
小结:
这个程序主要用socketserver实现了一台服务器链接多个客户端,并且进行信息交互,并且实现了几个简单的功能,如:get 文件下载、put 上传文件、cd 切换目录、ls 查看文件、breakpoint断点续传。
主要代码:
1 #server端 2 # 3 4 5 import os,sys 6 import hashlib 7 import socket 8 import socketserver 9 import json 10 import configparser 11 from conf import settings 12 13 14 STATUS_CODE = { 15 250 : "Invalid cmd format,e.g:{'action':'get','filename':'test.py','size':344", 16 251 : "Invalid cmd", 17 252 : "Invalid auth data", 18 253 : "Wrong username or password..", 19 254 : "Passed authentication", 20 255 : "Filename doesn't provided", 21 256 : "File doesn't exist on server", 22 257 : "ready to send file", 23 258 : "md5 verification", 24 259 : "Directory has been switched" 25 } 26 27 28 29 30 31 class FTPHandler(socketserver.BaseRequestHandler): 32 '''定义request handler类,从BaseRequestHandler类继承''' 33 def handle(self): 34 ''' 35 获取服务器端的信息 36 如果传来cd命令,改变工作目录 37 ''' 38 39 while True: 40 self.data = self.request.recv(1024).strip() 41 if not self.data: 42 print('Client closed..') 43 break 44 data = json.loads(self.data.decode()) 45 if data.get('action') is not None: 46 if hasattr(self,'_%s'%data.get('action')): 47 func =getattr(self,'_%s'%data.get('action')) 48 func_val = func(data) 49 if data.get('action') == 'cd': #cd 命令,改变工作目录 50 os.chdir(func_val) 51 else:pass 52 else: 53 print('Invalid cmd') 54 self.send_response(251) 55 else: 56 print('Invalid cmd format') 57 self.send_response(250) 58 59 60 def send_response(self,status_code,data=None): 61 '''向客户端返回数据''' 62 response = {'status_code': status_code, 'status_msg':STATUS_CODE[status_code]} 63 if data: 64 response.update(data) 65 self.request.send(json.dumps(response).encode()) 66 67 68 def _auth(self,*args,**kwargs): 69 '''判断用户是否输入完整的用户名和密码 70 验证用户名和密码是否合法''' 71 data = args[0] 72 if data.get('username') is None or data.get('password') is None: 73 self.send_response(252) 74 75 user = self.authenticate(data.get('username'),data.get('password')) 76 if user is None: 77 self.send_response(253) 78 else: 79 print('passed authentication',user) 80 self.user = user 81 self.send_response(254) 82 83 84 def authenticate(self,username,password): # 85 '''验证用户合法性,合法返回用户数据''' 86 config = configparser.ConfigParser() 87 config.read(settings.ACCOUNT_FILE) 88 if username in config.sections(): 89 _password = config[username]['Password'] 90 if _password == password: 91 print('pass auth..',username) 92 config[username]["Username"] = username 93 return config[username] 94 95 96 def get_response(self): 97 '''接收客户端回复结果''' 98 data = self.request.recv(1024) 99 data = json.loads(data.decode()) 100 return data 101 102 103 def show_progress(self,total): 104 '''显示进度条 105 total: 文件大小''' 106 received_size = 0 107 current_percent = 0 108 while received_size < total: 109 if int((received_size / total) * 100) > current_percent: 110 print(">",end='',flush=True) 111 current_percent = int((received_size / total) * 100) 112 new_size = yield 113 received_size += new_size 114 115 116 def _put(self,*args,**kwargs): 117 '''上传文件命令''' 118 data = args[0] 119 120 response = self.get_response() 121 if response["status_code"] == 257: # ready to receive 122 self.request.send(b'1') # send confirmation to server 123 user_home_dir = "%s\%s" % (settings.USER_HOME, self.user["Username"]) 124 base_filename = "%s\%s" % (user_home_dir, data.get('filename')) 125 received_size = 0 126 file_obj = open(base_filename, 'wb') 127 if data.get('md5'): 128 md5_obj = hashlib.md5() 129 while received_size < response['file_size']: 130 line = self.request.recv(4096) 131 received_size += len(line) 132 file_obj.write(line) 133 md5_obj.update(line) 134 else: 135 file_obj.close() 136 md5_val = md5_obj.hexdigest() 137 self.send_response(258, {'md5': md5_val}) 138 print("-----file rece done-----") 139 140 else: 141 while received_size < response['file_size']: 142 data = self.request.recv(4096) 143 received_size += len(data) 144 file_obj.write(data) 145 else: 146 print("-----file rece done-----") 147 file_obj.close() 148 else: 149 print(STATUS_CODE[response["status_code"]]) 150 151 152 def _get(self,*args,**kwargs): 153 '''下载文件命令''' 154 data = args[0] 155 if data.get('filename') is None: 156 self.send_response(255) 157 user_home_dir = "%s\%s" %(settings.USER_HOME,self.user["Username"]) 158 file_abs_path = "%s\%s" %(user_home_dir,data.get('filename')) 159 print("file abs path",file_abs_path) 160 161 if os.path.isfile(file_abs_path): 162 file_obj = open(file_abs_path,'rb') 163 file_size = os.path.getsize(file_abs_path) 164 self.send_response(257,data = {'file_size':file_size}) 165 self.request.recv(1) #等待客户端确认,防粘包 166 167 if data.get('md5'): 168 md5_obj = hashlib.md5() 169 for line in file_obj: 170 self.request.send(line) 171 md5_obj.update(line) 172 else: 173 file_obj.close() 174 md5_val = md5_obj.hexdigest() 175 self.send_response(258,{'md5':md5_val}) 176 print("send file done...") 177 else: 178 for line in file_obj: 179 self.request.send(line) 180 else: 181 file_obj.close() 182 print("send file done...") 183 else: 184 self.send_response(256) 185 186 187 188 def _ls(self,*args,**kwargs): 189 '''显示当前目录下的所有文件''' 190 if os.getcwd() == '%s\bin'% settings.BASE_DIR: 191 user_home_dir = "%s\%s" % (settings.USER_HOME, self.user["Username"]) 192 self.request.send(json.dumps(os.listdir(user_home_dir)).encode()) 193 else:self.request.send(json.dumps(os.listdir()).encode()) 194 195 196 197 def _cd(self,*args,**kwargs): 198 '''改变工作目录''' 199 data = args[0] 200 user_home_dir = "%s\%s" % (settings.USER_HOME, self.user["Username"]) 201 file_abs_path = "%s\%s" %(user_home_dir,data.get('path')) 202 try: 203 os.listdir(file_abs_path) 204 except FileNotFoundError as e: 205 self.request.send(json.dumps(e).encode()) 206 return 207 self.request.send(json.dumps(259).encode()) 208 os.chdir(file_abs_path) 209 return file_abs_path 210 211 212 def _breakpoint(self,*args,**kwargs): 213 '''断点续传''' 214 data = args[0] 215 if data.get('filename') is None: 216 self.send_response(255) 217 user_home_dir = "%s\%s" % (settings.USER_HOME, self.user["Username"]) 218 file_abs_path = "%s\%s" % (user_home_dir, data.get('filename')) 219 print("file abs path", file_abs_path) 220 print(data.get('breakpoint')) 221 222 if os.path.isfile(file_abs_path): 223 file_obj = open(file_abs_path, 'rb') 224 file_obj.seek(data.get('breakpoint')) 225 file_size = os.path.getsize(file_abs_path) 226 self.send_response(257, data={'file_size': file_size}) 227 self.request.recv(1) # 等待客户端确认 228 229 if data.get('md5'): 230 md5_obj = hashlib.md5() 231 for line in file_obj: 232 self.request.send(line) 233 md5_obj.update(line) 234 else: 235 file_obj.close() 236 md5_val = md5_obj.hexdigest() 237 self.send_response(258, {'md5': md5_val}) 238 print("send file done...") 239 else: 240 for line in file_obj: 241 self.request.send(line) 242 else: 243 file_obj.close() 244 print("send file done...") 245 else: 246 self.send_response(256) 247 pass
1 #client端 2 # 3 4 import socket 5 import hashlib 6 import os,json 7 import optparse 8 import socketserver 9 10 11 STATUS_CODE = { 12 250 : "Invalid cmd format,e.g:{'action':'get','filename':'test.py','size':344", 13 251 : "Invalid cmd", 14 252 : "Invalid auth data", 15 253 : "Wrong username or password..", 16 254 : "Passed authentication", 17 255 : "Filename doesn't provided", 18 256 : "File doesn't exist on server", 19 257 : "ready to send file", 20 258 : "md5 verification", 21 259 : "Directory has been switched" 22 } 23 24 25 26 27 class FTPClient(object): 28 '''客户端''' 29 def __init__(self): 30 '''用户信息输入格式化 31 变量定义''' 32 parser = optparse.OptionParser() 33 parser.add_option('-s', '--server', dest='server', help ='ftp server ip_addr') 34 parser.add_option('-p','--port', type='int', dest='port', help='ftp server port') 35 parser.add_option('-U', '--username', dest='username', help='username') 36 parser.add_option('-P', '--Password', dest='password', help='password') 37 self.options, self.args = parser.parse_args() 38 self.verify_args(self.options,self.args) 39 self.make_connection() 40 41 42 def make_connection(self): 43 '''连接服务器''' 44 self.sock = socket.socket() 45 self.sock.connect((self.options.server,self.options.port)) 46 47 48 49 def verify_args(self,options,args): 50 '''校验参数合法性''' 51 if options.username and options.password: 52 pass 53 elif options.username is None and options.password is None: 54 pass 55 else: 56 if options.username is None or options.password is None: 57 print('Error:username and password must be provided together..') 58 if options.server and options.port: 59 if options.port > 0 and options.port < 65535: 60 return True 61 else: 62 exit('Err:host port must in 0-65535') 63 64 65 def authenticate(self): 66 '''用户验证''' 67 if self.options.username: 68 return self.get_auth_result(self.options.username, self.options.password) 69 else: 70 retry_count = 0 71 while retry_count < 3: 72 username = input("username:").strip() 73 password = input("password:").strip() 74 return self.get_auth_result(username,password) 75 76 77 78 79 def get_auth_result(self,user,password): 80 '''用户认证''' 81 data = {'action':'auth', 82 'username':user, 83 'password':password} 84 85 self.sock.send(json.dumps(data).encode()) 86 response = self.get_response() 87 if response.get('status_code') == 254: 88 print("Passed authentication!") 89 self.user = user 90 return True 91 else: 92 print(data.get('status_msg')) 93 print('response:',data) 94 95 96 def __md5_required(self,cmd_list): 97 '''检测命令是否需要进行MD5验证''' 98 if '--md5' in cmd_list: 99 return True 100 101 102 def show_progress(self,total): 103 '''进度条''' 104 received_size = 0 105 current_percent = 0 106 while received_size < total: 107 if int((received_size / total) * 100) > current_percent: 108 print(">",end='',flush=True) 109 current_percent = int((received_size / total) * 100) 110 new_size = yield 111 received_size += new_size 112 113 114 def send_response(self,status_code,data=None): 115 '''向服务器端返回数据''' 116 response = {'status_code': status_code, 'status_msg':STATUS_CODE[status_code]} 117 if data: 118 response.update(data) 119 self.sock.send(json.dumps(response).encode()) 120 121 122 def get_response(self): 123 '''得到服务器端回复结果''' 124 data = self.sock.recv(1024) 125 data = json.loads(data.decode()) 126 return data 127 128 129 def _get(self,cmd_list): 130 '''下载文件''' 131 print("get--",cmd_list) 132 if len(cmd_list) == 1: 133 print("no filename follows...") 134 return 135 data_header = { 136 'action':'get', 137 'filename':cmd_list[1] 138 } 139 if self.__md5_required(cmd_list): 140 data_header['md5'] = True 141 142 self.sock.send(json.dumps(data_header).encode()) 143 response = self.get_response() 144 print(response) 145 try: 146 if response["status_code"] == 257:#ready to receive 147 self.sock.send(b'1') #send confirmation to server 148 base_filename = cmd_list[1].split('/')[-1] 149 received_size = 0 150 file_obj = open(base_filename,'wb') 151 if self.__md5_required(cmd_list): 152 md5_obj = hashlib.md5() 153 progress = self.show_progress(response['file_size']) #generator 154 progress.__next__() 155 while received_size < response['file_size']: 156 data = self.sock.recv(4096) 157 received_size += len(data) 158 try: 159 progress.send(len(data)) 160 except StopIteration as e: 161 print("100%") 162 file_obj.write(data) 163 md5_obj.update(data) 164 else: 165 print("-----file rece done-----") 166 file_obj.close() 167 md5_val = md5_obj.hexdigest() 168 md5_from_server = self.get_response() 169 if md5_from_server['status_code'] == 258: 170 if md5_from_server['md5'] == md5_val: 171 print("%s 文件一致性校验成功!"% base_filename) 172 print(md5_val,md5_from_server) 173 174 else: 175 progress = self.show_progress(response['file_size']) # generator 176 progress.__next__() 177 while received_size < response['file_size']: 178 data = self.sock.recv(4096) 179 received_size += len(data) 180 file_obj.write(data) 181 try: 182 progress.send(len(data)) 183 except StopIteration as e: 184 print("100%") 185 else: 186 print("-----file rece done-----") 187 file_obj.close() 188 else: 189 print(STATUS_CODE[response["status_code"]]) 190 except Exception as e: 191 base_file_size = os.path.getsize(base_filename) 192 with open('data\breakpoint', 'wb') as br_po: 193 data_header['action'] = 'breakpoint' 194 data_header['breakpoint'] = base_file_size 195 br_po.write(json.dumps(data_header).encode()) 196 197 198 def _put(self,cmd_list): 199 '''上传文件''' 200 print("put--", cmd_list) 201 if len(cmd_list) == 1: 202 print("no filename follows...") 203 return 204 data_header = { 205 'action': 'put', 206 'filename': cmd_list[1] 207 } 208 if self.__md5_required(cmd_list): 209 data_header['md5'] = True 210 211 self.sock.send(json.dumps(data_header).encode()) 212 if os.path.isfile(cmd_list[1]): 213 file_obj = open(cmd_list[1], 'rb') 214 file_size = os.path.getsize(cmd_list[1]) 215 self.send_response(257, data={'file_size': file_size}) 216 self.sock.recv(1) # 等待服务器端确认 217 if self.__md5_required(cmd_list): 218 md5_obj = hashlib.md5() 219 progress = self.show_progress(file_size) # generator 220 progress.__next__() 221 for line in file_obj: 222 self.sock.send(line) 223 try: 224 progress.send(len(line)) 225 except StopIteration as e: 226 print("100%") 227 md5_obj.update(line) 228 else: 229 file_obj.close() 230 md5_val = md5_obj.hexdigest() 231 md5_from_server = self.get_response() 232 if md5_from_server['md5'] == md5_val: 233 print("%s 文件一致性校验成功!"% cmd_list[1]) 234 self.send_response(258, {'md5': md5_val}) 235 print("send file done...") 236 else: 237 progress = self.show_progress(file_size) # generator 238 progress.__next__() 239 for line in file_obj: 240 self.sock.send(line) 241 try: 242 progress.send(len(line)) 243 except StopIteration as e: 244 print("100%") 245 else: 246 file_obj.close() 247 print("send file done...") 248 else: 249 print(256) 250 251 252 def _ls(self,*args,**kwargs): 253 '''获取当前目录下的所有文件''' 254 print('ls'.center(30,'-')) 255 data_header = { 256 'action': 'ls', 257 } 258 self.sock.send(json.dumps(data_header).encode()) 259 ls_val = self.sock.recv(1024) 260 ls_val = json.loads(ls_val.decode()) 261 for obj in ls_val: 262 print(obj) 263 264 265 def _cd(self,cmd_list): 266 '''改变工作目录''' 267 print("cd--", cmd_list) 268 if len(cmd_list) == 1: 269 cmd_list.append('') 270 data_header = { 271 'action': 'cd', 272 'path': cmd_list[1] 273 } 274 self.sock.send(json.dumps(data_header).encode()) 275 server_path = self.sock.recv(1024) 276 server_path = json.loads(server_path.decode()) 277 if server_path == 259 : 278 print("%s >" % cmd_list[1],end='') 279 280 281 def _breakpoint(self,*args,**kwargs): 282 '''断点续传''' 283 with open('data\breakpoint', 'rb') as br_po: 284 data_header = json.loads(br_po.read().decode()) 285 br_po.close() 286 if data_header: 287 print(data_header) 288 self.sock.send(json.dumps(data_header).encode()) 289 response = self.get_response() 290 try: 291 if response["status_code"] == 257: # ready to receive 292 self.sock.send(b'1') # send confirmation to server 293 base_filename = data_header['filename'].split('/')[-1] 294 received_size = data_header['breakpoint'] 295 file_obj = open(base_filename, 'ab') 296 file_obj.seek(data_header['breakpoint']) 297 if self.__md5_required(data_header): 298 md5_obj = hashlib.md5() 299 progress = self.show_progress(response['file_size']) # generator 300 progress.__next__() 301 while received_size < response['file_size']: 302 data = self.sock.recv(4096) 303 received_size += len(data) 304 try: 305 progress.send(len(data)) 306 except StopIteration as e: 307 print("100%") 308 file_obj.write(data) 309 md5_obj.update(data) 310 else: 311 print("-----file rece done-----") 312 file_obj.close() 313 md5_val = md5_obj.hexdigest() 314 md5_from_server = self.get_response() 315 if md5_from_server['status_code'] == 258: 316 if md5_from_server['md5'] == md5_val: 317 print("%s 文件一致性校验成功!" % base_filename) 318 with open('data\breakpoint', 'wb') as br_po: 319 br_po.write() 320 print(md5_val, md5_from_server) 321 322 323 324 else: 325 progress = self.show_progress(response['file_size']) # generator 326 progress.__next__() 327 while received_size < response['file_size']: 328 data = self.sock.recv(4096) 329 received_size += len(data) 330 file_obj.write(data) 331 try: 332 progress.send(len(data)) 333 except StopIteration as e: 334 print("100%") 335 else: 336 print("-----file rece done-----") 337 file_obj.close() 338 with open('data\breakpoint', 'wb') as br_po: 339 br_po.write() 340 except Exception as e: 341 base_file_size = os.path.getsize(base_filename) 342 with open('data\breakpoint', 'wb') as br_po: 343 data_header['breakpoint'] = base_file_size 344 br_po.write(json.dumps(data_header).encode()) 345 else:print('There is no need to transfer files..') 346 347 348 def _helps(self,*args,**kwargs): 349 '''帮助信息''' 350 helps = ''' 351 get 文件名 #下载文件 352 put 文件名 #上传文件 353 ls #获取当前目录下的所有文件 354 cd #改变工作目录 355 breakpoint #断点续传''' 356 print(helps) 357 358 359 def interactive(self): 360 '''根据命令分配函数''' 361 if self.authenticate(): 362 print('---start interactive iwth u...(helps 帮助信息)') 363 while True: 364 choice = input("[%s]:" % self.user).strip() 365 if len(choice) == 0: continue 366 cmd_list = choice.split() 367 if hasattr(self, "_%s" % cmd_list[0]): 368 func = getattr(self, "_%s" % cmd_list[0]) 369 func(cmd_list) 370 else: 371 print("Invalid cmd") 372 373 374 375 if __name__ == '__main__': 376 ftp = FTPClient() 377 ftp.interactive()