Python——用socket实现FTP上传下载功能

一、要求:

  开发一个支持多用户在线的FTP程序

  1、用户md5加密认证;

  2、允许同时多用户登录(socketserver);

  3、执行命令:

    客户端;ipconfig;

    服务端:subprocess;

  4、上传下载文件:

    显示进度条;

    断点续传;

二、程序目录结构:

  

三、程序运行顺序解构

  如图,服务端和客户端分别创建各自的类实例对象之后,启动程序用户从注册开始,客户端从start()函数开始按顺序调用register()、auth()、interactive()函数;其中客户端的register()和auth()函数直接和服务端进行通信,其他函数通过interactive()分拆参数之后进行反射执行函数;服务端接收来自客户端的消息之后,通过分发函数反射执行对应的功能函数,如用户注册、登录、上传、下载功能等。如此,通过反射模式,服务端和客户端都可以通过扩展函数实现更多功能。

  

四、客户端上传代码通信

  客户端上传功能,包括和服务端的四次通信应答,如下图;要注意的是,对于判断语句,可能会有两次接收或者发送通信,也有可能在判断之后可以忽略掉,如图中的第三次通信;下载功能原理一样。

   

 1 def file_put(self, user_data):
 2         """
 3         上传功能
 4         服务端处理客户端命令
 5         """
 6         # 第1次通信[接]:先序列化从客户端发送来的数据;
 7         json_data = json.loads(user_data)
 8 
 9         # 判断用户存储目录容量;
10         if not self.login_user.used_storage:
11             self.calculate_storage()
12 
13         # 第2次通信[发]:发送服务端用户目录容量标志判断码给服务端;
14         if self.login_user.used_storage + json_data['file_size'] > self.login_user.storage_limit:
15             self.request.sendall(bytes(json.dumps({"response": "303"}), encoding="utf-8"))
16         else:
17             self.request.sendall(bytes(json.dumps({"response": "301"}), encoding="utf-8"))
18 
19         # 获得当前目录绝对路径和文件大小;
20         file_abs_path = self.get_file_abs_path(json_data['file_name'])
21         total_file_size = int(json_data['file_size'])
22 
23         # 定义已接收的值;
24         has_received = 0
25 
26         # 如果目录文件存在;
27         if os.path.exists(file_abs_path):
28 
29             # 第3.1.Y次通信[发]:若目录文件存在,则向客户端发送2003标志码;
30             self.request.sendall(bytes("2003", encoding="utf8"))
31 
32             # 第3.1.1.Y/N次通信[接]:接收客户端回答,是否要继续上传的标志码;
33             is_continue = str(self.request.recv(1024), encoding="utf8")
34             if is_continue == "2004":
35 
36                 # 第3.1.2次通信[发]:继续上传,先发送已存在文件的大小;
37                 has_file_size = os.stat(file_abs_path).st_size
38                 self.request.sendall(bytes(str(has_file_size), encoding="utf8"))
39 
40                 # 此时,已接收的文件大小=已存在文件大小
41                 has_received += has_file_size
42 
43                 # 打开文件,追加;
44                 f = open(file_abs_path, "ab")
45 
46             # 第3.1.1.N次通信[接]:接收客户端回答,是否要继续上传的标志码;
47             else:
48 
49                 # 打开文件,新建写入;
50                 f = open(file_abs_path, "wb")
51 
52         # 第3.1.N次通信[发];如果目录文件不存在,则发送2002,并在本地新建文件写入;
53         else:
54             self.request.sendall(bytes("2002", "utf8"))
55             f = open(file_abs_path, "wb")
56 
57         # 第4次通信[接]:接收数据;
58         while has_received < total_file_size:
59             data = self.request.recv(1024)
60             f.write(data)
61             has_received += len(data)
62         print("ending")
63         f.close()
上传功能_服务端代码
 1 def instruction_put(self, instructions):
 2         """
 3         客户端上传命令
 4         :keyword instructions:用户输入的第二个参数
 5         """
 6         print("开始上传!")
 7         print(instructions)
 8         print(len(instructions))
 9         print(instructions[1])
10 
11         # 判断参数个数;
12         if len(instructions) < 2:
13             print("请添加要上传的文件路径!")
14             return
15         else:
16             local_path = instructions[1]
17 
18             # 先判断本地文件路径是否存在;
19             if os.path.exists(local_path):
20                 print("即将上传文件: %s" % local_path)
21                 file_name = os.path.basename(local_path)
22                 file_size = os.path.getsize(local_path)
23                 print(file_name, file_size)
24 
25                 # 第1次通信[发]:将上传命令序列化之后发送给服务端,告诉服务端要上传文件;
26                 command_str = "file_put|%s" % (json.dumps({"file_name": file_name, "file_size": file_size}))
27                 self.sock.sendall(bytes(command_str, encoding="utf-8"))
28 
29                 # 第2次通信[接]:接收服务端用户目录容量标志判断码给服务端,301或303;
30                 ok = str(self.sock.recv(1024), encoding="utf8")
31 
32                 # 第3.1.Y/N次通信[接]:接收来自服务端的判断码,如果目录文件存在则标志码为2003;
33                 result_exist = str(self.sock.recv(1024), encoding="utf8")
34                 print(result_exist)
35 
36                 has_sent = 0
37                 if result_exist == "2003":
38                     inp = input("文件存在,是否续传?Y/N").strip()
39 
40                     # 第3.1.1.Y次通信[发]:客户端回答,2004表示要继续上传;
41                     if inp.upper() == "Y":
42                         self.sock.sendall(bytes("2004", encoding="utf8"))
43 
44                         # 第3.1.2次通信[接]:接收服务端已存在的文件大小
45                         result_continue_pos = str(self.sock.recv(1024), encoding="utf8")
46                         print(result_continue_pos)
47                         has_sent = int(result_continue_pos)
48 
49                     else:
50                         # 第3.1.1.N次通信[发]:客户端回答,2005表示不继续上传;
51                         self.sock.sendall(bytes("2005", "utf8"))
52 
53                 # 以二进制打开,并拔到已发送位置
54                 file_obj = open(local_path, "rb")
55                 file_obj.seek(has_sent)
56 
57                 # 第4次通信[发]:发送数据;
58                 while has_sent < file_size:
59                     data = file_obj.read(1024)
60                     self.sock.sendall(data)
61                     has_sent += len(data)
62                     # self.bar(has_sent, file_size)
63                     self.progress(has_sent, file_size, 100)
64 
65                 file_obj.close()
66                 print("上传成功")
67 
68             else:
69                 print("文件不存在!")
上传功能_客户端代码

四、断点续传

  继点续传主要原理是:服务端记录已上传文件的大小,下次上传的时候,如果要断点续传的话,先将已上传的文件大小发给客户端,然后客户端从断点的位置在上传;如图标记所示。

  

四、显示进度条

  传输数据过程中,将已传输数据大小和文件数据总大小,传到进度条函数;代码如下。

 1     def progress(self, num, sum, l):
 2         """
 3         显示上传进度条
 4         num:已上传大小
 5         sum:文件总大小
 6         l:定义进度条大小
 7         """
 8         bar_length = l  # 定义进度条大小
 9         percent = float(num) / float(sum)
10         hashes = '=' * int(percent * bar_length)  # 定义进度显示的数量长度百分比
11         spaces = ' ' * (bar_length - len(hashes))  # 定义空格的数量=总长度-显示长度
12         sys.stdout.write(
13             "\r上传中: %.2fM/%.2fM %d%% [%s] " % (num / 1048576, sum / 1048576, percent * 100, hashes + spaces))  # 输出显示进度条
14         sys.stdout.flush()  # 强制刷新到屏幕

  

  

 

转载于:https://www.cnblogs.com/suliuer/p/5713284.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值