Python 程序:ftp

Python 程序:ftp


1、ftp实现功能

2、目录结构

3、代码

4、效果展示


一、ftp实现功能

1、用户登陆认证

2、多用户同时登陆

3、不同用户家目录不同

4、查看目录下文件

5、用户可以在家目录下切换目录

6、用户可以在家目录下创建目录

7、用户可以在家目录下删除文件或目录

8、用户磁盘配额(不同用户配额可不同,当文件大小超出服务器分配空间大小拒绝上传)

9、上传:支持断点续传

10、下载:支持断点续传和进度条显示

二、目录结构

三、代码

  1 import socket,os,json,sys
  2 class FtpClient(object):
  3 
  4     def __init__(self,ip,port):
  5         self.client = socket.socket()
  6         self.client.connect((ip,port))
  7         self.exit_flag = False
  8         self.interactive()
  9 
 10     def auth(self):
 11         retry_count = 0
 12         while retry_count < 3:
 13             username = input("username:").strip()
 14             if len(username) == 0:continue
 15             # passwd = getpass.getpass("password:")  #pycharm显示有问题
 16             passwd = input("passwd:").strip()
 17             msg_dic = {
 18                 "action":"ftp_auth",
 19                 "username":username,
 20                 "passwd":passwd
 21             }
 22             self.client.send(json.dumps(msg_dic).encode("utf-8"))
 23 
 24             auth_feedback = self.client.recv(1024)
 25             auth_feedback = auth_feedback.decode()
 26             if auth_feedback == "success":
 27                 print("33[32;1m登录成功!33[0m")
 28                 self.username  = username
 29                 self.cur_path = username
 30                 return True
 31             else:
 32                 print ("33[31;1m账号密码错误!33[0m")
 33                 retry_count +=1
 34         else:
 35             print ("33[31;1m尝试次数过多,exit!33[0m")
 36 
 37     def interactive(self):
 38         ftp_version = self.client.recv(1024)
 39         print(ftp_version.decode())
 40         self.auth()  #登录验证
 41         self.help()
 42         while True:
 43             cmd = input(">>:").strip()
 44             if len(cmd) == 0:
 45                 continue
 46             cmd_str = cmd.split()[0] #输入的第一个字符永远为指令,取出
 47             if hasattr(self,"cmd_%s"%cmd_str):
 48                 func = getattr(self,"cmd_%s"%cmd_str)
 49                 func(cmd)
 50             else:
 51                 self.help()
 52 
 53     def cmd_dir(self,*args):
 54         cmd_split = args[0].split()
 55         filename = ' '.join(cmd_split[1:])
 56         msg_dic = {
 57                     "action":cmd_split[0],
 58                     "filename":filename
 59                 }
 60         self.client.send(json.dumps(msg_dic).encode("utf-8"))
 61         # 防止粘包等服务器确认
 62         cmd_res_size = self.client.recv(1024)
 63         self.client.send(b"ok")
 64         cmd_res_size = int(cmd_res_size.decode())
 65         received_size = 0
 66         while received_size < cmd_res_size:
 67             data = self.client.recv(1024)
 68             received_size +=len(data)
 69             print(data.decode())
 70 
 71     def cmd_cd(self,*args):
 72 
 73         cmd_split = args[0].split()
 74         filename = ' '.join(cmd_split[1:])
 75         msg_dic = {
 76                     "action":cmd_split[0],
 77                     "filename":filename
 78                 }
 79         self.client.send(json.dumps(msg_dic).encode("utf-8"))
 80         feedback = self.client.recv(100)
 81         feedback = feedback.decode()
 82         if feedback.startswith("switch_dir::ok"):
 83             self.cur_path  = feedback.split("::")[-1]
 84         else:
 85             print ("33[31;1m%s33[0m" % feedback.split("::")[-1])
 86 
 87     def cmd_rmdir(self,*args):
 88         cmd_split = args[0].split()
 89         filename = ' '.join(cmd_split[1:])
 90         msg_dic = {
 91                     "action":cmd_split[0],
 92                     "filename":filename
 93                 }
 94         self.client.send(json.dumps(msg_dic).encode("utf-8"))
 95         feedback = self.client.recv(100)
 96         print(feedback.decode())
 97 
 98     def cmd_mkdir(self,*args):  #只支持创建目录,创建文件自己上传就行
 99         cmd_split = args[0].split()
100         filename = ' '.join(cmd_split[1:])
101         msg_dic = {
102                     "action":cmd_split[0],
103                     "filename":filename
104                 }
105         self.client.send(json.dumps(msg_dic).encode("utf-8"))
106         feedback = self.client.recv(100)
107         print(feedback.decode())
108 
109     def cmd_put(self,*args):
110         cmd_split = args[0].split()
111         if len(cmd_split) > 1:
112             filename = cmd_split[1]
113             if os.path.isfile(filename):
114                 filesize = os.stat(filename).st_size
115                 # msg_str = "%s|%s"%(filename,filesize)
116                 msg_dic = {
117                     "action":cmd_split[0],
118                     "filename":filename,
119                     "size":filesize,
120                     "has_file":False
121                 }
122                 self.client.send(json.dumps(msg_dic).encode("utf-8"))
123                 #防止粘包等服务器确认
124                 server_response = self.client.recv(1024)  #此时服务器端判断大小是否超出磁盘配额,返回参数
125                 data = server_response.decode()
126                 data_list= data.split("|")
127                 if data_list[0] == "ok" and data_list[1] == "no":
128                     f = open(filename,"rb")
129                     for line in f :
130                         self.client.send(line)
131                     else:
132                         print("file upload success..")
133                     f.close()
134                 elif data_list[0] == "ok" and data_list[1] != "no":
135                     print(data_list[1])
136                     size = int(data_list[1])
137                     f = open(filename,"rb")
138                     f.seek(size)#seek到断点位置
139                     for line in f :
140                         self.client.send(line)
141                     else:
142                         print("file upload success..")
143                     f.close()
144                 else:
145                     print(server_response.decode())
146             else:
147                 print(filename,"is not exist")
148 
149     def cmd_get(self,*args):
150         cmd_split = args[0].split()
151         if len(cmd_split) > 1:
152             filename = cmd_split[1]
153             msg_dic = {
154                 "action":cmd_split[0],
155                 "filename":filename,
156                 "size":0,
157                 "overridden":True
158             }
159 
160             if os.path.isfile(filename):
161                 print("文件已存在")
162                 old_size = os.stat(filename).st_size
163                 msg_dic["size"] = old_size
164             else:
165                 print("文件不存在")
166                 old_size = 0
167                 msg_dic["size"] = old_size
168 
169             self.client.send(json.dumps(msg_dic).encode("utf-8"))
170             #防止粘包等服务器确认
171             data = self.client.recv(1024)
172             server_response = json.loads(data.decode())
173 
174             if server_response["filename"] is not None:
175                 file_name = server_response["filename"]
176                 if old_size>=server_response["size"]:
177                     print("文件已下载完毕!")        #文件已下载完毕
178                     received_size = old_size
179                 else:
180                     f = open(filename, 'ab')
181                     received_size = old_size
182                 self.client.send(b"ok")
183                 while received_size < server_response["size"]:
184                     if server_response["size"] - received_size >= 1024: #此时要收不只一次
185                         size = 1024
186                     else:   #最后一次收的数据大小,防止多收,避免粘包
187                         size = server_response["size"] - received_size
188                     data = self.client.recv(1024)
189                     received_size += len(data)  #每次收到的大小
190                     f.write(data)
191                     cur_percent = received_size / server_response["size"] * 100
192                     self.show_progress(server_response["size"],received_size,cur_percent)   #显示进度条
193                 print("下载成功!")
194             else:
195                 print("文件不存在!")
196 
197     def show_progress(self,total,finished,percent):
198         progress_mark = "=" * int(percent/2)
199         sys.stdout.write("[%s/%s]%s>%s
" %(total,finished,progress_mark,percent))
200         sys.stdout.flush()
201         if percent == 100:
202             print ('
')
203 
204     def help(self):
205         msg = '''
206      ---命令菜单---
207         dir
208         mkdir filename
209         rmdir filename
210         cd filename
211         get filename
212         put filename
213         '''
214         print(msg)
215 
216 ftp = FtpClient("localhost",9999)
ftp_client
  1 import socketserver,json,os
  2 import user_data
  3 from os.path import join, getsize
  4 
  5 class MyTCPHandler(socketserver.BaseRequestHandler):
  6     def ftp_auth(self,*args):
  7         print ('----auth----')
  8         auth_res = False
  9         cmd_dic = args[0]
 10         if len(cmd_dic) == 3:
 11             msg_type = cmd_dic["action"]
 12             username = cmd_dic["username"]
 13             passwd = cmd_dic["passwd"]
 14             if username in user_data.accounts:
 15                 if user_data.accounts[username]['passwd'] == passwd:
 16                     auth_res = True
 17                     self.login_user = username
 18                     self.cur_path = '%s/%s' %(os.path.dirname(__file__),user_data.accounts[username]['home'])
 19                     self.home_path = '%s/%s' %(os.path.dirname(__file__),user_data.accounts[username]['home'])
 20                     self.quotation = user_data.accounts[username]['quotation']
 21                 else:
 22                     print ('---wrong passwd---')
 23                     auth_res = False
 24             else:
 25                 auth_res = False
 26         else:
 27             auth_res = False
 28         if auth_res:
 29             msg = "success"
 30             print ('33[32;1muser:%s has passed authentication!33[0m' %username)
 31         else:
 32             msg = "fail"
 33         self.request.send(msg.encode())
 34 
 35     def disk_size(self,dir):
 36         home_size = 0
 37         for root, dirs, files in os.walk(dir):
 38             home_size += sum([getsize(join(root, name)) for name in files])
 39         size = self.quotation - home_size
 40         return size
 41 
 42     def put(self,*args):
 43         '''接受客户端文件'''
 44         cmd_dic = args[0]
 45         filename = cmd_dic["filename"]
 46         filesize = cmd_dic["size"]
 47         size = self.disk_size(self.home_path)
 48         print(size,filesize)
 49         if size > int(filesize):
 50             if os.path.isfile(filename):
 51                 print("文件已存在")
 52                 old_size = os.stat(filename).st_size
 53                 print(old_size,filesize)
 54                 if old_size>=filesize:
 55                     self.request.send(b"ok|no")
 56                     f = open(filename + "_new","wb")        #文件已上传完毕,上传新的文件
 57                     received_size = 0
 58                 else:
 59                     b = "ok|%s"%old_size
 60                     self.request.send(b.encode())
 61                     f = open(filename, 'ab')
 62                     received_size = old_size
 63             else:
 64                 self.request.send(b"ok|no")
 65                 f = open(filename,"wb")
 66                 received_size = 0
 67 
 68             while received_size < filesize:
 69                 data = self.request.recv(1024)
 70                 f.write(data)
 71                 received_size += len(data)
 72             else:
 73                 print("file [%s] has uploaded.."%filename)
 74 
 75 
 76 
 77 
 78         else:
 79             result = "磁盘空间不足!"
 80             print(result)
 81             self.request.send(result.encode())
 82 
 83     def get(self,*args):
 84         '''客户端下载文件'''
 85         cmd_dic = args[0]
 86         filename = cmd_dic["filename"]
 87         get_size = cmd_dic["size"]
 88 
 89         if os.path.isfile(filename):  #判断是否已经存在
 90             filesize = os.stat(filename).st_size
 91             cmd_dic["size"] = filesize
 92             self.request.send(json.dumps(cmd_dic).encode("utf-8"))
 93             self.request.recv(1024)
 94             f = open(filename,"rb")
 95             f.seek(get_size)#seek到断点位置
 96             for line in f:
 97                 self.request.send(line)
 98             f.close()
 99 
100         else:
101             cmd_dic['filename'] = None
102             self.request.send(json.dumps(cmd_dic).encode("utf-8"))
103 
104     def dir(self,*args):
105         cmd_dic = args[0]
106         action = cmd_dic["action"]
107         filename = cmd_dic["filename"]
108         home_prefix = user_data.accounts[self.login_user]['home']
109         dir_path =  '%s/%s' %(self.cur_path,filename)
110         os.chdir(dir_path)
111         cmd_res = os.popen("dir").read()
112         print(cmd_res)
113         print(len(cmd_res.encode()))
114         # if len(cmd_res.encode()) == 0 :
115         #     cmd_res = "没有输出。。"
116         self.request.send(str(len(cmd_res.encode())).encode())
117         self.request.recv(1024)  #等客户端确认
118         self.request.send(cmd_res.encode())
119 
120     def cd(self,*args):
121         cmd_dic = args[0]
122         action = cmd_dic["action"]
123         filename = cmd_dic["filename"]
124         if len(filename) == 0:# means no dir follows cd cmd, go back to home directory
125             self.cur_path = self.home_path
126             relative_path = self.cur_path.split(self.home_path)[-1]
127             switch_res = "switch_dir::ok::%s" % relative_path
128         else:
129             if os.path.isdir("%s/%s" %(self.cur_path,filename)):
130                 abs_path = "%s/%s" %(self.cur_path,filename)
131                 if abs_path.startswith(self.home_path):
132                     self.cur_path = abs_path
133                     print("success")
134                     relative_path = self.cur_path.split(self.home_path)[-1]
135                     switch_res = "switch_dir::ok::%s" % relative_path
136                 else:
137                     switch_res = "switch_dir::error::target dir doesn't exist"
138             else:
139                 switch_res = "switch_dir::error::target dir doesn't exist"
140         self.request.send(switch_res.encode())
141 
142     def rmdir(self,*args):
143         cmd_dic = args[0]
144         action = cmd_dic["action"]
145         filename = cmd_dic["filename"]
146         back = ''
147         if os.path.isdir("%s/%s" %(self.cur_path,filename)):
148             print(self.cur_path)
149             os.rmdir("%s/%s" %(self.cur_path,filename))
150             back = "目录删除成功!"
151         elif os.path.exists("%s/%s" %(self.cur_path,filename)):
152             os.remove("%s/%s" %(self.cur_path,filename))
153             back = "文件删除成功!"
154         else:
155             back = "文件或目录不存在!"
156         self.request.send(back.encode())
157 
158     def mkdir(self,*args):
159         cmd_dic = args[0]
160         action = cmd_dic["action"]
161         filename = cmd_dic["filename"]
162         back = ''
163         print(self.cur_path)
164         print(self.home_path)
165         if self.cur_path.startswith(self.home_path):
166             os.mkdir(filename)
167             back = "目录创建成功!"
168         else:
169             back = "没有权限在此目录创建目录!"
170         self.request.send(back.encode())
171 
172     def handle(self):
173         self.request.send(b"Welcome, ftp version 0.0.1")
174         while True:
175             try:
176                 self.data = self.request.recv(1024).strip()
177                 print("{} wrote:".format(self.client_address[0]))
178                 print(self.data)
179                 cmd_dic = json.loads(self.data.decode())
180                 action = cmd_dic["action"]
181                 if hasattr(self,action):
182                     func = getattr(self,action)
183                     func(cmd_dic)
184             except ConnectionResetError as e :
185                 print("客户端已断开!",e)
186                 break
187 
188 if __name__ == "__main__":
189     HOST, PORT = "localhost", 9999
190     # server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)  #单线程
191     server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler)   #多线程并发,没来一个请求服务器就会开启一个独立的线程
192     server.serve_forever()
ftp_server
 1 accounts = {
 2     'zz': {'passwd': "123",
 3              'quotation': 100000000,
 4              'home': 'home/zz'
 5              },
 6     'jj':{'passwd': "123",
 7              'quotation': 1000000,
 8              'home': 'home/jj'
 9              },
10 }
user_data

四、效果展示

1、用户登陆认证:

2、多用户同时登录

3、不同用户家目录不同

4、查看目录下文件

5、在家目录下切换目录

6、在家目录下任意目录里创建目录

7、删除目录或文件

8、用户磁盘配额

9、上传:支持断点续传

10、下载:支持断点续传和进度条显示

原文地址:https://www.cnblogs.com/hy0822/p/9224571.html