Python实现支持并发、断点续传的FTP

参考网上一个FTP程序,重写了一遍,并稍加扩展

一、要求

1. 支持多用户同时登录

2. 可以注册用户,密码使用md5加密

3. 可以登录已注册用户

4.  支持cd切换目录,ls查看目录子文件

5. 支持上传、下载文件

6. 可以执行命令,如:ipconfig

二、代码

配置文件

1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3 
4 import os
5 
6 BASE_DIR = os.path.dirname(os.path.dirname(__file__))
7 BASE_HOME = os.path.join(BASE_DIR, 'home')
8 NAME_PWD = os.path.join(BASE_DIR, 'db', 'name_pwd')
9 USER_FILE = os.path.join(BASE_DIR, 'db')

服务端:

  1 #!/usr/bin/env python
  2 # -*- coding:utf-8 -*-
  3 
  4 import os
  5 import hashlib
  6 import pickle
  7 import subprocess
  8 import socketserver
  9 from config import settings
 10 
 11 baseHome = settings.BASE_HOME
 12 class MyServer(socketserver.BaseRequestHandler):
 13     def recv_file(self):
 14         '''
 15         文件传输
 16         :return:
 17         '''
 18         conn = self.request
 19         a = str(conn.recv(1024),encoding='utf-8')
 20         file_size, file_name = a.split(',')
 21         new_file_name = os.path.join(baseHome, file_name)
 22         if file_name in baseHome:    #检测文件是否已存在,涉及断点续传
 23             has_recv = os.stat(baseHome).st_size     #计算临时文件大小
 24             conn.sendall(bytes(has_recv, encoding='utf-8'))
 25             with open(new_file_name,'ab') as f:     #追加模式
 26                 data = conn.recv(1024)
 27                 f.write(data)
 28                 has_recv += len(data)
 29         else:
 30             has_recv = 0
 31             conn.sendall(bytes('s',encoding='utf-8'))   #客户端收到字符串s,从0开始发送
 32             with open(new_file_name, 'wb') as f:
 33                 while has_recv<= int(file_size):
 34                     data = conn.recv(1024)
 35                     f.write(data)
 36                     has_recv += len(data)
 37 
 38     def send_file(self, fileName):
 39         '''
 40         向客户端发送文件
 41         :param fileName:
 42         :return:
 43         '''
 44         filePath = os.path.join(self.currDir, fileName)
 45         conn = self.request
 46         if os.path.exists(filePath):
 47             size = os.stat(filePath).st_size
 48             conn.sendall(bytes(str(size)+','+fileName,encoding='utf-8'))
 49             ret = conn.recv(1024)
 50             r = str(ret,encoding='utf-8')
 51             if r=='s':
 52                 has_send = 0
 53             else:
 54                 has_send = int(r)
 55             with open(filePath,'rb') as f:
 56                 f.seek(has_send)
 57                 while has_send<size:
 58                     data = f.read(1024)
 59                     conn.sendall(data)
 60                     has_send+=len(data)
 61             conn.sendall(bytes('0', encoding='utf-8'))
 62         else:
 63             conn.sendall(bytes('0', encoding='utf-8'))
 64 
 65     def createDir(self, currDir, newName):
 66         '''
 67         创建文件夹
 68         :param currDir:当前所在目录
 69         :param newName: 新文件夹名称
 70         :return: 是否创建成功
 71         '''
 72         mulu = os.path.join(baseHome, currDir)
 73         newFilePath = os.path.join(mulu, newName)
 74         if os.path.exists(newFilePath):
 75             return '2'
 76         else:
 77             ret = '0'
 78             try:
 79                 os.makedirs(newFilePath)
 80                 ret = '1'
 81             except OSError as e:
 82                 ret = '0'
 83             return ret
 84 
 85     def command(self):
 86         '''
 87         执行命令
 88         :return:
 89         '''
 90         conn = self.request
 91         a = conn.recv(1024)
 92         ret = str(a, encoding='utf-8')
 93         ret2 = subprocess.check_output(ret, shell=True)
 94         r = divmod(len(ret2), 1024)
 95         s = r[0]+1
 96         conn.sendall(bytes(str(s), encoding='utf-8'))
 97         conn.recv(1024)
 98         conn.sendall(ret2)
 99 
100     def md5(self, pwd):
101         '''
102         判断密码进行加密
103         :param pwd:
104         :return:
105         '''
106         hash = hashlib.md5(bytes('xx7',encoding='utf-8'))
107         hash.update(bytes(pwd, encoding='utf-8'))
108         return hash.hexdigest()
109 
110     def login(self, username, pwd):
111         '''
112         登录
113         :param username:用户名
114         :param pwd: 密码
115         :return: 是否登录成功
116         '''
117         if os.path.exists(settings.NAME_PWD):
118             s = pickle.load(open(settings.NAME_PWD,'rb'))
119         if username in s:
120             if s[username]==self.md5(pwd):
121                 return True
122             else:
123                 return False
124         else:
125             return False
126 
127     def regist(self, username, pwd):
128         '''
129         注册
130         :param username:用户名
131         :param pwd: 密码
132         :return: 是否注册成功
133         '''
134         conn = self.request
135         s = {}
136         if os.path.exists(settings.NAME_PWD):
137             s = pickle.load(open(settings.NAME_PWD, 'rb'))
138         if username in s:
139             return False
140         else:
141             s[username] = self.md5(pwd)
142             mulu = os.path.join(settings.USER_FILE, username)
143             os.makedirs(mulu)
144             pickle.dump(s, open(settings.NAME_PWD, 'wb'))
145             return True
146 
147     def before(self, username, pwd, ret):
148         '''
149         判断注册和登录,并展示用户的详细目录信息,支持cd和ls命令
150         :param username: 用户名
151         :param pwd: 密码
152         :param ret:
153         :return:
154         '''
155         conn = self.request
156         if ret == '1':
157             r = self.login(username,pwd)
158             if r:
159                 conn.sendall(bytes('y',encoding='utf-8'))
160             else:
161                 conn.sendall(bytes('n',encoding='utf-8'))
162         elif ret == '2':
163             r = self.regist(username, pwd)
164             if r:
165                 conn.sendall(bytes('y',encoding='utf-8'))
166             else:
167                 conn.sendall(bytes('n',encoding='utf-8'))
168 
169     def user_file(self, username):
170         '''
171         展示用户的详细目录信息,支持cd和ls命令
172         :param username: 用户名
173         :return:
174         '''
175         conn = self.request
176         mulu = baseHome
177         self.currDir = mulu
178         conn.sendall(bytes(mulu, encoding='utf-8'))
179         while True:
180             if conn:
181                 b = conn.recv(1024)
182                 ret = str(b, encoding='utf-8')
183                 try:
184                     a, b = ret.split(' ',1)
185                 except Exception as e:
186                     a = ret
187                 if a == 'cd':
188                     if b=='..':
189                         mulu = os.path.dirname(mulu)
190                     else:
191                         mulu = os.path.join(mulu, b)
192                     self.currDir = mulu
193                     conn.sendall(bytes(mulu, encoding='utf-8'))
194                 elif a=='ls':
195                     ls = os.listdir(mulu)
196                     print(ls)
197                     a = ','.join(ls)
198                     if a=='':
199                         a = '.'
200                     conn.sendall(bytes(a, encoding='utf-8'))
201                 elif a=='mkdir':
202                     m = self.createDir(self.currDir,b)
203                     conn.sendall(bytes(m, encoding='utf-8'))
204                 elif a=='q':
205                     break
206 
207     def handle(self):
208         conn = self.request
209         conn.sendall(bytes('welcome',encoding='utf-8'))
210         b = conn.recv(1024)
211         ret = str(b, encoding='utf-8')
212         c = conn.recv(1024)
213         r = str(c, encoding='utf-8')
214         username, pwd = r.split(',')
215         self.before(username, pwd, ret)
216         self.user_file(username)
217         while True:
218             a=conn.recv(1024)
219             ret = str(a, encoding='utf-8')
220             if ret == '1':
221                 self.recv_file()
222             elif ret=='2':
223                 self.command()
224             elif ret[0:4]=='get:':
225                 self.send_file(ret[4:])
226             elif ret=='q':
227                 break
228             else:
229                 pass
230 
231 if __name__ == '__main__':
232     server = socketserver.ThreadingTCPServer(('',9999), MyServer)
233     server.serve_forever()

客户端:

  1 #!/usr/bin/env python
  2 # -*-coding:utf-8 -*-
  3 
  4 import os, sys
  5 import socket
  6 
  7 def send_file(file_path):
  8     '''
  9     发送文件
 10     :param file_path:文件名
 11     :return:
 12     '''
 13     size = os.stat(file_path).st_size
 14     file_name = os.path.basename(file_path)
 15     obj.sendall(bytes(str(size)+','+file_name,encoding='utf-8'))
 16     ret = obj.recv(1024)
 17     r = str(ret, encoding='utf-8')
 18     if r=='s':  #文件不存在,从头开始传
 19         has_send = 0
 20     else:   #文件存在
 21         has_send = int(r)
 22     with open(file_path, 'rb') as f:
 23         f.seek(has_send)    #定位到已经传到的位置
 24         while has_send<size:
 25             data = f.read(1024)
 26             obj.sendall(data)
 27             has_send+=len(data)
 28             sys.stdout.write('
')  #情况文件内容
 29             sys.stdout.write('已发送%s%%|%s' % (int(has_send/size*100), (round(has_send/size*40)*'|')))
 30             sys.stdout.flush()  #强制刷出内存
 31     print('上传成功!
')
 32 
 33 def recv_file(toPath, getFile):
 34     '''
 35     接收要下载的文件
 36     :param toPath: 本地要保存文件的存放路径
 37     :param getFile: 要下载的文件名称
 38     :return:
 39     '''
 40     obj.sendall(bytes('get:'+getFile,encoding='utf-8'))
 41     a = str(obj.recv(1024), encoding='utf-8')
 42     file_size, file_name = a.split(',')
 43     file_size = int(file_size)
 44     if file_size == 0:
 45         print('没有找到此文件')
 46     else:
 47         new_file_name = os.path.join(toPath, file_name)
 48         if file_name in toPath:
 49             has_recv = os.stat(toPath).st_size
 50             obj.sendall(bytes(has_recv, encoding='utf-8'))
 51             with open(new_file_name,'ab') as f:
 52                 while has_recv<=file_size:
 53                     data = obj.recv(1024)
 54                     f.write(data)
 55                     has_recv+=len(data)
 56                     sys.stdout.write('
')  # 情况文件内容
 57                     sys.stdout.write('已接收%s%%|%s' % (int(has_recv / file_size * 100), (round(has_recv / file_size * 40) * '|')))
 58                     sys.stdout.flush()  # 强制刷出内存
 59         else:
 60             has_recv = 0
 61             obj.sendall(bytes('s', encoding='utf-8'))
 62             with open(new_file_name, 'wb') as f:
 63                 while has_recv<= file_size:
 64                     data = obj.recv(1024)
 65                     f.write(data)
 66                     has_recv += len(data)
 67                     sys.stdout.write('
')  # 情况文件内容
 68                     sys.stdout.write('已接收%s%%|%s' % (int(has_recv / file_size * 100), (round(has_recv / file_size * 40) * '|')))
 69                     sys.stdout.flush()  # 强制刷出内存
 70         print('接收成功!
')
 71 
 72 
 73 def command(command_name):
 74     '''
 75     执行命令
 76     :param command_name:
 77     :return:
 78     '''
 79     obj.sendall(bytes(command_name, encoding='utf-8'))
 80     ret = obj.recv(1024)    #接受命令需要接受的次数
 81     obj.sendall(bytes('收到次数',encoding='utf-8'))
 82     r = str(ret, encoding='utf-8')
 83     for i in range(int(r)):     #共需接收int(r)次
 84         ret = obj.recv(1024)    #等待客户端发送
 85         r = str(ret, encoding='GBK')
 86         print(r)
 87 
 88 def login(username, pwd):
 89     '''
 90     登录
 91     :param username: 用户名
 92     :param pwd: 密码
 93     :return: 是否登录成功
 94     '''
 95     obj.sendall(bytes(username+','+pwd, encoding='utf-8'))
 96     ret = obj.recv(1024)
 97     r = str(ret, encoding='utf-8')
 98     if r=='y':
 99         return True
100     else:
101         return False
102 
103 def regist(username, pwd):
104     '''
105     注册
106     :param username: 用户名
107     :param pwd: 密码
108     :return: 是否注册成功
109     '''
110     obj.sendall(bytes(username+','+pwd, encoding='utf-8'))
111     ret = obj.recv(1024)
112     r = str(ret, encoding='utf-8')
113     if r=='y':
114         return True
115     else:
116         return False
117 
118 def before(username, pwd):
119     '''
120     选择注册和登录,并展示用户的详细目录信息,支持cd和ls命令
121     :param username: 用户名
122     :param pwd: 密码
123     :return:
124     '''
125     a = input('请选择 1.登录  2.注册:')
126     obj.sendall(bytes(a, encoding='utf-8'))
127     # obj.recv()
128     if a=='1':
129         ret = login(username, pwd)
130         if ret:
131             print('登录成功')
132             return 1
133         else:
134             print('用户名或密码错误')
135             return 0
136     elif a=='2':
137         ret = regist(username, pwd)
138         if ret:
139             print('注册成功')
140             return 1
141         else:
142             print('用户名已存在')
143             return 0
144 
145 def user_file(username):
146     # obj.sendall(bytes('打印用户文件路径', encoding='utf-8'))
147     ret = obj.recv(1024)
148     r = str(ret, encoding='utf-8')
149     print(r)
150     while True:
151         a = input('输入 cd切换目录,ls查看目录详细信息,mkdir创建文件夹,q退出:')
152         a = a.strip()
153         obj.sendall(bytes(a, encoding='utf-8'))
154         if a=='q':
155             break
156         elif a[0:5]=='mkdir':
157             ret = obj.recv(1024)
158             r = str(ret, encoding='utf-8')
159             if r=='1':
160                 print('文件夹创建成功')
161             elif r=='2':
162                 print('文件夹已存在!')
163             else:
164                 print('创建失败!')
165         else:
166             ret = obj.recv(1024)
167             r=str(ret, encoding='utf-8')
168             if len(r)==1:   #判断是cd结果,还是ls的结果(ls只有一个子目录,直接打印)
169                 print(r)
170             else:
171                 li = r.split(',')
172                 for i in li:
173                     print(i)
174 
175 def main(username, pwd):
176     ret = obj.recv(1024)
177     r = str(ret, encoding='utf-8')
178     print(r)
179     result = before(username, pwd)  #判断登录/注册
180     if result:
181         user_file(username)
182         while True:
183             a = input('请选择 1.传文件 2.执行命令 3.收文件 q 退出:')
184             obj.sendall(bytes(str(a),encoding='utf-8'))
185             if a=='1':
186                 b = input('请输入文件路径:')
187                 if os.path.exists(b):
188                     send_file(b)
189                     obj.sendall(bytes('hhe', encoding='utf-8'))
190             elif a=='2':
191                 b = input('请输入command:')
192                 command(b)
193             elif a=='3':
194                 b = input('请输入存放路径:')
195                 c = input('请输入要获取的文件:')
196                 recv_file(b, c)
197             elif a=='q':
198                 break
199             else:
200                 print('输入错误!')
201 
202     obj.close()
203 
204 if __name__ == '__main__':
205     obj = socket.socket()
206     obj.connect(('192.168.1.100',9999))
207     username = input('请输入用户名:')
208     pwd = input('请输入密码:')
209     main(username, pwd)
原文地址:https://www.cnblogs.com/crucial/p/6106241.html