Introduction à l'utilisation du serveur FTP avancé
高级FTP服务器 1. 用户加密认证 2. 多用户同时登陆 3. 每个用户有自己的家目录且只能访问自己的家目录 4. 对用户进行磁盘配额、不同用户配额可不同 5. 用户可以登陆server后,可切换目录 6. 查看当前目录下文件 7. 上传下载文件,保证文件一致性 8. 传输过程中现实进度条 9.支持断点续传 10.用户操作日志 服务端 启动参数 start 客户端 启动参数 -s localhost -P 9500 程序结构: seniorFTP/#综合目录 |- - -ftp_client/#客户端程序目录 | |- - -__init__.py | |- - -bin/#启动目录 | | |- - -__init__.py | | |- - -client_ftp.py#客户端视图启动 | | | |- - -cfg/#配置目录 | | |- - -__init__.py | | |- - -config.py#配置文件 | | | |- - -down/#下载文件目录 | | | |- - -putfile/#上传文件目录 | | | | | |- - -REDMAE |- - -ftp_server/#服务端程序目录 | |- - -__init__.py | |- - -bin/#启动目录 | | |- - -__init__.py | | |- - -start.py#服务端视图启动 | | |- - -user_reg.py#用户注册启动 | | | |- - -cfg/#配置目录 | | |- - -__init__.py | | |- - -config.py#配置文件 | | |- - -userpwd.cfg#用户信息文件 | | | |- - -core/#文件目录 | | |- - -__init__.py | | |- - -ftp_server.py#服务端主要逻辑 类
| | |- - -logs.py#日志主要逻辑 类
| | |- - -main.py#服务端启动主程序 | | | |- - -home/#用户文件目录 | | |- - -用户/#个人目录 | | | |- - -log/#日志文件目录 | | | |- - -REDMAE | | | |- - -REDMAE 先上流程图:
详细代码如下: |- - -ftp_client/#客户端程序目录 | |- - -__init__.py | |- - -bin/#启动目录 | | |- - -__init__.py | | |- - -client_ftp.py#客户端视图启动
<br>


1 #!usr/bin/env python 2 #-*-coding:utf-8-*- 3 # Author calmyan 4 import socket,os,json,getpass,hashlib 5 import os ,sys,optparse 6 7 STATUS_CODE={ 8 240:'格式出错,格式:{"action":"get","filename":"filename","size":100}', 9 241:'指令错误', 10 242:'用户密码出错', 11 243:'用户或密码出错', 12 244:'用户密码通过校验', 13 } 14 BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#获取相对路径转为绝对路径赋于变量 15 sys.path.append(BASE_DIR)#增加环境变量 16 from cfg import config 17 class FTPClient(object): 18 def __init__(self): 19 paresr=optparse.OptionParser() 20 paresr.add_option('-s','--server',dest='server',help='服务器地址') 21 paresr.add_option('-P','--port',type="int",dest='port',help='服务器端口') 22 paresr.add_option('-u','--username',dest='username',help='用户名') 23 paresr.add_option('-p','--password',dest='password',help='密码') 24 (self.options,self.args)=paresr.parse_args()#返回一个字典与列表的元组 25 self.verify_args(self.options,self.args)#判断参数 26 self.ser_connect()#连接服务端 27 self.cmd_list=config.CMD_LIST 28 self.rat=0#文件断点 29 30 #实例化一个连接端 31 def ser_connect(self): 32 self.c=socket.socket()#实例化一个连接端 33 self.c.connect((self.options.server,self.options.port))#进行连接 34 35 #判断用户与密码是否成对出现 36 def verify_args(self,options,args): 37 if (options.username is None and options.password is None) or (options.username is not None and options.password is not None):#判断用户与密码是否成对出现 38 pass##判断用户与密码单个出现 39 else: 40 exit('出错:请输入用户与密码!')#退出 41 if options.server and options.port:#端口判断 42 if options.port>0 and options.port<65535: 43 return True 44 else: 45 print('端口号:[%s]错误,端口范围:0-65535'%options.port) 46 47 #登陆方法 48 def landing(self):#登陆方法 49 '''用户验证''' 50 if self.options.username is not None:#判断用户名已经输入 51 #print(self.options.username,self.options.password) 52 return self.get_user_pwd(self.options.username,self.options.password)#返回结果 53 else: 54 print('用户登陆'.center(60,'=')) 55 ret_count=0#验证次数 56 while ret_count<5: 57 username=input('用户名:').strip() 58 password=getpass.getpass('密码:').strip() 59 if self.get_user_pwd(username,password): 60 return self.get_user_pwd(username,password)#调用远程验证用户 返回结果 61 else: 62 ret_count+=1#次数加一 63 print('认证出错次数[%s]'%ret_count) 64 else: 65 print('密码出错次数过多!') 66 exit() 67 68 #'''用户名与密码检验''' 69 def get_user_pwd(self,username,password): 70 '''用户名与密码检验''' 71 #发送 头文件 72 data={ 73 'action':'auth', 74 'username':username, 75 'password':password 76 } 77 self.c.send(json.dumps(data).encode())#发送到服务器 78 response = self.get_response()#得到服务端的回复 79 if response.get('status_code') == 244: 80 print(STATUS_CODE[244]) 81 self.user = username#存下用户名 82 self.user_dir=response.get('dir')#目录 83 return True 84 else: 85 print(response.get("status_msg") ) 86 87 #服务器回复 88 def get_response(self):#服务器回复 89 '''服务器回复信息''' 90 data=self.c.recv(1024)#接收回复 91 data = json.loads(data.decode()) 92 return data 93 94 #指令帮助 95 def help(self):#指令帮助 96 attr=''' 97 help 指令帮助 98 ---------------------------------- 99 info 个人信息100 ----------------------------------101 ls 查看当前目录(linux/windows)102 ----------------------------------103 pwd 查看当前路径(linux/windows)104 ----------------------------------105 cd 目录 切换目录(linux/windows)106 ----------------------------------107 get filename 下载文件108 ----------------------------------109 put filename 上传文件110 ----------------------------------111 --md5 使用md5 在get/put 后112 ----------------------------------113 mkdir name 创建目录(linux/windows)114 ----------------------------------115 rmdir name 删除目录(linux/windows)116 ----------------------------------117 rm filename 删除文件 (linux/windows)118 ----------------------------------119 exit 退出120 ----------------------------------121 '''.format()122 print(attr)123 124 ##交互125 def inter(self):#交互126 if self.landing():#通过用户密码认证127 print('指令界面'.center(60,'='))128 self.help()129 while True:130 cmd = input('[%s]-->指令>>>:'%self.user_dir).strip()131 if len(cmd)==0:continue#输入空跳过132 if cmd=='exit':exit()#退出指令133 cmd_str=cmd.split()#用空格分割 取命令到列表134 #print(cmd_str)135 #print(len(cmd_str))136 if len(cmd_str)==1 and cmd_str[0] in self.cmd_list:#如果是单个命令 并且在命令列表中137 #if len(cmd_str)==1:#如果是单个命令 并且在命令列表中138 if cmd_str[0]==config.HELP:139 self.help()140 continue141 func=getattr(self,'cmd_compr')#调用此方法142 ret=func(cmd_str)143 if ret:144 continue145 else:146 pass147 elif len(cmd_str)>1:148 if hasattr(self,'cmd_%s'%cmd_str[0]):#判断类中是否有此方法149 func=getattr(self,'cmd_%s'%cmd_str[0])#调用此方法150 func(cmd_str)#执行151 continue152 else:153 print('指令出错!')154 self.help()#155 156 #'''是否要md5'''157 def cmd_md5_(self,cmd_list):158 '''是否要md5'''159 if '--md5' in cmd_list:160 return True161 162 #进度条163 def show_pr(self,total):#进度条164 received_size = 0 #发送的大小165 current_percent = 0 #166 while received_size < total:167 if int((received_size / total) * 100 ) > current_percent :168 print("#",end="",flush=True)#进度显示169 current_percent = int((received_size / total) * 100 )170 new_size = yield #断点跳转 传入的大小171 received_size += new_size172 173 #单个命令174 def cmd_compr(self,cmd_str,**kwargs):175 mag_dict={176 "action":"compr",177 'actionname':cmd_str[0]178 }179 self.c.send(json.dumps(mag_dict).encode('utf-8'))#发送数据180 cmd_res_attr=self.get_response()#得到服务器的回复181 if type(cmd_res_attr) is not int:#如果不int 类型182 if cmd_res_attr["status_code"] ==241:#命令不对183 print(cmd_res_attr['status_msg'])184 return185 if cmd_res_attr["status_code"] ==240:#命令不对186 print(cmd_res_attr['status_msg'])187 return188 size_l=0#收数据当前大小189 self.c.send('准备好接收了,可以发了'.encode('utf-8'))190 receive_data= ''.encode()191 while size_l< cmd_res_attr:192 data=self.c.recv(1024)#开始接收数据193 size_l+=len(data)#加上194 receive_data += data195 else:196 receive_data=receive_data.decode()197 try:198 receive_data=eval(receive_data)#转为列表 或字典199 except Exception as e:200 pass201 if type(receive_data) is dict:#如果是字典202 for i in receive_data:203 print(i,receive_data[i])204 return 1205 if type(receive_data) is list:#如果是列表206 for i in receive_data:207 print(i)208 return 1209 print(receive_data)210 return 1211 212 #切换目录213 def cmd_cd(self,cmd_list,**kwargs):214 '''切换目录'''215 mag_dict={216 "action":"cd",217 'actionname':cmd_list[1]218 }219 self.c.send(json.dumps(mag_dict).encode('utf-8'))#发送数据220 msg_l=self.c.recv(1024)#接收数据 消息221 data=json.loads(msg_l.decode())222 if data["status_code"] ==251:#目录不可切换223 print(data['status_msg'])224 return225 elif data["status_code"] ==252:#目录可以换226 print(data['status_msg'])227 self.c.send(b'1')#发送到服务器,表示可以了228 data=self.c.recv(1024)229 print(data.decode())230 user_dir=data.decode()231 print(user_dir)232 self.user_dir=user_dir233 return234 elif data["status_code"] ==256:#目录不存在235 print(data['status_msg'])236 return237 238 #删除文件239 def cmd_rm(self,cmd_list,**kwargs):240 mag_dict={241 "action":"rm",242 'filename':cmd_list[1]243 }244 self.c.send(json.dumps(mag_dict).encode('utf-8'))#发送文件信息245 data=self.get_response()#得到服务器的回复246 if data["status_code"] ==245:#文件不存在247 print(data['status_msg'])248 #print('删除前空间:',data['剩余空间'])249 return250 elif data["status_code"] ==254:#文件删除完成251 print(data['status_msg'])252 print('删除前空间:',data['剩余空间'])253 pass254 self.c.send(b'1')#发送到服务器,表示可以255 data=self.get_response()#得到服务器的回复256 if data["status_code"] ==255:#文件删除完成257 print(data['status_msg'])258 print('删除后空间:',data['剩余空间'])259 return260 261 #创建目录262 def cmd_mkdir(self,cmd_list,**kwargs):263 mag_dict={264 "action":"mkdir",265 'filename':cmd_list[1]266 }267 self.c.send(json.dumps(mag_dict).encode('utf-8'))#发送文件信息268 data=self.get_response()#得到服务器的回复269 if data["status_code"] ==257:#目录已经存在270 print(data['status_msg'])271 return272 elif data["status_code"] ==256:#目录创建中273 print(data['目录'])274 pass275 self.c.send(b'1')#发送到服务器,表示可以276 data=self.get_response()#得到服务器的回复277 if data["status_code"] ==258:#目录创建中完成278 print(data['status_msg'])279 return280 pass281 282 #删除目录283 def cmd_rmdir(self,cmd_list,**kwargs):284 mag_dict={285 "action":"rmdir",286 'filename':cmd_list[1]287 }288 self.c.send(json.dumps(mag_dict).encode('utf-8'))#发送文件信息289 data=self.get_response()#得到服务器的回复290 if data["status_code"] ==256:#目录不存在291 print(data['status_msg'])292 return293 elif data["status_code"] ==260:#目录不为空294 print(data['status_msg'])295 print(data['目录'])296 return297 elif data["status_code"] ==257:#目录删除中298 print(data['目录'])299 pass300 self.c.send(b'1')#发送到服务器,表示可以301 data=self.get_response()#得到服务器的回复302 if data["status_code"] ==259:#目录删除完成303 print(data['status_msg'])304 return305 pass306 307 #上传方法308 def cmd_put(self,cmd_list,**kwargs):#上传方法309 if len(cmd_list) > 1:310 filename=cmd_list[1]#取文件名311 filename_dir=config.PUT_DIR+filename#拼接文件名路径312 313 if os.path.isfile(filename_dir):#是否是一个文件314 filesize=os.stat(filename_dir).st_size#获取文件大小315 #执行行为 名字,大小,是否316 mag_dict={317 "action":"put",318 'filename':filename,319 'size':filesize,320 'overridden':True,321 'md5':False322 }323 if self.cmd_md5_(cmd_list):#判断是否进行MD5324 mag_dict['md5'] = True325 self.c.send(json.dumps(mag_dict).encode('utf-8'))#发送文件信息326 data=self.get_response()#得到服务器的回复327 if data["status_code"] ==250:#磁盘空间不足328 print(data['status_msg'])329 print(mag_dict['size'])330 return331 if data["status_code"] ==249:#磁盘空间足够332 print(data['status_msg'])333 print('剩余空间',data['剩余空间'])334 self.c.send(b'1')#发送到服务器,表示可以上传文件了335 data=self.get_response()#得到服务器的回复336 if data["status_code"] ==230:#断点续传337 print(data['status_msg'])338 print(data['文件大小'])339 self.rat=data['文件大小']#文件指针位置340 pass341 elif data["status_code"] ==231:#非断点续传342 print(data['status_msg'])343 self.rat=0#文件指针位置344 pass345 f=open(filename_dir,'rb')#打开文件346 f.seek(self.rat)#移动到位置347 print(mag_dict['md5'])348 self.c.send(b'1')#发送到服务器,表示可以上传文件了349 if mag_dict['md5']==True:350 md5_obj = hashlib.md5()#定义MD5351 progress = self.show_pr(mag_dict['size']) #进度条 传入文件大小352 progress.__next__()353 while self.rat<filesize:354 line=f.read(1024)355 self.c.send(line)356 try:357 progress.send(len(line))#传入当前数据大小358 except StopIteration as e:359 print("100%")360 break361 md5_obj.update(line)#计算MD5362 363 else:364 print(filename,'发送完成!')365 f.close()366 md5_val = md5_obj.hexdigest()367 md5_from_server = self.get_response()#服务端的MD5368 if md5_from_server['status_code'] == 248:369 if md5_from_server['md5'] == md5_val:370 print("%s 文件一致性校验成功!" % filename)371 return372 else:373 progress = self.show_pr(mag_dict['size']) #进度条 传入文件大小374 progress.__next__()375 #for line in f:376 while self.rat<filesize:377 line=f.read(1024)378 self.c.send(line)379 try:380 progress.send(len(line))#传入当前数据大小381 except StopIteration as e:382 print("100%")383 break384 #print(line)385 else:386 print(filename,'发送完成!')387 f.close()388 return389 else:390 print(filename,'文件不存在!')391 392 #下载方法393 def cmd_get(self,cmd_list,**kwargs):#下载方法394 #cmd_split= args[0].split()#指令解析395 # if len(cmd_list) == 1:396 # print("没有输入文件名.")397 # return398 #down_filename = cmd_list[1].split('/')[-1]#文件名399 down_filename=cmd_list[1]#取文件名400 file_path='%s/%s'%(config.GET_DIR,down_filename)#拼接文件路径 用户down目录401 if os.path.isfile(file_path):#文件是否存402 filesize=os.stat(file_path).st_size#获取文件大小403 name_down=True404 else:405 filesize=0406 name_down=False407 mag_dict={408 "action":"get",409 'filename':cmd_list[1],410 'name_down':name_down,411 'size':filesize412 }413 if self.cmd_md5_(cmd_list):#判断是否进行MD5414 mag_dict['md5'] = True415 self.c.send(json.dumps(mag_dict).encode())#发送416 self.c.send(b'1')#发送到服务器,防粘包417 418 response = self.get_response()#服务器返回文件 的信息419 if response["status_code"] ==247:#如文件存在420 if name_down==True and response['file_size']==filesize:421 print('文件已经下载完成')422 self.c.send(b'2')423 return424 self.c.send(b'1')#发送到服务器,表示可以接收文件了425 #if name_down:426 received_size = filesize#当前接收的数据大小427 #else:428 #received_size = 0#当前接收的数据大小429 430 file_obj = open(file_path,"ab")#打开文件431 if self.cmd_md5_(cmd_list):432 md5_obj = hashlib.md5()433 progress = self.show_pr(response['file_size']) #进度条 传入文件大小434 progress.__next__()435 while received_size< response['file_size']:436 if response['file_size'] - received_size>1024:#表示接收不止一次437 size=1024438 else:#最后一次439 size=response['file_size'] - received_size440 #print('最后一个大小',size)441 data= self.c.recv(size)#接收数据442 443 try:444 progress.send(len(data))#传入当前数据大小445 except StopIteration as e:446 print("100%")447 received_size+=len(data)#接收数据大小累加448 file_obj.write(data)#写入文件449 md5_obj.update(data)#进行MD5验证450 else:451 print("下载完成".center(60,'-'))452 file_obj.close()453 md5_val = md5_obj.hexdigest()#获取MD5454 #print(md5_val)455 md5_from_server = self.get_response()#服务端的MD5456 #print(md5_from_server['md5'])457 if md5_from_server['status_code'] == 248:458 if md5_from_server['md5'] == md5_val:459 print("%s 文件一致性校验成功!" % down_filename)460 pass461 else:462 progress = self.show_pr(response['file_size']) #进度条 传入文件大小463 progress.__next__()464 while received_size< response['file_size']:465 if response['file_size'] - received_size>1024:#表示接收不止一次466 size=1024467 else:#最后一次468 size=response['file_size'] - received_size469 #print('最后一个大小',size)470 data= self.c.recv(size)#接收数据471 472 try:473 progress.send(len(data))#传入当前数据大小474 except StopIteration as e:475 print("100%")476 received_size+=len(data)#接收数据大小累加477 file_obj.write(data)#写入文件478 pass479 480 else:481 print("下载完成".center(60,'-'))482 file_obj.close()483 pass484 self.c.send(b'1')#发送到服务器,表示可以接收文件了485 486 if __name__=='__main__':487 488 c=FTPClient()489 c.inter()
| |- - -cfg/#配置目录 | | |- - -__init__.py | | |- - -config.py#配置文件


1 #!usr/bin/env python 2 #-*-coding:utf-8-*- 3 # Author calmyan 4 5 import os ,sys 6 BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#获取相对路径转为绝对路径赋于变量 7 sys.path.append(BASE_DIR)#增加环境变量 8 #print(BASE_DIR) 9 10 PUT_DIR=BASE_DIR+'\putfile\\'#定义用户上传目录文件路径变量11 GET_DIR=BASE_DIR+'\down\\'#定义用户下载目录文件路径变量12 HELP='help'13 CMD_LIST=['ls','pwd','info','help']
|- - -ftp_server/#服务端程序目录 | |- - -__init__.py | |- - -bin/#启动目录 | | |- - -__init__.py | | |- - -start.py#服务端视图启动


1 #!usr/bin/env python 2 #-*-coding:utf-8-*- 3 # Author calmyan 4 import socket,os,json 5 import os ,sys 6 BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#获取相对路径转为绝对路径赋于变量 7 sys.path.append(BASE_DIR)#增加环境变量 8 9 from core import main10 11 if __name__ == '__main__':12 13 main.ArvgHandler()
| | |- - -user_reg.py#用户注册启动


1 #!usr/bin/env python 2 #-*-coding:utf-8-*- 3 # Author calmyan 4 5 import configparser 6 import os ,sys 7 BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#获取相对路径转为绝对路径赋于变量 8 sys.path.append(BASE_DIR)#增加环境变量 9 from cfg import config10 #修改个信息 磁盘大小11 def set_info(name,pwd,size):12 config_info=configparser.ConfigParser()#读数据13 config_info.read(config.AUTH_FILE)#读文件 用户名密码14 #print(config_info.options(name))15 config_info[name]={}16 config_info.set(name,config.PWD,pwd)#密码17 config_info.set(name,config.QUOTATION,size)#磁盘信息18 config_info.write(open(config.AUTH_FILE,'w'))#写入文件19 file_path='%s/%s'%(config.USER_HOME,name)#拼接目录路径20 os.mkdir(file_path)#创建目录21 print('创建完成'.center(60,'='))22 print('用户名:[%s]\n密码:[%s]\n磁盘空间:[%s]'%(name,pwd,size))23 24 if __name__ == '__main__':25 name=input('name:')26 pwd=input('pwd:')27 size=input('size:')28 set_info(name,pwd,size)
| | |- - -userpwd.cfg#用户信息文件


1 #!usr/bin/env python 2 #-*-coding:utf-8-*- 3 # Author calmyan 4 5 import os ,sys 6 BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#获取相对路径转为绝对路径赋于变量 7 sys.path.append(BASE_DIR)#增加环境变量 8 #HOME_PATH = os.path.join(BASE_DIR, "home") 9 10 11 12 #USER_DIR='%s\\data\\'%BASE_DIR#定义用户数据目录文件路径变量13 #USER_DIR='%s/data'%BASE_DIR#定义用户数据目录文件路径变量14 #USER_HOME='%s\\home\\'%BASE_DIR#定义用户家目录文件路径变量15 USER_HOME='%s/home'%BASE_DIR#定义用户家目录文件路径变量16 #LOG_DIR='%s\\log\\'%BASE_DIR#日志目录17 USER_LOG='%s/log/user_log.log'%BASE_DIR#日志登陆文件18 USER_OPERT='%s/log/user_opert.log'%BASE_DIR#日志操作文件19 20 LOG_LEVEL='DEBUG'#日志级别21 22 AUTH_FILE='%s/cfg/userpwd.cfg'%BASE_DIR#用户名密码文件23 HOST='0.0.0.0'# IP24 PORT=9500#端口25 QUOTATION='Quotation'#磁盘空间26 PWD='PWD'#密码
| |- - -core/#服务端主要文件目录 | | |- - -__init__.py | | |- - -ftp_server.py#服务端主要逻辑 类


1 #!usr/bin/env python 2 #-*-coding:utf-8-*- 3 # Author calmyan 4 import socketserver,os,json,pickle,configparser,time 5 time_format='%Y%m%d%H%M%S'#定义时间格式 6 times=time.strftime(time_format)#定义时间 7 8 STATUS_CODE={ 9 230:'文件断点继传', 10 231:'新文件', 11 240:'格式出错,格式:{"action":"get","filename":"filename","size":100}', 12 241:'指令错误', 13 242:'用户名或密码为空', 14 243:'用户或密码出错', 15 244:'用户密码通过校验', 16 245:'文件不存在或不是文件', 17 246:'服务器上该文件不存在', 18 247:'准备发送文件,请接收', 19 248:'md5', 20 249:'准备接收文件,请上传', 21 250:'磁盘空间不够', 22 251:'当前已经为主目录', 23 252:'目录正在切换', 24 253:'正在查看路径', 25 254:'准备删除文件', 26 255:'删除文件完成', 27 256:'目录不存在', 28 257:'目录已经存在', 29 258:'目录创建完成', 30 259:'目录删除完成', 31 260:'目录不是空的', 32 } 33 import os ,sys,hashlib 34 BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#获取相对路径转为绝对路径赋于变量 35 sys.path.append(BASE_DIR)#增加环境变量 36 from cfg import config 37 from core.logs import log_log 38 from core.logs import user_opert 39 40 41 class MyTCPHandler (socketserver.BaseRequestHandler):# 42 43 def setup(self): 44 print('监听中。。。') 45 #'''用户名与密码是否为空''' 46 def cmd_auth(self,*args,**kwargs):#用户校验 47 '''用户名与密码是否为空''' 48 data=args[0]#获取 传来的数据 49 if data.get('username') is None or data.get('password') is None:#如果用户名或密码为空 50 self.send_mge(242)#发送错误码 51 name=data.get('username')#用户名 52 pwd=data.get('password')#密码 53 print(name,pwd) 54 user=self.authusername(name,pwd)#用户名与密码的校验 获名用户名 55 if user is None:#用户名不存在 56 self.send_mge(243) 57 else: 58 self.user=name#保存用户名 59 self.home_dir='%s/%s'%(config.USER_HOME,self.user)#拼接 用户home目录路径 用户根目录 60 self.user_home_dir=self.home_dir#当前所在目录 61 # self.user_dir=self.user_home_dir.split('/')[-1]#当前所在目录 相对 62 self.dir_join()#进行目录拼接 63 self.send_mge(244,data={'dir':self.user_dir})#相对 目录 64 65 #目录拼接 66 def dir_join(self,*args,**kwargs): 67 self.user_dir=self.user_home_dir.split(self.home_dir)[-1]+'/'#当前所在目录 相对 68 print(self.user_dir) 69 70 #'''用户名与密码的校验'' 71 def authusername(self,name,pwd): 72 '''用户名与密码的校验''' 73 config_info=configparser.ConfigParser()#读数据 74 config_info.read(config.AUTH_FILE)#读文件 用户名密码 75 if name in config_info.sections():#用户名存 76 password=config_info[name]['PWD'] 77 if password==pwd:#密码正确 78 print('通过校验!') 79 config_info[name]['USERname']=name#名字的新字段 80 info_str='用户[%s],成功登陆'%name 81 self.log_log.warning(info_str)#记录日志 82 #log_log(info_str) 83 return config_info[name] 84 else: 85 info_str='用户[%s],登陆错误'%name 86 #log_log(info_str) 87 self.log_log.warning(info_str)#记录日志 88 return 0 89 90 #判断文件 是否存在 91 def file_name(self,file_path): 92 if os.path.isfile(file_path):#文件是否存 93 return True 94 else: 95 return False 96 97 #判断目录是否存在 98 def file_dir(self,file_path): 99 if os.path.isdir(file_path):#目录是否存100 return True101 else:102 return False103 104 #删除文件105 def cmd_rm(self,*args,**kwargs):106 cmd_dict=args[0]#获取字典107 action=cmd_dict["action"]108 filename =cmd_dict['filename']#文件名109 file_path='%s/%s'%(self.user_home_dir,filename)#拼接文件路径110 if not self.file_name(file_path):111 self.send_mge(245)#文件不存在112 return113 else:114 user_size=self.disk_size()#获取磁盘信息115 self.send_mge(254,data={'剩余空间':user_size})#准备删除文件116 file_size=os.path.getsize(file_path)#获取文件大小117 pass118 self.request.recv(1) #客户端确认 防粘包119 os.remove(file_path)120 new_size=float((float(user_size)+float(file_size))/1024000)#空间大小增加121 self.set_info(str(new_size))#传入新大小122 self.send_mge(255,data={'剩余空间':new_size})#删除文件完成123 info_str=self.log_str('删除文件')#生成日志信息124 self.user_opert.critical(info_str)#记录日志125 return126 127 #创建目录128 def cmd_mkdir(self,*args,**kwargs):129 cmd_dict=args[0]#获取字典130 action=cmd_dict["action"]131 filename =cmd_dict['filename']#目录名132 file_path='%s/%s'%(self.user_home_dir,filename)#拼接目录路径133 if self.file_dir(file_path):134 self.send_mge(257)#目录已经 存在135 return136 else:137 self.send_mge(256,data={'目录':'创建中...'})#目录创建中138 self.request.recv(1) #客户端确认 防粘包139 os.mkdir(file_path)#创建目录140 self.send_mge(258)#目录完成141 info_str=self.log_str('创建目录')#生成日志信息142 self.user_opert.critical(info_str)#记录日志143 return144 145 #删除目录146 def cmd_rmdir(self,*args,**kwargs):147 cmd_dict=args[0]#获取字典148 action=cmd_dict["action"]149 filename =cmd_dict['filename']#目录名150 file_path='%s/%s'%(self.user_home_dir,filename)#拼接目录路径151 if not self.file_dir(file_path):152 self.send_mge(256)#目录不存在153 return154 elif os.listdir(file_path):155 self.send_mge(260,data={'目录':'无法删除'})#目录不是空的156 return157 else:158 self.send_mge(257,data={'目录':'删除中...'})#目录创建中159 self.request.recv(1) #客户端确认 防粘包160 os.rmdir(file_path)#删除目录161 self.send_mge(259)#目录删除完成162 info_str=self.log_str('删除目录')#生成日志信息163 self.user_opert.critical(info_str)#记录日志164 return165 166 #磁盘空间大小167 def disk_size(self):168 attr_list=self.user_info()#调用个人信息169 put_size=attr_list[1]#取得磁盘信息170 user_size=float(put_size)*1024000#字节171 return user_size172 173 #'''客户端上传文件 '''174 def cmd_put(self,*args,**kwargs):175 '''客户端上传文件 '''176 cmd_dict=args[0]#获取字典177 filename =cmd_dict['filename']#文件名178 file_size= cmd_dict['size']#文件大小179 #user_home_dir='%s/%s'%(config.USER_HOME,self.user)#拼接 用户home目录路径180 file_path='%s/%s'%(self.user_home_dir,filename)#拼接文件路径181 user_size=self.disk_size()#取得磁盘信息182 if float(file_size)>float(user_size):#空间不足183 self.send_mge(250,data={'剩余空间':user_size})184 return185 self.send_mge(249,data={'剩余空间':user_size})#发送一个确认186 self.request.recv(1) #客户端确认 防粘包187 if self.file_name(file_path):#判断文件名是否存在,188 s_file_size=os.path.getsize(file_path)##获取服务器上的文件大小189 if file_size>s_file_size:#如果服务器上的文件小于要上传的文件进190 tmp_file_size=os.stat(file_path).st_size#计算临时文件大小191 reversed_size=tmp_file_size#接收到数据大小192 self.send_mge(230,data={'文件大小':reversed_size})#发送临时文件大小193 pass194 else:# file_size==s_file_size:#如果大小一样195 file_path=file_path+'_'+times#命名新的文件 名196 reversed_size=0#接收到数据大小197 self.send_mge(231)#发送 不是断点文件198 pass199 else:200 reversed_size=0#接收到数据大小201 self.send_mge(231)#发送 不是断点文件202 pass203 204 f=open(file_path,'ab')205 self.request.recv(1) #客户端确认 防粘包206 if cmd_dict['md5']:#是否有 md5207 md5_obj = hashlib.md5() # 进行MD5208 while reversed_size< int(file_size):#接收小于文件 大小209 if int(file_size) - reversed_size>1024:#表示接收不止一次210 size=1024211 else:#最后一次212 size=int(file_size) - reversed_size213 #print('最后一个大小',size)214 data= self.request.recv(size)#接收数据215 md5_obj.update(data)216 reversed_size+=len(data)#接收数据大小累加217 f.write(data)#写入文件218 else:219 f.close()220 print('[%s]文件上传完毕'.center(60,'-')%filename)221 md5_val = md5_obj.hexdigest()#得出MD5222 print(md5_val)223 self.send_mge(248,{'md5':md5_val})#发送md5给客户端224 else:225 while reversed_size< int(file_size):#接收小于文件 大小226 if int(file_size) - reversed_size>1024:#表示接收不止一次227 size=1024228 else:#最后一次229 size=int(file_size) - reversed_size230 #print('最后一个大小',size)231 data= self.request.recv(size)#接收数据232 reversed_size+=len(data)#接收数据大小累加233 f.write(data)#写入文件234 else:235 print('[%s]文件上传完毕'%filename.center(60,'-'))236 f.close()237 new_size=float((float(user_size)-float(file_size))/1024000)#扣除空间大小238 self.set_info(str(new_size))#传入新大小239 info_str=self.log_str('文件上传')#生成日志信息240 self.user_opert.critical(info_str)#记录日志241 return242 243 #用户下载文件244 def cmd_get(self,*args,**kwargs):#用户下载文件245 ''' 用户下载文件'''246 data=args[0]247 print(data)248 if data.get('filename') is None:#判断文件名不为空249 self.send_mge(245)250 return251 252 self.request.recv(1) #客户端确认 防粘包253 file_path='%s/%s'%(self.user_home_dir,data.get('filename'))#拼接文件路径 用户文件路径254 if os.path.isfile(file_path):#判断文件是否存在255 file_obj=open(file_path,'rb')#打开文件句柄\256 file_size=os.path.getsize(file_path)#获取文件大小257 if data['name_down']:258 send_size=data['size']#已经发送数据大小259 #self.send_mge(230,data={'文件大小':file_size})#断点续传260 else:261 send_size=0262 #self.send_mge(231)#非断点续传263 #self.request.recv(1) #客户端确认 防粘包264 file_obj.seek(send_size)#移动到265 self.send_mge(247,data={'file_size':file_size})#发送相关信息266 attr=self.request.recv(1024) #客户端确认 防粘包267 if attr.decode()=='2':return #如果返回是268 if data.get('md5'):269 md5_obj = hashlib.md5()270 while send_size<file_size:271 line=file_obj.read(1024)272 #for line in file_obj:273 self.request.send(line)274 md5_obj.update(line)275 else:276 file_obj.close()277 md5_val = md5_obj.hexdigest()278 self.send_mge(248,{'md5':md5_val})279 print("发送完毕.")280 else:281 while send_size<file_size:282 line=file_obj.read(1024)283 #for line in file_obj:284 self.request.send(line)285 else:286 file_obj.close()287 print("发送完毕.")288 self.request.recv(1) #客户端确认 防粘包289 info_str=self.log_str('下载文件')#生成日志信息290 #user_opert(info_str)#记录日志291 self.user_opert.critical(info_str)#记录日志292 return293 294 #切换目录295 def cmd_cd(self,cmd_dict,*args,**kwargs):296 '''切换目录'''297 cmd_attr=cmd_dict['actionname']#获取命令298 if cmd_attr=='..' or cmd_attr=='../..':299 if (self.home_dir)==self.user_home_dir:300 self.send_mge(251)301 return302 elif cmd_attr=='../..':303 self.send_mge(252)#可以切换到上级目录304 self.user_home_dir=self.home_dir#绝对目录 = home305 self.user_dir='/'306 clinet_ack=self.request.recv(1024)#为了去粘包307 self.request.send(self.user_dir.encode())#返回相对目录308 return309 else:310 self.send_mge(252)#可以切换到上级目录311 print(self.user_home_dir)#绝对目录312 print(os.path.dirname(self.user_home_dir))#父级目录313 self.user_home_dir=os.path.dirname(self.user_home_dir)#父级目录314 self.dir_join()#目录拼接切换315 clinet_ack=self.request.recv(1024)#为了去粘包316 self.request.send(self.user_dir.encode())#返回相对目录317 return318 319 elif os.path.isdir(self.user_home_dir+'/'+cmd_attr):#如果目录存在320 self.send_mge(252)321 self.user_home_dir=self.user_home_dir+'/'+cmd_attr#目录拼接322 self.dir_join()#相对目录拼接切换323 clinet_ack=self.request.recv(1024)#为了去粘包324 print(clinet_ack.decode())325 self.request.send(self.user_dir.encode())326 return327 else:328 self.send_mge(256)#目录不存在329 return330 331 #查看目录路径 CD332 def cmd_pwd(self,cmd_dict):333 self.request.send(str(len(self.user_dir.encode('utf-8'))).encode('utf-8'))#发送大小334 clinet_ack=self.request.recv(1024)#为了去粘包335 self.request.send(self.user_dir.encode())#发送相对路径336 info_str=self.log_str('查看目录路径')#生成日志信息337 #logger.warning338 self.user_opert.critical(info_str)#记录日志339 return340 341 #修改个信息 磁盘大小342 def set_info(self,new_size):343 config_info=configparser.ConfigParser()#读数据344 config_info.read(config.AUTH_FILE)#读文件 用户名密码345 print(config_info.options(self.user))346 config_info.set(self.user,config.QUOTATION,new_size)347 config_info.write(open(config.AUTH_FILE,'w'))348 349 #读取个人信息350 def user_info(self):351 config_info=configparser.ConfigParser()#读数据352 config_info.read(config.AUTH_FILE)#读文件 用户名密码353 print(config_info.options(self.user))354 pwds=config_info[self.user][config.PWD]#密码355 Quotation=config_info[self.user][config.QUOTATION]#磁盘配额 剩余356 user_info={}357 user_info['用户名']=self.user358 user_info['密码']=pwds359 user_info['剩余磁盘配额']=Quotation360 return user_info,Quotation361 362 #查看用户信息363 def cmd_info(self,*args,**kwargs):364 attr=self.user_info()365 info_dict=attr[0]366 self.request.send(str(len(json.dumps(info_dict))).encode('utf-8'))#367 clinet_ack=self.request.recv(1024)#为了去粘包368 self.request.send(json.dumps(info_dict).encode('utf-8'))#发送指令369 info_str=self.log_str('查看用户信息')#生成日志信息370 self.user_opert.critical(info_str)#记录日志371 return372 373 #日志信息生成374 def log_str(self,msg,**kwargs):375 info_str='用户[%s]进行了[%s]操作'%(self.user,msg)376 return info_str377 378 379 #目录查看380 def cmd_ls(self,*args,**kwargs):381 data=os.listdir(self.user_home_dir)#查看目录文件382 print(data)383 datas=json.dumps(data)#转成json格式384 self.request.send(str(len(datas.encode('utf-8'))).encode('utf-8'))#发送大小385 clinet_ack=self.request.recv(1024)#为了去粘包386 self.request.send(datas.encode('utf-8'))#发送指令387 info_str=self.log_str('目录查看')#生成日志信息388 self.user_opert.critical(info_str)#记录日志389 return390 ##单个命令391 def cmd_compr(self,cmd_dict,**kwargs):392 attr=cmd_dict['actionname']#赋于变量393 if hasattr(self,'cmd_%s'%attr):#是否存在394 func=getattr(self,'cmd_%s'%attr)#调用395 func(cmd_dict)396 return397 else:398 print('没有相关命令!')399 self.send_mge(241)400 return401 402 #'''发送信息码给客户端'''403 def send_mge(self,status_code,data=None):404 '''发送信息码给客户端'''405 mge={'status_code':status_code,'status_msg':STATUS_CODE[status_code]}#消息406 if data:#不为空407 mge.update(data)#提示码进行更新408 print(mge)409 self.request.send(json.dumps(mge).encode())#发送给客户端410 411 #重写handle方法412 def handle(self):#重写handle方法413 while True:414 #try:415 self.data=self.request.recv(1024).strip()#接收数据416 print('ip:{}'.format(self.client_address[0]))#连接的ip417 print(self.data)418 self.log_log=log_log()#登陆日志419 self.user_opert=user_opert()#操作日志420 if not self.data:421 print("[%s]客户端断开了!."%self.user)422 info_str='用户[%s],退出'%self.user423 424 break425 cmd_dict=json.loads(self.data.decode())#接收 数据426 if cmd_dict.get('action') is not None:#判断数据格式正确427 action=cmd_dict['action']#文件 头428 if hasattr(self,'cmd_%s'%action):#是否存在429 func=getattr(self,'cmd_%s'%action)#调用430 func(cmd_dict)431 else:432 print('没有相关命令!')433 self.send_mge(241)434 else:435 print('数据出错!')436 self.send_mge(240)437 #except Exception as e:438 # print('客户端断开了!',e)439 # break
| | |- - -logs.py#日志主要逻辑 类


1 #!usr/bin/env python 2 #-*-coding:utf-8-*- 3 # Author calmyan 4 import os,logging,time 5 from cfg import config 6 LOG_LEVEL=config.LOG_LEVEL 7 8 9 def log_log():#登陆日志,传入内容10 logger=logging.getLogger('用户成功登陆日志')#设置日志模块11 logger.setLevel(logging.DEBUG)12 fh=logging.FileHandler(config.USER_LOG,encoding='utf-8')#写入文件13 fh.setLevel(config.LOG_LEVEL)#写入信息的级别14 fh_format=logging.Formatter('%(asctime)s %(message)s',datefmt='%m/%d/%Y %I:%M:%S %p')#日志格式15 fh.setFormatter(fh_format)#关联格式16 logger.addHandler(fh)#添加日志输出模式17 #logger.warning(info_str)18 return logger19 20 def user_opert():#用户操作日志,传入内容21 logger=logging.getLogger('用户操作日志')#设置日志模块22 logger.setLevel(logging.CRITICAL)23 fh=logging.FileHandler(config.USER_OPERT,encoding='utf-8')#写入文件24 fh.setLevel(config.LOG_LEVEL)#写入信息的级别25 fh_format=logging.Formatter('%(asctime)s %(message)s',datefmt='%m/%d/%Y %I:%M:%S %p')#日志格式26 fh.setFormatter(fh_format)#关联格式27 logger.addHandler(fh)#添加日志输出模式28 #logger.critical(info_str)29 return logger
| | |- - -main.py#服务端启动主程序


1 #!usr/bin/env python 2 #-*-coding:utf-8-*- 3 # Author calmyan 4 5 import socketserver,os,json,pickle 6 import os ,sys 7 BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#获取相对路径转为绝对路径赋于变量 8 sys.path.append(BASE_DIR)#增加环境变量 9 from cfg import config10 11 12 from core.ftp_server import MyTCPHandler13 14 import optparse15 class ArvgHandler(object):16 def __init__(self):# 可 传入系统参数17 self.paresr=optparse.OptionParser()#启用模块18 #self.paresr.add_option('-s','--host',dest='host',help='服务绑定地址')19 #self.paresr.add_option('-s','--port',dest='host',help='服务端口')20 (options,args)=self.paresr.parse_args()#返回一个字典与列表的元组21 22 self.verufy_args(options,args)#进行校验23 def verufy_args(self,options,args):24 '''校验与调用'''25 if hasattr(self,args[0]):#反射判断参数26 func=getattr(self,args[0])#生成一个实例27 func()#开始调用28 else:29 self.paresr.print_help()#打印帮助文档30 def start(self):31 print('服务启动中....')32 s=socketserver.ThreadingTCPServer((config.HOST,config.PORT),MyTCPHandler)#实例化一个服务端对象33 s.serve_forever()#运行服务器34 print('服务关闭')
<br><br>
<br>
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

AI Hentai Generator
Générez AI Hentai gratuitement.

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Sujets chauds



La vitesse du XML mobile à PDF dépend des facteurs suivants: la complexité de la structure XML. Méthode de conversion de configuration du matériel mobile (bibliothèque, algorithme) Méthodes d'optimisation de la qualité du code (sélectionnez des bibliothèques efficaces, optimiser les algorithmes, les données de cache et utiliser le multi-threading). Dans l'ensemble, il n'y a pas de réponse absolue et elle doit être optimisée en fonction de la situation spécifique.

Il est impossible de terminer la conversion XML à PDF directement sur votre téléphone avec une seule application. Il est nécessaire d'utiliser les services cloud, qui peuvent être réalisés via deux étapes: 1. Convertir XML en PDF dans le cloud, 2. Accédez ou téléchargez le fichier PDF converti sur le téléphone mobile.

Il n'y a pas de fonction de somme intégrée dans le langage C, il doit donc être écrit par vous-même. La somme peut être obtenue en traversant le tableau et en accumulant des éléments: Version de boucle: la somme est calculée à l'aide de la longueur de boucle et du tableau. Version du pointeur: Utilisez des pointeurs pour pointer des éléments de tableau, et un résumé efficace est réalisé grâce à des pointeurs d'auto-incitation. Allouer dynamiquement la version du tableau: allouer dynamiquement les tableaux et gérer la mémoire vous-même, en veillant à ce que la mémoire allouée soit libérée pour empêcher les fuites de mémoire.

Il n'est pas facile de convertir XML en PDF directement sur votre téléphone, mais il peut être réalisé à l'aide des services cloud. Il est recommandé d'utiliser une application mobile légère pour télécharger des fichiers XML et recevoir des PDF générés, et de les convertir avec des API Cloud. Les API Cloud utilisent des services informatiques sans serveur et le choix de la bonne plate-forme est crucial. La complexité, la gestion des erreurs, la sécurité et les stratégies d'optimisation doivent être prises en compte lors de la gestion de l'analyse XML et de la génération de PDF. L'ensemble du processus nécessite que l'application frontale et l'API back-end fonctionnent ensemble, et il nécessite une certaine compréhension d'une variété de technologies.

XML peut être converti en images en utilisant un convertisseur XSLT ou une bibliothèque d'images. Convertisseur XSLT: Utilisez un processeur XSLT et une feuille de style pour convertir XML en images. Bibliothèque d'images: utilisez des bibliothèques telles que PIL ou ImageMagick pour créer des images à partir de données XML, telles que des formes de dessin et du texte.

La validation du format XML consiste à vérifier sa structure et sa conformité avec DTD ou schéma. Un analyseur XML est requis, tel que ElementTree (Basic Syntax Heatking) ou LXML (vérification plus puissante, prise en charge XSD). Le processus de vérification implique l'analyse du fichier XML, le chargement du schéma XSD et l'exécution de la méthode AssertValid pour lancer une exception lorsqu'une erreur est détectée. La vérification du format XML nécessite également de gérer diverses exceptions et de mieux comprendre le langage du schéma XSD.

Il n'y a pas de salaire absolu pour les développeurs Python et JavaScript, selon les compétences et les besoins de l'industrie. 1. Python peut être davantage payé en science des données et en apprentissage automatique. 2. JavaScript a une grande demande dans le développement frontal et complet, et son salaire est également considérable. 3. Les facteurs d'influence comprennent l'expérience, la localisation géographique, la taille de l'entreprise et les compétences spécifiques.

Les étapes pour convertir XML en MP3 incluent: Extraire les données audio de XML: analyser le fichier XML, trouver la chaîne de codage Base64 contenant les données audio et les décoder en format binaire. Encoder les données audio à MP3: Installez l'encodeur MP3 et définissez les paramètres de codage, encodez les données audio binaires au format MP3 et enregistrez-les dans un fichier.
