〖Demo〗-- 高級FTP服務器開發

【高級FTP服務器開發】

要求:服務器

1. 用戶加密認證app

2. 多用戶同時登錄socket

3. 每一個用戶有本身的家目錄且只能訪問本身的家目錄ide

4. 對用戶進行磁盤配額、不一樣用戶配額可不一樣加密

5. 用戶能夠登錄server後,可切換目錄spa

6. 查看當前目錄下文件指針

7. 上傳下載文件,保證文件一致性code

8. 傳輸過程當中現實進度條server

9. 支持斷點續傳blog

 

 

 

 

路徑以下

  1 import socket
  2 import pickle
  3 import hashlib
  4 import sys
  5 import time
  6 import os
  7 A = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  8 class Ftp_client(object):
  9     def __init__(self):
 10         self.client = socket.socket()
 11     def help(self):
 12         '''
 13         幫助說明
 14         :return:
 15         '''
 16         print('''請輸入正確的指令:
 17         ls: 查看根目錄下文件
 18         cd: 切換目錄
 19         download: 下載文件
 20         upload:上文件
 21         mkdir:新創建文件夾
 22                 ''')
 23     def connet(self, ip, port):
 24         '''
 25         連接服務器
 26         :param ip:
 27         :param port:
 28         :return:
 29         '''
 30         self.client.connect((ip, port))
 31         data = self.client.recv(1024)
 32         print(data.decode())
 33         self.main()
 34         self.ftp_main()
 35     def login(self):
 36         '''
 37         登陸
 38         :return:
 39         '''
 40         name = input('請輸入姓名').lower()
 41         password = input('請輸入密碼')
 42         dict = {'name': name, 'password': password}
 43         self.client.sendall(pickle.dumps(dict))
 44         data = self.client.recv(1024)
 45         print(data.decode())
 46         if data.decode()=='輸入有誤':
 47             return False
 48     def register(self):
 49         '''
 50         註冊
 51         :return:
 52         '''
 53         while True:
 54             a = input('請輸入註冊哪一種用戶: 1: 普通用戶(可用空間10M), 2: VIP用戶(可用30M)')
 55             if a =='1':
 56                 space = 10485760
 57                 break
 58             elif a== '2':
 59                 space = 31457280
 60                 break
 61             else:
 62                 print('輸入有誤')
 63                 continue
 64         name = input('請輸入姓名').lower()
 65         pd = input('請輸入密碼')
 66         dict = {'name': name, 'password': pd, 'space': space}
 67         self.client.sendall(pickle.dumps(dict))
 68         data = self.client.recv(1024)
 69         print(data.decode())
 70         if data.decode()== '用戶名已存在,請從新輸入':
 71             return False
 72 
 73     def main(self):
 74         while True:
 75             a = input('請輸入 1. 用戶登陸 2. 用戶註冊 3.退出')
 76             if a == '1':
 77                 self.client.sendall('login'.encode())
 78                 res = self.login()
 79             elif a == '2':
 80                 self.client.sendall('register'.encode())
 81                 res = self.register()
 82             elif a == '3':
 83                 exit()
 84             else:
 85                 print('輸入有誤')
 86                 continue
 87             if res is False:
 88                     continue
 89             else:
 90                 break
 91 
 92     def download(self):
 93         '''
 94         下載
 95         :return:
 96         '''
 97         while True:
 98             data = self.client.recv(1024)
 99             choose = input('文件所在路徑 1 根目錄 2 子目錄')
100             if choose == '1':
101                 path = '1'
102                 break
103             elif choose =='2':
104                 path = input('請輸入路徑,子路徑用 / 分隔隔開') # 根目錄不用輸入
105                 break
106             else:
107                 print('輸入有誤')
108                 continue
109         self.client.sendall(path.encode())
110         data = self.client.recv(1024)
111         filename = input('請輸入下載文件名')
112         self.client.sendall(filename.encode())
113         size = self.client.recv(1024).decode()
114         if size == '該文件不存在':
115             print ('該文件不存在')
116             return False
117         else:
118             size = int(size)
119             if os.path.exists(os.path.join(A, 'db', filename)):
120                 r_size = int(os.path.getsize(os.path.join(A, 'db', filename)))#存在文件的size
121                 while True:
122                     choose = input('文件已存在, 1 從新下載 2 中止下載 3 新起名再下載 4 繼續下載')
123                     if choose == '2':
124                         dic={}
125                         dic['choose'] = choose
126                         self.client.sendall(pickle.dumps(dic))
127                         return False
128                     elif choose == '1':
129                         f = open(os.path.join(A, 'db',filename),'wb')
130                         r_size = 0
131                         break
132                     elif choose == '3':
133                         name = input('請輸入新文件名')
134                         f = open(os.path.join(A, 'db',name),'wb')
135                         r_size = 0
136                         break
137                     elif choose == '4':
138                         f = open(os.path.join(A, 'db',filename),'ab')
139                         break
140                     else:
141                         print('輸入有誤,請從新輸入')
142                 dic={}
143                 dic['choose'] = choose
144                 dic['size'] = r_size
145                 self.client.sendall(pickle.dumps(dic))
146             else:
147                 r_size = 0
148                 f = open(os.path.join(A, 'db', filename),'xb')
149 
150 
151             if size == 0:
152                 f.close()
153                 print('接收完成')
154             else:
155                 while  r_size < size:
156                     file = self.client.recv(1024)
157                     f.write(file)
158                     f.flush() #文件強行寫入file,預防中途中斷
159                     r_size += len(file)
160                     view_bar(r_size, size)
161                     time.sleep(0.1)
162                 else:
163                     print('接收完成')
164                     f.close()
165 
166 
167 
168     def upload(self):
169         filename = input('請輸入上傳的文件名')
170         if os.path.exists(os.path.join(A, 'db', filename)):
171             size = os.path.getsize(os.path.join(A, 'db', filename)) #文件size
172             path = input('請輸入上傳的路徑,子路徑用 / 分隔隔開, h爲根目錄')
173             dic = {}
174             dic['filename'] = filename
175             dic['size'] = size
176             dic['path'] = path
177             self.client.sendall(pickle.dumps(dic))
178             f =  open(os.path.join(A, 'db', filename), 'rb')
179         else:
180             print ('此文件不存在')
181             return False
182         data = self.client.recv(1024)
183         ls = pickle.loads(data) #ls[2]: ;
184         if ls[-1]=='1': #ls[-1]:檢查下載的路徑是否存在
185             print ('此路徑不存在')
186             return False
187         if ls[0] == '0':#ls[0]:檢查空間夠不夠;
188             print ('空間不足,不能上傳')
189 
190         else:
191             if ls[1] == '0': #ls[1]:檢查下載是否存在
192                 while True:
193                     a = input('文件已存在, 1 從新下載 2 中止下載 3 新起名再下載 4 繼續下載')
194                     f_ls = []
195                     f_ls.append(a)
196                     if a == '1':
197                         break
198                     elif a == '2':
199                         return False
200                     elif a =='3':
201                         f_name = input('請輸入新文件名')
202                         f_ls.append(f_name)
203                         break
204                     elif a=='4':
205                         l_size = ls[2] #ls[2]:已下載的文件大小
206                         f.seek(l_size) #把指針放到已下載的地方,繼續下載
207                         break
208                     else:
209                         print ('輸入有誤')
210             else:
211                 f_ls = []
212                 f_ls.append('5') # 5:下載文件不存在
213             self.client.sendall(pickle.dumps(f_ls))
214             data = self.client.recv(1024).decode()
215             print (data)
216             for line in f:
217                 self.client.sendall(line)
218                 num = f.tell() #查看文件上傳位置
219                 view_bar(num, size)
220                 time.sleep(0.1)
221             f.close()
222             print ('接收完成')
223             return False
224 
225 
226     def ls(self):
227         data = self.client.recv(1024)
228         if data =='0'.encode():
229             print('此目錄爲空')
230         elif data =='1'.encode():
231             print('此文件不存在')
232         else:
233             ls = pickle.loads(data)
234             print('此文件裏有:')
235             for i in ls:
236                 print(i)
237 
238     def mkdir(self):
239         name = input('請輸入文件夾名')
240         self.client.sendall(name.encode())
241         data = self.client.recv(1024).decode()
242         print(data)
243 
244     def cd(self):
245         name = input('請輸入路徑,子路徑用 / 分隔隔開') # 根目錄不用輸入
246         self.client.sendall(name.encode())
247         path = self.client.recv(1024).decode()
248 
249         if path == '0':
250             print ('此目錄不存在')
251             return False
252         else:
253             print ('所在文件夾路徑爲 %s' % path)
254         self.client.sendall('ok'.encode())
255         data = self.client.recv(1024)
256         if data =='0'.encode():
257             print('此目錄爲空')
258         else:
259             ls = pickle.loads(data)
260             print('此文件裏有:')
261             for i in ls:
262                 print(i)
263 
264 
265 
266     def ftp_main(self):
267         while True:
268             a = input('請輸入相應的指令, help:查詢, exit:退出')
269             if hasattr(self, a):
270                 self.client.sendall(a.encode())
271                 func = getattr(self, a)
272                 func()
273             elif a == 'exit':
274                 exit()
275             else:
276                 self.help()
277                 continue
278 
279 
280 
281 
282 
283 
284 def view_bar(num, total):
285     '''進度條'''
286     rate = float(num) / float(total)
287     rate_num = int(rate * 100)
288     r = '\r%d%%' % (rate_num, ) #\r 回到到開頭
289     sys.stdout.write(r)
290     sys.stdout.flush()  #刪除記錄
291 
292 ftp = Ftp_client()
293 ftp.connet('localhost', 9999)
client
import socketserver
import pickle
import os
import time
A = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
class MyTCPHandler(socketserver.BaseRequestHandler):
    def handle(self):
        while True:
            try:
                self.request.sendall('連接成功'.encode())
                while True:
                    data = self.request.recv(1024).decode()
                    if data =='login':
                        a = self.login()
                    else:
                        a = self.register() #a爲 list,[0]是name,[1]是可用空間
                    if a is False:
                        continue
                    else:
                        while True:
                            data = self.request.recv(1024)
                            func = getattr(self, data.decode())
                            func(a)
            except Exception: #檢查客戶端是否鏈接正常
                print('客戶端斷開')
                break

    def login(self):
        db_dict = pickle.load(open(os.path.join(A, 'db', 'register'),'rb'))
        self.data = self.request.recv(1024)
        dict = pickle.loads(self.data)
        if dict['name'] in db_dict:
            if dict['password']==db_dict[dict['name']][0]:
                self.request.sendall('登陸成功'.encode())
                return [dict['name'], db_dict[dict['name']][1]] #返回用戶名,用戶可用空間
            else:
                self.request.sendall('輸入有誤'.encode())
                return False
        else:
            self.request.sendall('輸入有誤'.encode())
            return False
    def register(self):
        dict = pickle.loads(self.request.recv(1024))
        print (dict)
        if os.path.exists(os.path.join(A, 'db', dict['name'])):
            self.request.sendall('用戶名已存在,請從新輸入'.encode())
            return False
        else:
            self.request.sendall('註冊成功'.encode())
            os.makedirs(os.path.join(A, 'db', dict['name']))
            # n_dict ={}
            n_dict = pickle.load(open(os.path.join(A, 'db', 'register'), 'rb'))
            print (1)
            print (n_dict)
            n_dict[dict['name']]=[dict['password'],int(dict['space'])] #存儲格式爲{姓名:[密碼,可用空間大少]}
            print (n_dict)
            pickle.dump(n_dict,open(os.path.join(A, 'db', 'register'), 'wb'))
            return [dict['name'], int(dict['space'])]
    def help(self, a):
        return False
    def upload(self, list):
        name = list[0]
        b_path = os.path.join(A, 'db', name) #本身的根目錄
        h_size = int(list[1]) #本身可用的空間大小
        data = self.request.recv(1024)
        dic = pickle.loads(data)
        f_size = int(dic['size']) #上傳文件大小
        filename = dic['filename']
        path = dic['path']
        s_ls = []
        if h_size < f_size:
            a = '0' #空間不足
            s_ls.append(a)
        else:
            a = '1'
            s_ls.append(a)
        if path=='h': #存在根目錄
            l_path =os.path.join(b_path,filename)
        else:
            res = path.split('/')
            print (res)
            for i in res:
                b_path = os.path.join(b_path, i) #合拼成子目錄
            l_path = os.path.join(b_path,filename) #文件路徑

        if os.path.exists(l_path):
            b = '0' #文件已存在
            file_size = os.path.getsize(l_path)
            s_ls.append(b)
            s_ls.append(file_size)
        else:
            b = '1'
            s_ls.append(b)
        if os.path.exists(b_path):
            c = '0'
        else:
            c='1'#文件夾不存在,能夠結束
            s_ls.append(c)
            self.request.sendall(pickle.dumps(s_ls))
            return False
        s_ls.append(c)
        self.request.sendall(pickle.dumps(s_ls))
        f_ls = pickle.loads(self.request.recv(1024))#文件以什麼方式打開
        self.request.sendall('準備開始'.encode())
        if f_ls[0] =='1':
            f = open(l_path,'wb')
            file_size = 0
        elif f_ls[0]=='2':
            return False
        elif f_ls[0]=='3':#文件名另起
            filename = f_ls[1]
            l_path = os.path.join(b_path,filename)
            f = open(l_path,'wb')
            file_size = 0
        elif f_ls[0]=='4':
            f = open(l_path,'ab')
        else:
            f = open(l_path,'xb')
            file_size = 0
        if f_size == 0:
            f.close()
            return False
        else:
            while file_size< f_size:
                line = self.request.recv(1024)
                f.write(line)
                f.flush()
                file_size += len(line)
            else:
                f.close()
                l_dict = pickle.load(open(os.path.join(A, 'db', 'register'), 'rb'))
                l_dict[name][1] = h_size - f_size #修改已用空間
                pickle.dump(l_dict, open(os.path.join(A, 'db', 'register'), 'wb'))
                return False

    def download(self, list):
        self.request.sendall('ok'.encode())
        path = self.request.recv(1024).decode() #檢查文件存在子目錄或根目錄
        self.request.sendall('check'.encode())
        filename = self.request.recv(1024).decode()
        name = list[0]
        if path == '1':
            l_path = os.path.join(A, 'db', name,filename)
        else:
            data = path.split('/')
            pathname = os.path.join(A, 'db', name)
            for i in data:
                pathname = os.path.join(pathname, i) #合拼子目錄
            l_path = os.path.join(pathname,filename)
            print (l_path)
        if os.path.exists(l_path):
            f = open(l_path, 'rb')
            size = os.path.getsize(l_path) #檢查文件
            self.request.sendall(str(size).encode()) #要以字符串格式傳數字
            data = self.request.recv(1024)
            dic = pickle.loads(data)
            if dic['choose']=='2':
                return False
            elif dic['choose']=='4':
                f.seek(int(dic['size'])) #把指針定位到已下載的地方
            for line in f:
                self.request.sendall(line)
            f.close()
            print ('done')
        else:
            self.request.sendall('該文件不存在'.encode())

    def ls(self, list):
        name = list[0]
        ls = os.listdir(os.path.join(A, 'db', name))
        if len(ls)==0:
            self.request.sendall('0'.encode())
        else:
            a = []
            for i in ls:
                a.append(i) #把存在的文件放入list
            self.request.sendall(pickle.dumps(a))
    def cd(self, list):
        data = self.request.recv(1024).decode()
        name = list[0]
        path = os.path.join(A, 'db', name) #根目錄
        path_ls = data.split('/')
        for i in path_ls:
            path = os.path.join(path, i) #合拼子目錄
        print (path)
        if os.path.exists(path) is False:
            print (1)
            path = '0'
            self.request.sendall(path.encode())
            return False
        ls = os.listdir(path)
        self.request.sendall(path.encode())
        data = self.request.recv(1024)
        if len(ls)==0:
            self.request.sendall('0'.encode())
        else:
            a = []
            for i in ls:
                a.append(i)
            self.request.sendall(pickle.dumps(a))




    def mkdir(self, a):
        filename = self.request.recv(1024).decode()
        name = a[0]
        if os.path.exists(os.path.join(A, 'db', name,filename)): #檢查路徑是否存在
            self.request.sendall('文件夾已存在'.encode())
        else:
            os.makedirs(os.path.join(A, 'db', name, filename))
            self.request.sendall('已建好'.encode())






host, port = 'localhost',  9999
server = socketserver.ThreadingTCPServer((host, port), MyTCPHandler)
server.serve_forever()
server
相關文章
相關標籤/搜索