python简易FTP
python简易FTP
一、需求
1. 用户加密认证
2. 每个用户有自己的家目录 ,且只能访问自己的家目录
3. 允许用户在ftp server上随意切换目录cd
4. 允许上传post和下载get文件
5. 文件传输过程中显示进度条
二、程序目录结构

三、源代码
FTPclient客户端
1 import os,sys 2 3 BASE_dir=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 4 5 sys.path.append(BASE_dir) 6 7 from service import supermain 8 9 if __name__=="__main__": 10 supermain.client_main()
1 import os 2 BIND_HOST="127.0.0.1" 3 BIND_PORT=8666 4 BASEDIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 5 USER_HOME=os.path.join(BASEDIR,"home")
1 import hashlib,sys 2 def get_file_MD5(file_path): 3 obj=hashlib.md5() 4 f=open(file_path,"rb") 5 while True: 6 data=f.read(8000) 7 8 if not data: 9 break 10 obj.update(data) 11 return obj.hexdigest() 12 13 def bar(sent_num,whole_num): 14 f_sent_whole=float(sent_num)/float(whole_num) 15 int_f=int(f_sent_whole*100) 16 temp="\r%d %%"%(int_f,) 17 sys.stdout.write(temp) 18 sys.stdout.flush()
1 import socket,json,re,os 2 from conf import settings 3 from lib import commons 4 def client_main(): 5 sk = socket.socket() 6 ip_port = (settings.BIND_HOST, settings.BIND_PORT) 7 sk.connect(ip_port) 8 client_main_class(sk) 9 10 class client_main_class(object): 11 def __init__(self,sk): 12 self.name=None 13 self.home=None 14 self.sk=sk 15 #接收服务器端发来的欢迎并打印 16 print(str(self.sk.recv(1024),"utf8")) 17 while True: 18 self.info() 19 #输入cmd|ipconfig 20 inp=input(">>>").strip() 21 #得到cmd 22 inp_split=inp.split("|",1)[0] 23 if inp_split=="help": 24 continue 25 if inp_split=="exit": 26 break 27 func_list=["cmd","post","get"] 28 if inp_split in func_list: 29 client_func=getattr(self,inp_split) 30 client_func(inp) 31 32 def info(self): 33 info_data="""请按格式输入 34 cmd|xxxx 35 post|xxxx 36 get|xxxx 37 help 38 exit 39 """ 40 print(info_data) 41 42 def login(self): 43 print("账号未登录") 44 username=input("请输入账号:").strip() 45 password=input("请输入密码:").strip() 46 info_user={"username":username,"password":password} 47 #将账号和密码发送过去 48 self.sk.sendall(bytes(json.dumps(info_user),"utf8")) 49 #接收服务器发来的消息如果是4002代表登陆成功,其他就失败 50 data=str(self.sk.recv(1024),"utf8") 51 if data=="4002": 52 self.name=username 53 self.home=os.path.join(settings.USER_HOME,self.name) 54 if not os.path.exists(self.home): 55 os.makedirs(self.home) 56 print("登陆成功!✿✿ヽ(°▽°)ノ✿✿") 57 else: 58 print("登陆失败") 59 60 def cmd(self,inp): 61 self.sk.sendall(bytes(inp,"utf8")) 62 server_send=self.sk.recv(1024) 63 server_send_str=str(server_send,"utf8") 64 if server_send_str =="4001": 65 self.login() 66 else: 67 self.sk.sendall(bytes("客户端准备好接收了","utf8")) 68 #接收服务器发来的关于接下来要发送的文字字数多少 69 cmd_result_len=int(str(self.sk.recv(1024),"utf8")) 70 print(cmd_result_len) 71 #回响一下进入下一步然后准备接收文字 72 self.sk.sendall(bytes("response","utf8")) 73 client_cmd_result_len=0 74 all_data=bytes() 75 #只要字数不等就一直接收 76 while client_cmd_result_len<cmd_result_len: 77 cmd_data=self.sk.recv(1024) 78 client_cmd_result_len+=len(cmd_data) 79 all_data += cmd_data 80 81 print(str(all_data,"utf8")) 82 83 84 #上传文件 85 def post(self,inp): 86 #post|F:\1.txt 2.txt 87 method,files_path=inp.split("|",1) 88 89 local_file_path,target_file_name=re.split("\s+",files_path) 90 #将本地文件的名称、大小、MD5值、传过去以后在服务器端文件名称传过去 91 local_file_name=os.path.basename(local_file_path) 92 local_file_size=os.stat(local_file_path).st_size 93 local_file_MD5=commons.get_file_MD5(local_file_path) 94 local_file_info="post|%s|%s|%s|%s"%(local_file_name,local_file_size,local_file_MD5,target_file_name) 95 96 self.sk.sendall(bytes(local_file_info, "utf8")) 97 server_send = self.sk.recv(1024) 98 server_send_str = str(server_send, "utf8") 99 has_sent=0 100 if server_send_str=="4001": 101 self.login() 102 return 103 while True: 104 if server_send_str=="3000": 105 input_continues=input("是否要断点续传?按Y(yes)或N(no)") 106 if input_continues=="Y": 107 self.sk.sendall(bytes("Y","utf8")) 108 target_file_size=int(str(self.sk.recv(1024),"utf8")) 109 has_sent=target_file_size 110 111 elif input_continues=="N": 112 self.sk.sendall(bytes("N","utf8")) 113 else: 114 print("输入错误") 115 continue 116 f = open(local_file_path, "rb") 117 f.seek(has_sent) 118 while has_sent<local_file_size: 119 file_data=f.read(1024) 120 if not file_data: 121 break 122 self.sk.sendall(file_data) 123 has_sent+=len(file_data) 124 commons.bar(has_sent,local_file_size) 125 f.close() 126 print("上传成功") 127 break 128 129 130 131 #下载文件 132 def get(self,inp): 133 #不加这一步没有家目录会报错,TypeError: join() argument must be str or bytes, not 'NoneType' 134 self.sk.sendall(bytes(inp,"utf8")) 135 server_send_str=str(self.sk.recv(1024),"utf8") 136 #去登录 137 if server_send_str=="4001": 138 self.login() 139 return 140 #确认登陆 141 if server_send_str=="4100": 142 #get|服务器那里叫什么.txt 下载过来叫什么.txt 143 fun,files_path=inp.split("|",1) 144 target_file_name,local_file_name=re.split("\s+",files_path) 145 local_file_path=os.path.join(settings.USER_HOME,self.name,local_file_name) 146 has_file_size=0 147 #文件存在并文件大小大于0,就记录文件大小,如果文件大小为0当做没文件算 148 if os.path.exists(local_file_path) and os.stat(local_file_path).st_size>0: 149 has_file_size=os.stat(local_file_path).st_size 150 get_info="get|%s|%d"%(target_file_name,has_file_size) 151 self.sk.sendall(bytes(get_info,"utf8")) 152 server_replay=str(self.sk.recv(1024),"utf8") 153 154 if server_replay=="5000": 155 #想知道对面文件大小5001 156 self.sk.sendall(bytes("5001","utf8")) 157 server_file_size=int(str(self.sk.recv(1024),"utf8")) 158 if has_file_size>0 and has_file_size<server_file_size: 159 inp_Y_N=input("是否断点续传?Y/N:").strip() 160 if inp_Y_N=="Y": 161 f=open(local_file_path,"ab") 162 self.sk.sendall(bytes("Y", "utf8")) 163 else: 164 f=open(local_file_path,"wb") 165 self.sk.sendall(bytes("N", "utf8")) 166 has_file_size = 0 167 elif has_file_size==server_file_size: 168 self.sk.sendall(bytes("N", "utf8")) 169 f = open(local_file_path, "wb") 170 has_file_size=0 171 else: 172 self.sk.sendall(bytes("N", "utf8")) 173 f=open(local_file_path,"wb") 174 has_file_size = 0 175 # self.sk.sendall(bytes("客户端准备好接收文件了","utf8")) 176 while has_file_size<server_file_size: 177 sent_data=self.sk.recv(1024) 178 f.write(sent_data) 179 has_file_size+=len(sent_data) 180 commons.bar(has_file_size,server_file_size) 181 f.close() 182 print("传输完成!!!!") 183 elif server_replay=="5100": 184 print("服务器没有这个文件或文件大小为0")
FTPserver服务器
1 import subprocess,os,sys 2 BASEDIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 3 4 sys.path.insert(0,BASEDIR) 5 6 from service import supermain 7 8 if __name__=="__main__": 9 supermain.MultiServer()
1 import os,sys 2 BASEDIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 3 USER_HOME=os.path.join(BASEDIR,"home") 4 5 BIND_HOST="127.0.0.1" 6 BIND_PORT=8666
1 import socketserver,json,os,subprocess,re 2 from conf import settings 3 class action(object): 4 def __init__(self,conn): 5 self.conn=conn 6 self.is_login=False 7 self.home=None 8 self.name=None 9 self.current_dir=None 10 11 def login(self): 12 #发送4001到client客户端代表需要登陆 13 self.conn.sendall(bytes("4001","utf8")) 14 15 client_info=json.loads(str(self.conn.recv(1024),"utf8")) 16 if client_info["username"]=="wxy" and client_info["password"]=="123": 17 self.is_login=True 18 self.name=client_info["username"] 19 self.conn.sendall(bytes("4002","utf8")) 20 #初始化家目录和当前目录 21 self.initialize() 22 else: 23 self.conn.sendall(bytes("4003"), "utf8") 24 25 def initialize(self): 26 self.home=os.path.join(settings.USER_HOME,self.name) 27 self.current_dir=os.path.join(settings.USER_HOME,self.name) 28 29 def cmd(self,client_inp): 30 #4004已在登陆状态 31 self.conn.sendall(bytes("4004","utf8")) 32 print(str(self.conn.recv(1024),"utf8")) 33 client_cmd,client_msg=client_inp.split("|",1) 34 #dir F:\wxy 35 client_msg_list=re.split('\s+',client_msg,1) 36 #查看目录结构 37 if client_msg_list[0]=="dir": 38 #只有dir就直接在后面加上家目录文件列表 39 if len(client_msg_list)==1: 40 if self.current_dir: 41 client_msg_list.append(self.current_dir) 42 else: 43 client_msg_list.append(self.home) 44 45 if client_msg_list[0]=="cd": 46 if len(client_msg_list)==1: 47 if self.current_dir: 48 client_msg_list.append(self.current_dir) 49 else: 50 client_msg_list.append(self.home) 51 #列表里的字符串以空格的方式连接成一个字符串 52 client_msg=" ".join(client_msg_list) 53 #"cd F:\FTP" 54 print(client_msg) 55 try: 56 #获取信息 57 a_stdout=subprocess.Popen(client_msg,shell=True,stdout=subprocess.PIPE) 58 cmd_result=a_stdout.stdout.read() 59 # cmd_result=subprocess.check_output(client_msg,shell=True) 60 #cmd_result返回的是bytes格式gbk编码换成utf8的字节 61 cmd_result=bytes(str(cmd_result,"gbk"),"utf8") 62 except Exception as e: 63 cmd_result=bytes("cmd格式不对,参考cmd|cd F:\wxy","utf8") 64 65 cmd_result_len=len(cmd_result) 66 #int转str转bytes然后发过去 67 self.conn.sendall(bytes(str(cmd_result_len),"utf8")) 68 response=self.conn.recv(1024) 69 self.conn.sendall(cmd_result) 70 71 72 73 74 75 def post(self,client_inp): 76 func,client_file_name,client_file_size,client_file_MD5,target_file_name=client_inp.split("|",4) 77 target_file_path=os.path.join(self.home,target_file_name) 78 has_file_len=0 79 client_file_size_int=int(client_file_size) 80 #文件是否已经存在,存在就计算出文件大小发送给客户端要不要计算文件大小 81 if os.path.exists(target_file_path) and os.stat(target_file_path).st_size>0: 82 target_file_size=os.stat(target_file_path).st_size 83 84 #3000有文件存在 85 self.conn.sendall(bytes("3000","utf8")) 86 #3001收到要断点续传 87 Y_N_recv=str(self.conn.recv(1024), "utf8") 88 if Y_N_recv=="3001": 89 self.conn.sendall(bytes(str(target_file_size),"utf8")) 90 has_file_len=target_file_size 91 f=open(target_file_path,"ab") 92 93 else: 94 f=open(target_file_path,"wb") 95 #不存在发3006 96 else: 97 self.conn.sendall(bytes("3006","utf8")) 98 f=open(target_file_path,"wb") 99 while client_file_size_int>has_file_len: 100 data=self.conn.recv(1024) 101 if not data: 102 break 103 f.write(data) 104 has_file_len+=len(data) 105 f.close() 106 print("传输完成") 107 108 109 110 111 def get(self,client_inp): 112 #确认已经登陆了 113 self.conn.sendall(bytes("4100","utf8")) 114 client_inp=str(self.conn.recv(1024),"utf8") 115 print(client_inp) 116 #get|服务器文件名|客户端文件大小 117 fun,target_file_name,has_file_size=client_inp.split("|",2) 118 #因为has_file_size在前面合并的时候已经变成字符串了所以要变回int 119 has_file_size=int(has_file_size) 120 target_file_path=os.path.join(self.home,target_file_name) 121 has_sent=0 122 if os.path.exists(target_file_path) and os.stat(target_file_path).st_size>0: 123 target_file_size=os.stat(target_file_path).st_size 124 self.conn.sendall(bytes("5000","utf8")) 125 #5001想知道服务器文件大小 126 self.conn.recv(1024) 127 self.conn.sendall(bytes(str(target_file_size),"utf8")) 128 inp_Y_N=str(self.conn.recv(1024),"utf8") 129 if inp_Y_N=="Y": 130 has_sent=has_file_size 131 f=open(target_file_path,"rb") 132 f.seek(has_sent) 133 while has_sent<target_file_size: 134 sent_file_data=f.read(1024) 135 136 self.conn.sendall(sent_file_data) 137 has_sent+=len(sent_file_data) 138 f.close() 139 print("传输完成!!") 140 else: 141 self.conn.sendall(bytes("5100"),"utf8") 142 143 class MultiServerHandler(socketserver.BaseRequestHandler): 144 def handle(self): 145 conn=self.request 146 conn.sendall(bytes("欢迎使用","utf8")) 147 server_obj=action(conn) 148 while True: 149 #收到cmd|ipconfig 150 client_inp=conn.recv(1024) 151 # 看客户端是否已经断开连接 152 if not client_inp: 153 break 154 client_inp=str(client_inp,"utf8") 155 #检查是否已经登陆 156 if server_obj.is_login: 157 selection_func=client_inp.split("|",1)[0] 158 #得到func=server_obj.selection_func,直接执行action里面的cmd或post这些 159 func=getattr(server_obj,selection_func) 160 func(client_inp) 161 else: 162 #去登陆 163 server_obj.login() 164 class MultiServer(object): 165 def __init__(self): 166 server=socketserver.ThreadingTCPServer((settings.BIND_HOST,settings.BIND_PORT),MultiServerHandler) 167 server.serve_forever()

浙公网安备 33010602011771号