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的主要内容,如果未能解决你的问题,请参考以下文章

⭐Python实现多线程并发下载大文件(制作支持断点续传的下载器的绝佳参考⁉️)⭐

支持断点续传的大文件传输协议

用C实现断点续传的功能,详细点的实现原理是啥嘞

java文件断点续传的简单实现

局域网超大文件上传和断点续传的实现

超大文件上传和断点续传的组件