Python实现支持并发断点续传的FTP
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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(‘\r‘) #情况文件内容 29 sys.stdout.write(‘已发送%s%%|%s‘ % (int(has_send/size*100), (round(has_send/size*40)*‘|‘))) 30 sys.stdout.flush() #强制刷出内存 31 print(‘上传成功!\n‘) 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(‘\r‘) # 情况文件内容 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(‘\r‘) # 情况文件内容 68 sys.stdout.write(‘已接收%s%%|%s‘ % (int(has_recv / file_size * 100), (round(has_recv / file_size * 40) * ‘|‘))) 69 sys.stdout.flush() # 强制刷出内存 70 print(‘接收成功!\n‘) 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)
以上是关于Python实现支持并发断点续传的FTP的主要内容,如果未能解决你的问题,请参考以下文章