码迷,mamicode.com
首页 > 编程语言 > 详细

python第四十八天--高级FTP

时间:2017-06-19 15:54:42      阅读:228      评论:0      收藏:0      [点我收藏+]

标签:解析   用户密码   没有   form   关闭   style   asa   dict   war   

高级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#客户端视图启动

技术分享
  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                         continue
141                     func=getattr(self,cmd_compr)#调用此方法
142                     ret=func(cmd_str)
143                     if ret:
144                         continue
145                     else:
146                         pass
147                 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                         continue
152                 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 True
161 
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_size
172 
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                 return
185             if cmd_res_attr["status_code"] ==240:#命令不对
186                 print(cmd_res_attr[status_msg])
187                 return
188         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 += data
195         else:
196             receive_data=receive_data.decode()
197             try:
198                 receive_data=eval(receive_data)#转为列表 或字典
199             except Exception as e:
200                 pass
201             if type(receive_data) is dict:#如果是字典
202                 for i in receive_data:
203                     print(i,receive_data[i])
204                 return 1
205             if type(receive_data) is list:#如果是列表
206                 for i in receive_data:
207                     print(i)
208                 return 1
209             print(receive_data)
210             return 1
211 
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             return
225         elif data["status_code"] ==252:#目录可以换
226             print(data[status_msg])
227             self.c.send(b1)#发送到服务器,表示可以了
228             data=self.c.recv(1024)
229             print(data.decode())
230             user_dir=data.decode()
231             print(user_dir)
232             self.user_dir=user_dir
233             return
234         elif data["status_code"] ==256:#目录不存在
235             print(data[status_msg])
236             return
237 
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             return
250         elif data["status_code"] ==254:#文件删除完成
251             print(data[status_msg])
252             print(删除前空间:,data[剩余空间])
253             pass
254         self.c.send(b1)#发送到服务器,表示可以
255         data=self.get_response()#得到服务器的回复
256         if data["status_code"] ==255:#文件删除完成
257             print(data[status_msg])
258             print(删除后空间:,data[剩余空间])
259             return
260 
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             return
272         elif data["status_code"] ==256:#目录创建中
273             print(data[目录])
274             pass
275         self.c.send(b1)#发送到服务器,表示可以
276         data=self.get_response()#得到服务器的回复
277         if data["status_code"] ==258:#目录创建中完成
278             print(data[status_msg])
279             return
280         pass
281 
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             return
293         elif data["status_code"] ==260:#目录不为空
294             print(data[status_msg])
295             print(data[目录])
296             return
297         elif data["status_code"] ==257:#目录删除中
298             print(data[目录])
299             pass
300         self.c.send(b1)#发送到服务器,表示可以
301         data=self.get_response()#得到服务器的回复
302         if data["status_code"] ==259:#目录删除完成
303             print(data[status_msg])
304             return
305         pass
306 
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:False
322                 }
323                 if self.cmd_md5_(cmd_list):#判断是否进行MD5
324                     mag_dict[md5] = True
325                 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                     return
331                 if data["status_code"] ==249:#磁盘空间足够
332                     print(data[status_msg])
333                     print(剩余空间,data[剩余空间])
334                     self.c.send(b1)#发送到服务器,表示可以上传文件了
335                     data=self.get_response()#得到服务器的回复
336                     if data["status_code"] ==230:#断点续传
337                         print(data[status_msg])
338                         print(data[文件大小])
339                         self.rat=data[文件大小]#文件指针位置
340                         pass
341                     elif data["status_code"] ==231:#非断点续传
342                         print(data[status_msg])
343                         self.rat=0#文件指针位置
344                         pass
345                     f=open(filename_dir,rb)#打开文件
346                     f.seek(self.rat)#移动到位置
347                     print(mag_dict[md5])
348                     self.c.send(b1)#发送到服务器,表示可以上传文件了
349                     if mag_dict[md5]==True:
350                         md5_obj = hashlib.md5()#定义MD5
351                         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                                 break
361                             md5_obj.update(line)#计算MD5
362 
363                         else:
364                             print(filename,发送完成!)
365                             f.close()
366                             md5_val = md5_obj.hexdigest()
367                             md5_from_server = self.get_response()#服务端的MD5
368                             if md5_from_server[status_code] == 248:
369                                 if md5_from_server[md5] == md5_val:
370                                     print("%s 文件一致性校验成功!" % filename)
371                                     return
372                     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                                 break
384                             #print(line)
385                         else:
386                             print(filename,发送完成!)
387                             f.close()
388                             return
389             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         #     return
398         #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=True
404         else:
405             filesize=0
406             name_down=False
407         mag_dict={
408                     "action":"get",
409                     filename:cmd_list[1],
410                     name_down:name_down,
411                     size:filesize
412                 }
413         if self.cmd_md5_(cmd_list):#判断是否进行MD5
414             mag_dict[md5] = True
415         self.c.send(json.dumps(mag_dict).encode())#发送
416         self.c.send(b1)#发送到服务器,防粘包
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(b2)
423                 return
424             self.c.send(b1)#发送到服务器,表示可以接收文件了
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=1024
438                     else:#最后一次
439                         size=response[file_size] - received_size
440                         #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()#获取MD5
454                     #print(md5_val)
455                     md5_from_server = self.get_response()#服务端的MD5
456                     #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                     pass
461             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=1024
467                     else:#最后一次
468                         size=response[file_size] - received_size
469                         #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                     pass
479 
480                 else:
481                     print("下载完成".center(60,-))
482                     file_obj.close()
483                     pass
484             self.c.send(b1)#发送到服务器,表示可以接收文件了
485 
486 if __name__==__main__:
487 
488     c=FTPClient()
489     c.inter()
View Code
|           |- - -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]
View Code
|- - -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 main
10 
11 if __name__ == __main__:
12 
13     main.ArvgHandler()
View Code
|           |      |- - -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 config
10 #修改个信息 磁盘大小
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)
View Code
|           |      |- - -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# IP
24 PORT=9500#端口
25 QUOTATION=Quotation#磁盘空间
26 PWD=PWD#密码
View Code
|           |- - -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  True
101         else:
102             return False
103 
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             return
113         else:
114             user_size=self.disk_size()#获取磁盘信息
115             self.send_mge(254,data={剩余空间:user_size})#准备删除文件
116             file_size=os.path.getsize(file_path)#获取文件大小
117             pass
118         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         return
126 
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             return
136         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             return
144 
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             return
154         elif os.listdir(file_path):
155             self.send_mge(260,data={目录:无法删除})#目录不是空的
156             return
157         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             return
165 
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_size
172 
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             return
185         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                 pass
194             else:# file_size==s_file_size:#如果大小一样
195                 file_path=file_path+_+times#命名新的文件 名
196                 reversed_size=0#接收到数据大小
197                 self.send_mge(231)#发送 不是断点文件
198                 pass
199         else:
200             reversed_size=0#接收到数据大小
201             self.send_mge(231)#发送 不是断点文件
202             pass
203 
204         f=open(file_path,ab)
205         self.request.recv(1) #客户端确认 防粘包
206         if cmd_dict[md5]:#是否有 md5
207             md5_obj = hashlib.md5() #   进行MD5
208             while reversed_size< int(file_size):#接收小于文件 大小
209                 if int(file_size) - reversed_size>1024:#表示接收不止一次
210                     size=1024
211                 else:#最后一次
212                     size=int(file_size) - reversed_size
213                     #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()#得出MD5
222                 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=1024
228                 else:#最后一次
229                     size=int(file_size) - reversed_size
230                     #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         return
242 
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             return
251 
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=0
262                 #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             return
293 
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                 return
302             elif cmd_attr==../..:
303                 self.send_mge(252)#可以切换到上级目录
304                 self.user_home_dir=self.home_dir#绝对目录 = home
305                 self.user_dir=/
306                 clinet_ack=self.request.recv(1024)#为了去粘包
307                 self.request.send(self.user_dir.encode())#返回相对目录
308                 return
309             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                 return
318 
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             return
327         else:
328             self.send_mge(256)#目录不存在
329             return
330 
331     #查看目录路径 CD
332     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.warning
338         self.user_opert.critical(info_str)#记录日志
339         return
340 
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.user
358         user_info[密码]=pwds
359         user_info[剩余磁盘配额]=Quotation
360         return user_info,Quotation
361 
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         return
372 
373     #日志信息生成
374     def log_str(self,msg,**kwargs):
375         info_str=用户[%s]进行了[%s]操作%(self.user,msg)
376         return info_str
377 
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         return
390     ##单个命令
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             return
397         else:
398             print(没有相关命令!)
399             self.send_mge(241)
400             return
401 
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]))#连接的ip
417             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.user
423 
424                 break
425             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
View Code
|           |      |- - -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 logger
19 
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
View Code
|           |      |- - -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 config
10 
11 
12 from  core.ftp_server import  MyTCPHandler
13 
14 import optparse
15 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(服务关闭)
View Code

 



python第四十八天--高级FTP

标签:解析   用户密码   没有   form   关闭   style   asa   dict   war   

原文地址:http://www.cnblogs.com/uge3/p/7048938.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!