python實現簡單FTP


  • 需求

1. 用戶登陸  ---configparse 存儲信息 進行登錄驗證
2. 上傳/下載文件        ---get、put函數
3. 不同用戶家目錄不同    ----configparse定義家目錄
4. 查看當前目錄下文件    ----dir命令,如何進行權限判定,只能進入到自己的目錄下面?
5. 充分使用面向對象知識    -----就是用類、函數

 

  • 代碼結構

  • 服務端

  1 #!/usr/bin/env python
  2 #-*- coding:utf-8 -*-
  3 # @Time    : 2017/12/14 15:25
  4 # @Author  : lichuan
  5 # @File    : 服務端.py
  6 
  7 
  8 import socketserver
  9 import struct
 10 import json
 11 import subprocess
 12 import os
 13 import configparser
 14 from module import common
 15 import subprocess
 16 
 17 class My_FtpServer(socketserver.BaseRequestHandler):
 18 
 19     def handle(self):
 20         # while True:
 21         try:
 22             name = self.name_check()    #進行用戶名驗證
 23             self.path = common.get_home_path(name)   #根據用戶名獲取家目錄
 24             if not os.path.exists(self.path):
 25                 os.mkdir(self.path)
 26             # os.chdir(path)
 27             print(os.getcwd())
 28             while True:
 29                 data=self.request.recv(8)   #接收命令長度
 30                 if not data:break   #防止在linux、mac等系統中會有問題
 31                 msg_length=struct.unpack('q',data)[0]  #是一個元組形勢的數據,包含命令長度
 32                 cmd = self.request.recv(msg_length).decode('utf-8')
 33                 if cmd.upper() == 'QUIT':  #quit 進行退出
 34                     print('break')
 35                     break
 36                 if hasattr(self,cmd.split()[0]): #收到的消息,進行切分,拿出第一個進行判斷
 37                     print(cmd.split()[0])
 38                     func = getattr(self,cmd.split()[0])
 39                     func(cmd)
 40                 else:  #如果沒有函數,則進行執行命令
 41                     os.chdir(self.path)
 42                     res=subprocess.Popen(cmd,
 43                                      shell=True,
 44                                      stdout=subprocess.PIPE,
 45                                      stderr=subprocess.PIPE)
 46                     err = res.stderr.read()
 47                     if err:
 48                         back_msg=err
 49                     else:
 50                         back_msg=res.stdout.read()
 51                         if cmd.split()[0] == 'cd':
 52                             new_path = os.getcwd()+os.sep+cmd.split()[1]
 53                             if self.path in new_path:
 54                                 print('self.path',self.path)
 55                                 print('new_path',new_path)
 56                                 self.path = new_path
 57                     print(self.path)
 58                     self.send_msg(back_msg)
 59         except Exception as e:
 60             print("Error:",e)
 61             print('handle exception!')
 62 
 63     def send_header(self,file_info): #發送報頭
 64         # file_info = {'cmd': cmd,'status': status}
 65         f_head = json.dumps(file_info)  #json化代碼,用於傳輸
 66         f_head_bytes = bytes(f_head, encoding='utf-8') #變成bytes類型
 67         self.request.send(struct.pack('q', len(f_head_bytes)))  # 傳報頭長度過去
 68         self.request.sendall(f_head_bytes)  # 傳報頭過去
 69 
 70     def get(self,cmd):
 71         print('----get function----')
 72         # print(os.getcwd())
 73         os.chdir(self.path)
 74         l = cmd.split()
 75         if len(l) != 2:
 76             print('get command is worng!')
 77             return
 78         # BASE_DIR = os.path.dirname(os.path.abspath(__file__))
 79         f = l[1]
 80         if os.path.exists(f):
 81             file_path = os.path.abspath(f)
 82         else:
 83             print('file not exists!')
 84             return
 85         print('file path:',os.path.abspath(file_path))
 86         if os.path.isfile(file_path) and self.path in file_path:
 87             file_size = os.path.getsize(file_path)
 88             file_md5 = common.GetFileMd5(file_path)  # 獲取文件的md5值
 89             status = 'OK'
 90             file_info = {'cmd': cmd, 'filename': os.path.basename(file_path), 'file_size': file_size, 'md5': file_md5,'status':status}
 91             # f_head = json.dumps(file_info)
 92             # print(type(f_head))
 93             # f_head_bytes = bytes(f_head, encoding='utf-8')
 94             # print(f_head_bytes,type(f_head_bytes),type(f_head))
 95             # print('len:',len(f_head))
 96             # self.request.send(struct.pack('q', len(f_head_bytes)))  # 傳報頭長度過去
 97             # self.request.sendall(f_head_bytes)  # 傳報頭過去
 98             self.send_header(file_info)
 99             with open(file_path, 'rb') as read_f:
100                 for line in read_f:
101                     self.request.send(line)  # 傳文件過去
102         else:
103             print('權限不夠或者不是文件')
104             status = 'ERROR'
105             file_info = {'cmd': cmd,'status': status}
106             self.send_header(file_info)
107 
108 
109     def put(self,cmd):
110         print('----put function----')
111         os.chdir(self.path)
112         l = cmd.split()
113         if len(l) != 2:
114             print('put command is worng!')
115             return
116         head_l=self.request.recv(8) #接收報頭長度
117         longth = int(struct.unpack('q',head_l)[0]) #接收到的應該是個元組,取第一位,即長度
118         file_json = self.request.recv(longth).decode('utf-8')
119         head_dic = json.loads(file_json)
120         status = head_dic['status']
121         if status == 'OK':
122             file_name=head_dic['filename']
123             file_size=head_dic['file_size']
124             recv_size = 0
125             with open(file_name,'wb') as write_f:
126                 while recv_size < file_size:
127                     res=self.request.recv(1024)
128                     recv_size+=len(res)
129                     write_f.write(res)
130                     print(recv_size,file_size)
131             file_path=os.path.abspath(file_name)
132             # print('file_name:',file_name)
133             print('file_path:',file_path)
134             file_md5=common.GetFileMd5(file_path)
135             # print(file_md5)
136             # print(head_dic['md5'])
137             if file_md5 == head_dic['md5']:
138                 print('文件傳輸成功')
139             else:
140                 print('文件不一致')
141                 os.remove(file_path)
142 
143     def send_msg(self,msg):#用於發送信息
144         self.head_l = struct.pack('q',len(msg))
145         self.request.send(self.head_l)
146         self.request.sendall(msg)
147 
148     #進行用戶名密碼驗證的函數
149     def name_check(self):
150         while True:
151             name = self.request.recv(1024).decode('utf-8')
152             self.request.send('0'.encode('utf-8'))
153             passwd=self.request.recv(1024).decode('utf-8')
154             if common.login(name,passwd):
155                 self.request.send('0'.encode('utf-8'))
156                 return name
157             else:
158                 self.request.send('1'.encode('utf-8'))
159                 continue
160 
161     #執行dir命令顯示目錄下的內容
162     def show_path_content(self,path):
163         print('show path'+path)
164         cmd = 'dir ' + path
165         res1 = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
166         return res1.stdout.read().decode('gbk')
167 
168 if __name__ == '__main__':
169     fserver = socketserver.ThreadingTCPServer(('127.0.0.1',8001),My_FtpServer)
170     fserver.serve_forever()
171     fserver.server_close()
172     # for l in os.listdir(os.getcwd()):
173     #     print(l)
174     # for i in os.walk(os.path.dirname(os.path.dirname((os.path.abspath(__file__))))):
175     #     print(i)
服務端.py

 

  • 客戶端

  1 #!/usr/bin/env python
  2 #-*- coding:utf-8 -*-
  3 # @Time    : 2017/12/14 15:25
  4 # @Author  : lichuan
  5 # @File    : 客戶端.py
  6 
  7 
  8 #_*_coding:utf-8_*_
  9 
 10 import time
 11 import socket
 12 import struct
 13 import json
 14 import os
 15 from module import common
 16 
 17 BUFSIZE=1024
 18 
 19 
 20 class FTP_ClientServer:
 21     def __init__(self,ip_port,logined=False):
 22         self.ip_port=ip_port
 23         self.logined = logined
 24         self.socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 25         try:
 26             self.socket.connect(ip_port)
 27         except:
 28             self.socket.close()
 29             raise
 30 
 31     def run(self):
 32         while True:
 33             if not self.logined:
 34                 self.login()
 35             else:
 36                 msg = input('>>:').strip()
 37                 msg_longth=struct.pack('q',len(msg))
 38                 self.socket.send(msg_longth)
 39                 self.socket.sendall(msg.encode('utf-8'))
 40                 if msg.upper() == 'QUIT':
 41                     break
 42                 if hasattr(self,msg.split()[0]):
 43                     func=getattr(self,msg.split()[0])
 44                     func(msg)
 45                 else:  #傳送命令過去的邏輯
 46                     recv = self.socket.recv(8)
 47                     data = struct.unpack('q',recv) #返回的是一個元組,(123,),123是長度
 48                     print(data)
 49                     longth = data[0]
 50                     recv_size = 0
 51                     recv_data = b''
 52                     while recv_size < longth:
 53                         recv_data+=self.socket.recv(1024)
 54                         recv_size+=1024
 55                     print(recv_data.decode('gbk'))
 56 
 57     #程序開始時,進行登錄驗證的函數
 58     def login(self):
 59         name = input('username:').strip()
 60         passwd = input('password:').strip()
 61         if len(name) == 0: return False
 62         if len(passwd) == 0: return False
 63         self.socket.send(name.encode('utf-8'))  # 發送name過去進行驗證
 64         recv = self.socket.recv(1)
 65         if recv.decode('utf-8') != '0':  # name返回結果不為0,說明name不存在
 66             print('name error')
 67             return False
 68         self.socket.send(passwd.encode('utf-8'))
 69         recv = self.socket.recv(1)
 70         if recv.decode('utf-8') != '0':  # passwd返回結果不為0,說明passwd錯誤
 71             print('passwd error')
 72             return False
 73         print('login successful!')
 74         self.logined = True
 75         return True
 76 
 77     def recv_msg(self):
 78         head_l = self.socket.recv(8)
 79         data_head = struct.unpack('q',head_l)
 80 
 81     def get(self,cmd):
 82         l = cmd.split()
 83         if len(l) != 2:
 84             print('get command is worng!')
 85             return
 86         head_l = self.socket.recv(8)  # 接收報頭長度
 87         longth = int(struct.unpack('q', head_l)[0])  # 接收到的應該是個元組,取第一位,即長度
 88         file_json = self.socket.recv(longth).decode('utf-8')
 89         head_dic = json.loads(file_json)
 90         status = head_dic['status']
 91         if status == 'OK':
 92             file_name = head_dic['filename']
 93             file_size = head_dic['file_size']
 94             recv_size = 0
 95             with open(file_name, 'wb') as write_f:
 96                 while recv_size < file_size:
 97                     res = self.socket.recv(1024)
 98                     recv_size += len(res)
 99                     write_f.write(res)
100                     print(recv_size, file_size)
101             file_path = os.path.abspath(file_name)
102             # print('file_name:',file_name)
103             print('file_path:', file_path)
104             file_md5 = common.GetFileMd5(file_path)
105             # print(file_md5)
106             # print(head_dic['md5'])
107             if file_md5 == head_dic['md5']:
108                 print('文件傳輸成功')
109             else:
110                 print('文件不一致')
111                 os.remove(file_path)
112         else:
113             print('權限不夠或者不是文件')
114 
115     def put(self,msg):
116         l=msg.split()
117         if len(l) != 2:
118             print('put command is worng!')
119             return
120         BASE_DIR=os.path.dirname(os.path.abspath(__file__))
121         f=l[1]
122         if os.path.exists(f):
123             file_path = os.path.abspath(f)
124         else:
125             print('file not exists!')
126             return
127         if os.path.isfile(file_path):
128             file_size = os.path.getsize(file_path)
129             file_md5 = common.GetFileMd5(file_path)  #獲取文件的md5值
130             status = 'OK'
131             file_info = {'cmd':msg,'filename':os.path.basename(file_path),'file_size':file_size,'md5':file_md5,'status':status}
132             self.send_header(file_info)
133             # f_head=json.dumps(file_info)
134             # print(type(f_head))
135             # f_head_bytes = bytes(f_head,encoding='utf-8')
136             # print(f_head_bytes,type(f_head_bytes),type(f_head))
137             # print('len:',len(f_head))
138             # self.socket.send(struct.pack('q',len(f_head_bytes))) #傳報頭長度過去
139             # self.socket.sendall(f_head_bytes)     #傳報頭過去
140             num=0
141             with open(file_path,'rb') as read_f:
142                 for line in read_f:
143                     self.socket.send(line)       #傳文件過去
144                     # num+=1
145                     # print(num)
146         else:
147             status = 'ERROR'
148             file_info = {'cmd': msg,'status': status}
149             self.send_header(file_info)
150 
151     def send_header(self,file_info): #發送報頭
152         # file_info = {'cmd': cmd,'status': status}
153         f_head = json.dumps(file_info)  #json化代碼,用於傳輸
154         f_head_bytes = bytes(f_head, encoding='utf-8') #變成bytes類型
155         self.socket.send(struct.pack('q', len(f_head_bytes)))  # 傳報頭長度過去
156         self.socket.sendall(f_head_bytes)  # 傳報頭過去
157 
158 if __name__ == '__main__':
159     ip_port = ('127.0.0.1', 8001)
160     f=FTP_ClientServer(ip_port)
161     f.run()
客戶端.py

 

  • 用戶定義文件

1 [egon]
2 name = egon
3 home_path = egon
4 passwd = bc5b9cb3e4ab483335edab3347f3c102
5 
6 [alex]
7 name = alex
8 home_path = alex
9 passwd = bc5b9cb3e4ab483335edab3347f3c102
users.cfg

 

  • 公共定義函數文件

 1 #!/usr/bin/env python
 2 #-*- coding:utf-8 -*-
 3 # @Time    : 2017/10/20 15:46
 4 # @Author  : lichuan
 5 # @File    : common.py
 6 
 7 import hashlib
 8 import os
 9 import re
10 from log import my_log_settings
11 import logging
12 import pickle
13 import configparser
14 
15 def login(name,passwd):
16     path = os.path.dirname(os.path.abspath(__file__))+os.sep+'users.cfg'
17     config = configparser.ConfigParser()
18     config.read(path)
19     # print(config.sections())
20     if name not in config.sections():
21         print('name is wrong!')
22         return False
23     encrypt_passwd = encrypt(passwd)
24     # print(encrypt_passwd)
25     p = config.get(name,'passwd')
26     # print(p)
27     if encrypt_passwd == p:
28         print('login successful')
29         return True
30     else:
31         print('password is wrong!')
32         return False
33 
34 #根據name獲取家目錄路徑
35 def get_home_path(name):
36     config = configparser.ConfigParser()
37     path = os.path.dirname(os.path.abspath(__file__)) + os.sep + 'users.cfg'
38     # print(path)
39     config.read(path)
40     # print(config.sections())
41     if name not in config.sections():
42         print('name is not exist')
43         return None
44     else:
45         p = config.get(name,'home_path')
46         new_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + os.sep +'db'+os.sep+p
47         # print(new_path)
48         return new_path
49 
50 def encrypt(str):
51     '''
52     對傳入字符串進行加鹽加密
53     :param str: 需要進行加密的字符串
54     :return: 返回加密過的字符串
55     '''
56     encrpt=hashlib.md5()
57     encrpt.update(bytes('admin1234nnnnnn',encoding='utf-8'))
58     encrpt.update(bytes(str,encoding='utf-8'))
59     return encrpt.hexdigest()
60 
61 #計算文件的md5值
62 def GetFileMd5(filename):
63      if not os.path.isfile(filename):
64          print('file is not exists')
65          return
66      myhash = hashlib.md5()
67      with open(filename,'rb') as f:
68          while True:
69              b = f.read(8096)
70              if not b :
71                  break
72              myhash.update(b)
73          return myhash.hexdigest()
74 
75 # if __name__ == '__main__':
76 #     a=GetFileMd5('users.cfg')
77 #     print(a)
common.py

 

  • 執行效果

C:\Python35\python.exe C:/Users/dell/PycharmProjects/day7_2/bin2/客戶端2.py
username:alex
password:123456
login successful!
>>:dir
(458,)
 驅動器 C 中的卷沒有標簽。
 卷的序列號是 D48A-2DD8

 C:\Users\dell\PycharmProjects\day7_2\db\alex 的目錄

2018/01/09  11:02    <DIR>          .
2018/01/09  11:02    <DIR>          ..
2018/01/05  17:18             5,231 aa.txt
2018/01/09  11:02             5,231 bb.txt
2018/01/08  16:46    <DIR>          ddd
2018/01/05  14:43             8,815 test.py
               3 個文件         19,277 字節
               3 個目錄 23,366,094,848 可用字節

>>:rm bb.txt
(61,)
'rm' 不是內部或外部命令,也不是可運行的程序
或批處理文件。

>>:del bb.txt
(0,)

>>:dir
(414,)
 驅動器 C 中的卷沒有標簽。
 卷的序列號是 D48A-2DD8

 C:\Users\dell\PycharmProjects\day7_2\db\alex 的目錄

2018/01/09  11:15    <DIR>          .
2018/01/09  11:15    <DIR>          ..
2018/01/05  17:18             5,231 aa.txt
2018/01/08  16:46    <DIR>          ddd
2018/01/05  14:43             8,815 test.py
               2 個文件         14,046 字節
               3 個目錄 23,366,037,504 可用字節

>>:put bb.txt
>>:get aa.txt
1024 5231
1546 5231
1605 5231
1646 5231
1698 5231
1700 5231
1752 5231
1774 5231
1817 5231
1862 5231
1903 5231
1946 5231
2026 5231
2062 5231
2152 5231
2185 5231
2211 5231
2261 5231
2297 5231
2388 5231
2423 5231
2449 5231
2485 5231
2514 5231
2535 5231
2537 5231
2562 5231
2600 5231
2647 5231
2649 5231
2673 5231
2698 5231
2723 5231
2767 5231
2787 5231
2847 5231
2961 5231
3023 5231
3065 5231
3107 5231
3150 5231
3173 5231
3221 5231
3263 5231
3309 5231
3348 5231
3384 5231
3429 5231
3477 5231
3518 5231
3558 5231
3607 5231
3634 5231
3668 5231
3709 5231
3750 5231
3765 5231
3803 5231
3837 5231
3839 5231
3863 5231
3886 5231
3911 5231
3955 5231
3975 5231
4036 5231
4083 5231
4127 5231
4142 5231
4181 5231
4201 5231
4240 5231
4292 5231
4369 5231
4482 5231
4524 5231
4559 5231
4618 5231
4685 5231
4726 5231
4815 5231
4883 5231
4902 5231
4952 5231
4989 5231
5056 5231
5084 5231
5116 5231
5118 5231
5146 5231
5181 5231
5214 5231
5227 5231
5229 5231
5231 5231
file_path: C:\Users\dell\PycharmProjects\day7_2\bin2\aa.txt
文件傳輸成功
>>:quit
View Code

 

定義了一個self.path參數,對用戶的目錄進行限定,以此做權限控制。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM