socketserver实现FTP


功能:
1、用户加密认证
2、允许同时多用户登录
3、每个用户有自己的家目录 ,且只能访问自己的家目录
4、对用户进行磁盘配额,每个用户的可用空间不同
5、允许用户在ftp server上随意切换目录
6、允许用户查看当前目录下文件
7、允许上传和下载文件,保证文件一致性
8、文件传输过程中显示进度条
附加功能:支持文件的断点续传

环境
  
python 3.5

特性:
  用socketserver实现FTP

主要功能实现:
  1、用户加密认证
    ConfigParser 是Python自带的模块, 用来读写配置文件,将用户信息以下边的格式存入account.py文件,读出后进行判断
[DEFAULT]

[alex]
Password = 123456
Quotation = 100

[jack]
Password = 123456
Quotation = 100

    2、允许同时多用户登录
      从scokerserver 继承 socketserver.ThreadingTCPServer即可实现
    3、每个用户有自己的家目录 ,且只能访问自己的家目录
      将所有路径封装在home目录下
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

USER_HOME = '%shome' % BASE_DIR
LOG_DIR = '%slog' % BASE_DIR
LOG_LEVEL = 'DEBUG'
    4、允许用户在ftp server上随意切换目录
      用os模块改变工作目录
os.chdir(func_val)
    5、允许用户查看当前目录下文件
      用os模块的os.listdir,下为server端代码
    def _ls(self,*args,**kwargs):
        '''显示当前目录下的所有文件'''
        if os.getcwd() == '%s\bin'% settings.BASE_DIR:
            user_home_dir = "%s\%s" % (settings.USER_HOME, self.user["Username"])
            self.request.send(json.dumps(os.listdir(user_home_dir)).encode())
        else:self.request.send(json.dumps(os.listdir()).encode())
    6、允许上传和下载文件,保证文件一致性
      判断上传和下载的文件在传输前后的大小,用以判断文件是否保持一致
  1 #server端
  2 #
  3     def _put(self,*args,**kwargs):
  4         '''上传文件命令'''
  5         data = args[0]
  6 
  7         response = self.get_response()
  8         if response["status_code"] == 257:  # ready to receive
  9             self.request.send(b'1')  # send confirmation to server
 10             user_home_dir = "%s\%s" % (settings.USER_HOME, self.user["Username"])
 11             base_filename = "%s\%s" % (user_home_dir, data.get('filename'))
 12             received_size = 0
 13             file_obj = open(base_filename, 'wb')
 14             if data.get('md5'):
 15                 md5_obj = hashlib.md5()
 16                 while received_size < response['file_size']:
 17                     line = self.request.recv(4096)
 18                     received_size += len(line)
 19                     file_obj.write(line)
 20                     md5_obj.update(line)
 21                 else:
 22                     file_obj.close()
 23                     md5_val = md5_obj.hexdigest()
 24                     self.send_response(258, {'md5': md5_val})
 25                     print("-----file rece done-----")
 26 
 27             else:
 28                 while received_size < response['file_size']:
 29                     data = self.request.recv(4096)
 30                     received_size += len(data)
 31                     file_obj.write(data)
 32                 else:
 33                     print("-----file rece done-----")
 34                     file_obj.close()
 35         else:
 36             print(STATUS_CODE[response["status_code"]])
 37 
 38 
 39     def _get(self,*args,**kwargs):
 40         '''下载文件命令'''
 41         data = args[0]
 42         if data.get('filename') is None:
 43             self.send_response(255)
 44         user_home_dir = "%s\%s" %(settings.USER_HOME,self.user["Username"])
 45         file_abs_path = "%s\%s" %(user_home_dir,data.get('filename'))
 46         print("file abs path",file_abs_path)
 47 
 48         if os.path.isfile(file_abs_path):
 49             file_obj = open(file_abs_path,'rb')
 50             file_size = os.path.getsize(file_abs_path)
 51             self.send_response(257,data = {'file_size':file_size})
 52             self.request.recv(1)    #等待客户端确认,防粘包
 53 
 54             if data.get('md5'):
 55                 md5_obj = hashlib.md5()
 56                 for line in file_obj:
 57                     self.request.send(line)
 58                     md5_obj.update(line)
 59                 else:
 60                     file_obj.close()
 61                     md5_val = md5_obj.hexdigest()
 62                     self.send_response(258,{'md5':md5_val})
 63                     print("send file done...")
 64             else:
 65                 for line in file_obj:
 66                     self.request.send(line)
 67                 else:
 68                     file_obj.close()
 69                     print("send file done...")
 70         else:
 71             self.send_response(256)
 72 
 73 
 74 
 75 
 76 # client端
 77 #
 78     def _get(self,cmd_list):
 79         '''下载文件'''
 80         print("get--",cmd_list)
 81         if len(cmd_list) == 1:
 82             print("no filename follows...")
 83             return
 84         data_header = {
 85             'action':'get',
 86             'filename':cmd_list[1]
 87         }
 88         if self.__md5_required(cmd_list):
 89             data_header['md5'] = True
 90 
 91         self.sock.send(json.dumps(data_header).encode())
 92         response = self.get_response()
 93         print(response)
 94         try:
 95             if response["status_code"] == 257:#ready to receive
 96                 self.sock.send(b'1') #send confirmation to server
 97                 base_filename = cmd_list[1].split('/')[-1]
 98                 received_size = 0
 99                 file_obj = open(base_filename,'wb')
100                 if self.__md5_required(cmd_list):
101                     md5_obj = hashlib.md5()
102                     progress = self.show_progress(response['file_size']) #generator
103                     progress.__next__()
104                     while received_size < response['file_size']:
105                         data = self.sock.recv(4096)
106                         received_size += len(data)
107                         try:
108                             progress.send(len(data))
109                         except StopIteration as e:
110                             print("100%")
111                         file_obj.write(data)
112                         md5_obj.update(data)
113                     else:
114                         print("-----file rece done-----")
115                         file_obj.close()
116                         md5_val = md5_obj.hexdigest()
117                         md5_from_server = self.get_response()
118                         if md5_from_server['status_code'] == 258:
119                             if md5_from_server['md5'] == md5_val:
120                                 print("%s 文件一致性校验成功!"% base_filename)
121                         print(md5_val,md5_from_server)
122 
123                 else:
124                     progress = self.show_progress(response['file_size'])  # generator
125                     progress.__next__()
126                     while received_size < response['file_size']:
127                         data = self.sock.recv(4096)
128                         received_size += len(data)
129                         file_obj.write(data)
130                         try:
131                             progress.send(len(data))
132                         except StopIteration as e:
133                             print("100%")
134                     else:
135                         print("-----file rece done-----")
136                         file_obj.close()
137             else:
138                 print(STATUS_CODE[response["status_code"]])
139         except Exception as e:
140             base_file_size = os.path.getsize(base_filename)
141             with open('data\breakpoint', 'wb') as br_po:
142                 data_header['action'] = 'breakpoint'
143                 data_header['breakpoint'] = base_file_size
144                 br_po.write(json.dumps(data_header).encode())
145 
146 
147     def _put(self,cmd_list):
148         '''上传文件'''
149         print("put--", cmd_list)
150         if len(cmd_list) == 1:
151             print("no filename follows...")
152             return
153         data_header = {
154             'action': 'put',
155             'filename': cmd_list[1]
156         }
157         if self.__md5_required(cmd_list):
158             data_header['md5'] = True
159 
160         self.sock.send(json.dumps(data_header).encode())
161         if os.path.isfile(cmd_list[1]):
162             file_obj = open(cmd_list[1], 'rb')
163             file_size = os.path.getsize(cmd_list[1])
164             self.send_response(257, data={'file_size': file_size})
165             self.sock.recv(1)  # 等待服务器端确认
166             if self.__md5_required(cmd_list):
167                 md5_obj = hashlib.md5()
168                 progress = self.show_progress(file_size)  # generator
169                 progress.__next__()
170                 for line in file_obj:
171                     self.sock.send(line)
172                     try:
173                         progress.send(len(line))
174                     except StopIteration as e:
175                         print("100%")
176                     md5_obj.update(line)
177                 else:
178                     file_obj.close()
179                     md5_val = md5_obj.hexdigest()
180                     md5_from_server = self.get_response()
181                     if md5_from_server['md5'] == md5_val:
182                             print("%s 文件一致性校验成功!"% cmd_list[1])
183                     self.send_response(258, {'md5': md5_val})
184                     print("send file done...")
185             else:
186                 progress = self.show_progress(file_size)  # generator
187                 progress.__next__()
188                 for line in file_obj:
189                     self.sock.send(line)
190                     try:
191                         progress.send(len(line))
192                     except StopIteration as e:
193                         print("100%")
194                 else:
195                     file_obj.close()
196                     print("send file done...")
197         else:
198             print(256)
get and put
    7、文件传输过程中显示进度条
      根据已传文件大小和总文件大小比对判断,输出符号
 1     def show_progress(self,total):
 2         '''显示进度条
 3            total: 文件大小'''
 4         received_size = 0
 5         current_percent = 0
 6         while received_size < total:
 7             if int((received_size / total) * 100) > current_percent:
 8                 print(">",end='',flush=True)
 9                 current_percent = int((received_size / total) * 100)
10             new_size = yield
11             received_size += new_size
进度条
    附加功能:支持文件的断点续传
      将上次断点信息记录下来,也就是记录已传文件大小,再次启用时,将目标文件光标移到上次断点处,然后进行续传
 1     def _breakpoint(self,*args,**kwargs):
 2         '''断点续传'''
 3         data = args[0]
 4         if data.get('filename') is None:
 5             self.send_response(255)
 6         user_home_dir = "%s\%s" % (settings.USER_HOME, self.user["Username"])
 7         file_abs_path = "%s\%s" % (user_home_dir, data.get('filename'))
 8         print("file abs path", file_abs_path)
 9         print(data.get('breakpoint'))
10 
11         if os.path.isfile(file_abs_path):
12             file_obj = open(file_abs_path, 'rb')
13             file_obj.seek(data.get('breakpoint'))
14             file_size = os.path.getsize(file_abs_path)
15             self.send_response(257, data={'file_size': file_size})
16             self.request.recv(1)  # 等待客户端确认
17 
18             if data.get('md5'):
19                 md5_obj = hashlib.md5()
20                 for line in file_obj:
21                     self.request.send(line)
22                     md5_obj.update(line)
23                 else:
24                     file_obj.close()
25                     md5_val = md5_obj.hexdigest()
26                     self.send_response(258, {'md5': md5_val})
27                     print("send file done...")
28             else:
29                 for line in file_obj:
30                     self.request.send(line)
31                 else:
32                     file_obj.close()
33                     print("send file done...")
34         else:
35             self.send_response(256)
36         pass
breakpoint

   主要知识点:

    类的继承
os模块的应用
json模块的应用
类方法的运用
md5加密方法
scoket链接
scoketserver链接
反射
异常处理


小结:
这个程序主要用socketserver实现了一台服务器链接多个客户端,并且进行信息交互,并且实现了几个简单的功能,如:get 文件下载、put 上传文件、cd 切换目录、ls 查看文件、breakpoint断点续传。

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