该文档为用python3实现ftp上传下载等功能。
1 import optparse 2 import socket 3 import json,os 4 import shelve 5 6 class FtpClient(object): 7 """ftp客户端""" 8 MSG_SIZE = 1024 # 消息最长1024 9 10 def __init__(self): 11 self.username = None 12 self.terminal_display = None 13 self.shelve_obj = shelve.open(".luffy_db") 14 self.current_dir = None 15 16 parser = optparse.OptionParser()#创建parser这个对象,optparse这个模块是个类 17 parser.add_option("-s","--server", dest="server", help="ftp server ip_addr") 18 parser.add_option("-P","--port",type="int", dest="port", help="ftp server port") 19 parser.add_option("-u","--username", dest="username", help="username info") 20 parser.add_option("-p","--password", dest="password", help="password info") 21 self.options , self.args = parser.parse_args() 22 23 #print(self.options,self.args,type(self.options),self.options.server) 24 self.argv_verification()#调用检查参数合法性函数 25 26 self.make_connection()#调用建立socket连接函数
上面这段首先创建一个类。该客户端代码均在这个类中实现。
定义一个接收值的变量,然后定义初始化函数,定义几个静态属性,这几个属性有些是后面函数需要的,所以提前定义。
之后用OptionParser这个函数生成一个命令行声明。这个函数具体相关可以从网络中查到。
再之后调用两个函数,一个是检查参数合法性的,另一个是建立socket连接。
1 def argv_verification(self): 2 """检查参数合法性""" 3 if not self.options.server or not self.options.port: 4 #如果options的server参数或port参数不为真则exit()关闭并打印括号内内容。 5 exit("Error: must supply server and port parameters") 6 7 8 def make_connection(self): 9 """建立socket链接""" 10 self.sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 11 self.sock.connect((self.options.server,self.options.port)) 12 13 def get_response(self):#接收返回值 14 """获取服务器端返回""" 15 data = self.sock.recv(self.MSG_SIZE)#接收1024个 16 return json.loads(data.decode())#返回decode之后内容
上面代码为三个函数,分别是检查合法性、创建连接、接收返回值。
1 def auth(self): 2 """用户认证""" 3 count = 0 4 while count < 3:#输入最多3次 5 username = input("username:").strip() 6 if not username:continue#如果输入不为真则继续 7 password = input("password:").strip() 8 9 cmd = { 10 'action_type':'auth',#action_type定义功能关键词,将关键词传入值 11 'username':username,#用户名 12 'password':password,#密码 13 } 14 #定义一个字典,该字典将为以后客户端与服务端通讯作为模板。 15 self.sock.send(json.dumps(cmd).encode("utf-8"))#将字典序列化然后encode,发送 16 response = self.get_response()#调用get_response函数,并赋值。该函数获取服务器端返回内容,并反序列化 17 print("response:",response)#打印得到的内容 18 if response.get('status_code') == 200:#pass auth#如果得到的内容是状态码200 19 self.username = username 20 # 定义一个数据属性,将__init__里的None赋值掉,为了以后调用方便 21 self.terminal_display = "[%s]>>:" % self.username#将用户名赋值给显示 22 self.current_dir = "\\"#设置当前目录路径,将init函数重写 23 return True 24 else: 25 print(response.get("status_msg"))#如果验证不成功打印状态码 26 count += 1
auth函数主要负责登录认证功能。注释写的蛮详细了,我就不赘述了。
1 def unfinished_file_check(self): 2 """检查shelve db ,把为正常传完的文件列表打印,按用户的指令决定是否重传""" 3 if list(self.shelve_obj.keys()):#如果shelve列表为真 4 print("-------Unfinished file list -------------") 5 for index,abs_file in enumerate(self.shelve_obj.keys()): 6 #enumerate函数将一个可迭代的容器列出它的索引 7 received_file_size = os.path.getsize(self.shelve_obj[abs_file][1]) 8 #接收到的大小=getsize大小 9 print("%s. %s %s %s %s" %(index,abs_file, 10 self.shelve_obj[abs_file][0], 11 received_file_size, 12 received_file_size/self.shelve_obj[abs_file][0]*100 13 )) 14 15 while True: 16 choice = input("[select file index to re-download]").strip() 17 if not choice:continue 18 if choice == 'back':break 19 if choice.isdigit(): 20 choice = int(choice) 21 if choice >= 0 and choice <= index: 22 selected_file = list(self.shelve_obj.keys())[choice] 23 already_received_size = os.path.getsize(self.shelve_obj[selected_file][1]) 24 25 print("tell server to resend file ", selected_file) 26 #abs_filename + size +received_size 27 self.send_msg('re_get', file_size=self.shelve_obj[selected_file][0], 28 received_size=already_received_size, 29 abs_filename=selected_file) 30 31 response = self.get_response() 32 if response.get('status_code') == 401:#"File exist ,ready to re-send !", 33 local_filename = self.shelve_obj[selected_file][1] 34 35 36 37 f = open(local_filename,'ab') 38 total_size = self.shelve_obj[selected_file][0] 39 recv_size = already_received_size 40 current_percent = int(recv_size /total_size *100) 41 progress_generator = self.progress_bar(total_size,current_percent,current_percent) 42 progress_generator.__next__() 43 while recv_size < total_size: 44 if total_size - recv_size < 8192: # last recv 45 data = self.sock.recv(total_size - recv_size) 46 else: 47 data = self.sock.recv(8192) 48 recv_size += len(data) 49 f.write(data) 50 progress_generator.send(recv_size) 51 #progress_generator.send(received_size) 52 #print(recv_size,total_size) 53 else: 54 print("file re-get done") 55 else: 56 print(response.get("status_msg"))