python作业高级FTP(第八周)

作业需求:

1. 用户加密认证

2. 多用户同时登陆

3. 每个用户有自己的家目录且只能访问自己的家目录

4. 对用户进行磁盘配额、不同用户配额可不同

5. 用户可以登陆server后,可切换目录

6. 查看当前目录下文件

7. 上传下载文件,保证文件一致性

8. 传输过程中现实进度条

9. 支持断点续传

 

思路分析:

上一个简单服务器的升级版本,先一个版本链接:http://www.cnblogs.com/sean-yao/p/7772159.html,在原有代码中,重构并实现9个需求,提升程序健壮性:

更新如下:

1. FTP数据存储目录,使用相对路径 ,提升迁移性。

2. FTP在上传下载的时候有粘包处理,其他命令如ls,pls等也做了粘包处理。

3.增加了ConnectionResetError,Exception,UnicodeDecodeError,socket.error等异常处理。

对于高级FTP需求:

1. 用hashlib加密用户输入的密码让Server保存Md5值,实现简单认证加密。

2. 多用户同时登陆使用socketserver模块(上一版本已经实现),控制线程并发多用户同时登陆和操作。

3. 创建用户时候将密码文件FTP目录,相对路径写到密码文件中,认证成功后可以调用相对路径,然后使用OS模块的os.chdir($dir)进行切换用户家目录操作。

4. 用random函数随机一个512M-1024M之间的磁盘配额,(用户剩余空间 = 限额 - 家目录总文件大小)对比文件大小就可以进行磁盘配额的操作。

5. 用户操作使用cd命令,可以切换到家目录的任意目录(前一版本已经实现)。

6. 通过ls查看家目录下的二级目录三级目录等文件(前一版本已经实现)。

7. 上传下载文件,保证文件一致性使用hashlib,让服务端传送客户端进行校验。

8. 传输过程中现实进度条 上传和下载的进度条都已经完成,使用progressbar模块。

9. 断点续传,创建临时文件,客户端上传时,服务器检查临时文件,有就发大小发给客户端,客户端seek到文件断点处给服务器端发送数据。

 

代码示例:

  此次作业是以上一个版本代码做的重构http://www.cnblogs.com/sean-yao/p/7772159.html,所以这里只放入新增加的部分包含ftp_client.py,ftp_server.py,total_size_class.py,auth.py

README:

作者:yaobin
版本:高级Ftp示例版本 v0.2
开发环境: python3.6程序介绍:1. 用户加密认证
2. 多用户同时登陆
3. 每个用户有自己的家目录且只能访问自己的家目录
4. 对用户进行磁盘配额、不同用户配额可不同
5. 用户可以登陆server后,可切换目录
6. 查看当前目录下文件
7. 上传下载文件,保证文件一致性
8. 传输过程中现实进度条
9. 支持断点续传使用说明:
1.可以在Linux和Windows都可以运行
2.Linux调用了cd,mkdir,ls,rm,命令
3.Windows调用了cd,md,dir,del,命令
On Linux,Windows
Client: Python3 ./Ftp_Client.py
put 上传
get 下载
mkdir 创建目录
ls  查看文件信息
rm  删除文件
drm 删除目录
Server:Python3 ./Ftp_Server.py
put 上传
get 下载
mkdir 创建目录
ls  查看文件信息
rm  删除文件
drm 删除目录文件目录结构:
├─简单Ftp            #程序执行目录
│  README
│  __init__.py
│
├─bin
│      Ftp_Client.py    #客户端程序
│      Ftp_Server.py    #服务器端程序__init__.py
│
├─conf
│  │  setting.py        #配置文件
│  │  __init__.py
│  │
│  └─__pycache__
│          setting.cpython-36.pyc
│          __init__.cpython-36.pyc
│
├─core
│  │  auth.py           #用户验证逻辑交互
│  │  commands.py       #命令逻辑交互
│  │  ftp_client.py     #sock_客户端逻辑交互
│  │  ftp_server.py     #sock_服务端逻辑交互
│  │  logger.py         #日志逻辑交互---未完成
│  │  main.py           #客户端程序
│  │  __init__.py
│  │
│  └─__pycache__
│          auth.cpython-36.pyc
│          commands.cpython-36.pyc
│          ftp_client.cpython-36.pyc
│          ftp_server.cpython-36.pyc
│          main.cpython-36.pyc
│          __init__.cpython-36.pyc
│
├─db
│  │  __init__.py
│  │
│  ├─colin              #用户目录
│  │  │  colin.bak
│  │  │  colin.dat      #用户账号密码文件
│  │  │  colin.dir
│  │  │  __init__.py
│  │  │
│  │  └─colin           #用户ftp家目录
│  │      │  __init__.py
│  │      │
│  │      └─aaa
│  ├─pub                #ftp程序模拟pub目录
│  │      FTP作业.7z
│  │      socket通信client.py
│  │      __init__.py
│  │      选课系统.png
│  │
│  └─user_path          #用户路径文件,判断用户家目录
│          path
│
├─logs                  #日志未完成
│      access.log
│      transmission.log
│      __init__.py
│
├─src
│  │  auth_class.py         #用户验证类
│  │  linux_cmd_class.py    #linux命令类
│  │  server_class.py       #server_socket类
│  │  windows_cmd_class.py  #server命令类
│  │  __init__.py
│  │  total_size_class.py   #磁盘配额类
│  └─__pycache__
│          auth_class.cpython-36.pyc
│          linux_cmd_class.cpython-36.pyc
│          server_class.cpython-36.pyc
│          windows_cmd_class.cpython-36.pyc
│          __init__.cpython-36.pyc
│
└─test                  #测试
        args_test.py__init__.py
View Code

ftp_client.py: FTP客户端交互程序

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Colin Yao
import os
import sys
import socket
import time
import hashlib
import json
import progressbar
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)
from conf import settingclass Ftp_client(object):def link(self):try:self.sending_msg_list = []self.ip_addr = '127.0.0.1'self.ip_port = 62000self.client = socket.socket()self.client.connect((self.ip_addr, self.ip_port))while True:self.sending_msg = Noneself.data = self.client.recv(1024)print("\033[34;1m[+]Server>>>recv: %s\033[0m" %self.data.decode())self.menu()sending_msg = input('请输入命令>>>:')self.sending_msg_list = sending_msg.split()if len(self.sending_msg_list)  == 0:data_header = {"test1": {"action": "","file_name": "","size": 0}}self.client.send(json.dumps(data_header).encode())elif len(self.sending_msg_list) == 1:if self.sending_msg_list[0] == 'ls' or self.sending_msg_list[0] == 'pls':self.cmd()else:#get BUG fixdata_header ={"test1": {"action": self.sending_msg_list[0],"file_name": ".","size": 0}}self.client.send(json.dumps(data_header).encode())elif len(self.sending_msg_list) >= 2 :#windows/linux文件路径处理if setting.os_res == 'Windows':try :new_path = self.sending_msg_list[1].encode('utf-8')self.res_new = self.sending_msg_list[1].strip().split('\\')self.file_name1 = self.res_new[-1]except IndexError:passelif setting.os_res == 'Linux':try:self.res_new = self.sending_msg_list[1].strip().split('/')self.file_name1 = self.res_new[-1]except IndexError:passif self.sending_msg_list[0] == "put":try:self.put(self.sending_msg_list[1])except IndexError:self.client.send('put'.encode())elif self.sending_msg_list[0] == "get":try:self.get(self.file_name1)except IndexError and ValueError:self.client.send('get'.encode())elif self.sending_msg_list[0] == "exit":breakelif self.sending_msg_list[0] == "ls" or self.sending_msg_list[0] == "pls":try:self.cmd()except AttributeError:self.cmd()else:#cd rm drm mkdir 命令等try:data_header = {"test1": {"action": self.sending_msg_list[0],"file_name": self.file_name1,"size": 0}}self.client.send(json.dumps(data_header).encode())except AttributeError:data_header =  {"test1": {"action": self.sending_msg_list[0],"file_name": "","size": 0}}self.client.send(json.dumps(data_header).encode())except ConnectionResetError and ConnectionAbortedError:print("[+]Server is Down ....Try to Reconnect......")self.link()#cmd方法def cmd(self):if  len(self.sending_msg_list) == 1:data_header =  {"test1": {"action": self.sending_msg_list[0],"file_name": "","size": 0}}elif  len(self.sending_msg_list) >= 2:data_header = { "test1": {"action": self.sending_msg_list[0],"file_name": self.file_name1,"size": 0}}self.client.send(json.dumps(data_header).encode())  #发送cmd请求主要是ls会有粘包的可能cmd_res_size = self.client.recv(1024)self.client.send('准备分割粘包'.encode('utf-8'))cmd_res_size1 = int(cmd_res_size.decode())received_size = 0received_data = b''while received_size < int(cmd_res_size.decode()):data = self.client.recv(1024)received_size += len(data)received_data += dataelse:print(received_data.decode())#get方法def get(self,file_name):md5_file = hashlib.md5()data_header =  {"test1": {"action": "get","file_name": file_name,"size": 0}}self.client.send(json.dumps(data_header).encode())  #发送get请求self.data = self.client.recv(1024)     #拿到sizeif self.data.decode() == '0':self.client.send(b'come on')else:self.client.send(b'come on')file_size = int(self.data.decode())def file_tr():P = progressbar.ProgressBar()N = int(self.data.decode())P.start(N)file_object = open(file_name, 'wb')received_size = 0while received_size <  file_size :#粘包处理if file_size -received_size > 1024:size = 1024#小于1024处理'''elif file_size < 1024:size = file_sizeelse:size = file_size - received_sizerecv_data = self.client.recv(size)#接收数据的时候和进度条保持一致received_size += len(recv_data)md5_file.update(recv_data)P.update(received_size)file_object.write(recv_data)else:P.finish()new_file_md5 = md5_file.hexdigest()file_object.close()time.sleep(0.1)print('[+]Client:New_File[%s]Recv Done File_Md5:%s' % (file_name, new_file_md5))if os.path.exists(file_name) == False:file_tr()else:user_choice = input('文件已经存在即将要删除并下载 [y/删掉旧文件 | n/覆盖旧文件] >>>:')if user_choice == 'y':os.remove(file_name)file_tr()elif user_choice == 'n':file_tr()else:file_tr()#put方法def put(self,file_name):if os.path.exists(file_name)== True:if os.path.isfile(file_name):file_obj = open(file_name, "rb")data_header =  {"test1": {"action": "put","file_name": self.file_name1,"size": os.path.getsize(self.sending_msg_list[1].encode())}}self.client.send(json.dumps(data_header).encode())self.data = self.client.recv(1024)#有 not enough 数据  还有数字字符的数据resume_message = (self.data.decode())if resume_message == 'not enough Spare_size':print('[+]----Server Space not enough put smaller----')data_header = {"test1": {"action": "e1930b4927e6b6d92d120c7c1bba3421","file_name": "","size": 0}}self.client.send(json.dumps(data_header).encode())else:resume_size = int(self.data.decode())file_send_size = os.path.getsize(self.sending_msg_list[1])#断点续传处理if  resume_size  < file_send_size and resume_size !=0 :file_obj = open(file_name, "rb")md5_file = hashlib.md5()file_obj.seek(resume_size)#seek到断点位置file_resume_send_size = (os.path.getsize(self.sending_msg_list[1])-resume_size)#断点大小data_header = {"test1": {"action": "resume_put","file_name": self.file_name1,"size": file_resume_send_size}}self.client.send(json.dumps(data_header).encode())self.data = self.client.recv(1024)#测试发送P = progressbar.ProgressBar()P.start(file_send_size)new_size = resume_sizefor line in file_obj:self.client.send(line)new_size += len(line)#time.sleep(1)查看断点续传效果
                            P.update(new_size)md5_file.update(line)P.finish()file_obj.close()print("[+]Client>>>recv:Send Resume File Done Md5",md5_file.hexdigest())#文件下载处理else:file_obj = open(file_name, "rb")md5_file = hashlib.md5()new_size =0P = progressbar.ProgressBar()P.start(file_send_size)for line in file_obj:self.client.send(line)new_size += len(line)P.update(new_size)md5_file.update(line)P.finish()file_obj.close()print("[+]Client>>>recv:Send File Done Md5:",md5_file.hexdigest())else:print('[+]file is no valid.')self.client.send('cmd'.encode())else:print('[+] File Not Found')data_header = {"test1": {"action": "aaaa","file_name": "","size": 0}}self.client.send(json.dumps(data_header).encode())def menu(self):menu = ''';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;进入目录     cd       eg: cd /tmp/python查看文件     ls       eg: ls /tmp/README创建目录     mkdir    eg: mkdir /tmp/python删除文件     rm       eg: rm /tmp/README删除目录     drm      eg: drm /tmp/python上传文件     put      eg: put /python/README下载文件     get      eg: get /python/README新增命令     pls      eg: pls 查看db/pub目录文件注销用户     exit注意事项     notice   windows和linux的路径不同;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;'''print(menu)
View Code

ftp_server.py:FTP服务器端交互程序

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Colin Yao
import os
import sys
import time
import json
import shelve
import hashlib
import socket
import traceback
import socketserver
BASE_DIR  = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)
from conf import setting
from core.commands import Commands
from src.total_size_class import quotaclass Ftp_server(socketserver.BaseRequestHandler):def parsecmd(self,data):data = json.loads(data.decode())file_action = data["test1"]["action"]file_path = data["test1"]["file_name"]file_size = int(data["test1"]["size"])file_obj = (setting.data_path+setting.file_object)file_md5 = hashlib.md5()print('from ip : %s information : %s' % (self.client_address[0], self.data.decode()))#固定用户工作家目录if setting.os_res == 'Windows':if os.getcwd() == (setting.bin_path):os.chdir(file_obj )else:try:with open(setting.path_file, 'r')  as f:f1 = []f2 = f.readline().split('\\')f1.append(f2)f3 = os.getcwd()f4 = f3.split('\\')if f4[5] == f1[0][1] and f4[6] == f1[0][2]:passelse:os.chdir(file_obj )except IndexError as e:os.chdir(file_obj)elif setting.os_res == 'Linux':if os.getcwd() == (setting.bin_path):os.chdir(file_obj )else:try:with open(setting.path_file, 'r')  as f:f1 = []f2 = f.readline().split('/')f1.append(f2)f3 = os.getcwd()f4 = f3.split('/')if f4[5] == f1[0][1] and f4[6] == f1[0][2]:passelse:os.chdir(file_obj )except IndexError as e:os.chdir(file_obj)#'用户家目录文件大小file_obj_size用户磁盘配额大小 quota_sizefile_obj_size = quota(file_obj).directory_size()user_info = shelve.open(setting.data_path + setting.file_object)if setting.os_res == 'Windows':with open(setting.path_file, 'r')  as f:f1 = []f2 = f.readline().split('\\')f1.append(f2)user_name_key = f1[0][1]self.quota_user_size = user_info[user_name_key][3]user_info.close()elif setting.os_res == 'Linux':with open(setting.path_file, 'r')  as f:f1 = []f2 = f.readline().split('/')f1.append(f2)user_name_key = f1[0][1]self.quota_user_size = user_info[user_name_key][3]user_info.close()try:#上传方法if file_action == 'put':spare_size = (self.quota_user_size - file_obj_size)def file_tr():md5_file = hashlib.md5()self.request.send(str(file_size).encode())file_object = open((file_path + '.new'), 'wb')received_size = 0while received_size < file_size:if file_size - received_size > 1024:size = 1024elif file_size < 1024:size = file_sizeelse:size = file_size - received_sizerecv_data = self.request.recv(size)received_size += len(recv_data)md5_file.update(recv_data)file_object.write(recv_data)#print(file_size, received_size)else:print('[+]File Recv Successful')file_object.close()#重命名文件self.request.send(b'File Data Recv Successful Md5:%s'%(md5_file.hexdigest().encode()))os.rename((file_path + '.new'),file_path)#self.request.send(b'File Data Recv Successful')def put_size():#磁盘限额和断点续传的处理if file_size <= spare_size:if os.path.exists(file_path + '.new'):new_size = os.path.getsize(file_path + '.new')if new_size == 0 or new_size>file_size:file_tr()else:self.request.send(str(new_size).encode())#如果不存在.new的临时文件else:file_tr()elif file_size > spare_size or spare_size == 0:print('[+] Server Spare_size not enough',self.data.decode())self.request.send(b'not enough Spare_size')#文件路径不存在if os.path.exists(file_path) == False:put_size()#路径存在处理else:#保持文件最新,put bug fixif file_path == '.':self.request.send(b"-b:bash:[+]Server[%s]---file is no valid." % file_path.encode())else:os.remove(file_path)put_size()#***断点续传方法***elif file_action == 'resume_put':spare_size = (self.quota_user_size - file_obj_size)def resume_put_file_tr():md5_file = hashlib.md5()self.request.send(b'read recv resume data')if os.path.exists(file_path + '.new'):file_object = open((file_path + '.new'), 'ab')received_size = 0while received_size < file_size:if file_size - received_size > 1024:size = 1024elif file_size < 1024:size = file_sizeelse:size = file_size - received_sizerecv_data = self.request.recv(size)received_size += len(recv_data)md5_file.update(recv_data)file_object.write(recv_data)#print(file_size, received_size)else:file_object.close()print('[+]File Resume Recv Successful',time.time())os.rename((file_path + '.new'), (file_path))self.request.sendall(b'File Resume Recv Successful Md5 %s'%(md5_file.hexdigest().encode()))#断点续传只要判断磁盘限额即可def resume_put_size():if file_size <= spare_size:resume_put_file_tr()elif file_size > spare_size or spare_size == 0:print('[+] Server Spare_size not enough', self.data.decode())self.request.send(b'not enough Spare_size')#文件路径不存在处理if os.path.exists(file_path) == False:resume_put_size()else:# 保持文件最新
                    os.remove(file_path)resume_put_size()#下载方法elif file_action == 'get':#公共下载目录为db/pub,客户端默认下载路径为用户家目录'
                os.chdir(setting.ftp_path)if os.path.isfile(file_path) and os.path.exists(file_path):if setting.os_res == 'Windows':file_size = os.path.getsize(setting.ftp_path + '\\' + file_path)file_obj_path = (setting.ftp_path + '\\' + file_path)elif setting.os_res == 'Linux':file_size = os.path.getsize(setting.ftp_path + '/' + file_path)file_obj_path = (setting.ftp_path + '/' + file_path)file_obj = open(file_path, "rb")#磁盘配额-用户家文件总大小=剩余磁盘空间,用剩余磁盘空间与下载文件大小做对比spare_size = (self.quota_user_size - file_obj_size)if file_size <= spare_size:self.request.send(str(file_size).encode())self.request.recv(1024)for line in file_obj:#md5校验
                            file_md5.update(line)self.request.send(line)file_obj.close()self.request.send(b"[+]File[%s]Send Done File_Md5:%s" %(file_path.encode(),file_md5.hexdigest().encode()))#磁盘配额处理elif file_size > spare_size or spare_size ==0 :#文件总大小>剩余空间发送消息给客户端
                        self.request.send(str(0).encode())self.request.recv(1024)self.request.send(b'-b:bash: There is Not Enough Space The rest is %smb'%str(round(spare_size / 1024 / 1024)).encode())else:#get不存在文件,导致json.decoder.JSONDecodeError,处理方式传一个jsonif file_path == '.':self.request.send(b"-b:bash:[+]Server[%s]---file is no valid."%file_path.encode())else:self.request.send(str(0).encode())self.request.recv(1024)self.request.send(b"-b:bash:[+]Server[%s]---file is no valid."%file_path.encode())#查看FTP文件方法elif file_action == 'pls':os.chdir(setting.ftp_path)res = Commands(file_path).ls()if setting.os_res == 'Windows':res1 = res.decode('gbk')elif setting.os_res == 'Linux':res1 = res.decode()if len(res1) == 0:#粘包处理passself.request.send(str(len(res1.encode())).encode('utf-8'))client_send = self.request.recv(1024)self.request.send(res1.encode('utf-8'))self.request.send(b'-bash: [%s] [%s]:' %(file_action.encode(),file_path.encode()))os.chdir(file_obj)#查看文件方法elif file_action == 'ls':res = Commands(file_path).ls()#上一版本没有文件大小信息,只是传送了列表if setting.os_res == 'Windows':res1 = res.decode('gbk')elif setting.os_res == 'Linux':res1 = res.decode()if len(res1) == 0:#粘包处理passself.request.send(str(len(res1.encode())).encode('utf-8'))client_send = self.request.recv(1024)self.request.send(res1.encode('utf-8'))self.request.send(b'-bash: [%s] [%s]:'% (file_action.encode(),file_path.encode()))#CD方法elif  file_action== 'cd':if os.path.exists(file_obj + '\\' + file_path) == True:os.chdir(file_obj + '\\'+ file_path)self.request.send(b'-bash: [%s] [%s]:'%(file_action.encode(),file_path.encode()))else:self.request.send(b'-bash:Directory Exitis')#创建目录方法elif file_action == 'mkdir':if os.path.exists(file_path) == True:self.request.send(b'-bash: directory exitis ')else:res = Commands(file_path).mkdir()self.request.send(b'-bash: [%s] [%s]:'%(file_action.encode(),file_path.encode()))#文件删除方法elif file_action == 'rm':if os.path.isfile(file_path) == True:res = Commands(file_path).rm()self.request.send(b'-bash: [%s] [%s]:'%(file_action.encode(),file_path.encode()))else:self.request.send(b'-bash: [%s]: Not file'%file_path.encode())#目录删除方法elif file_action == 'drm':if os.path.isdir(file_path) == True:Commands(file_path).drm()self.request.send(b'-bash: %s: Delete OK'%file_path.encode())else:self.request.send(b'-bash: [%s]: No such File or Directory '%file_path.encode())elif file_action == 'e1930b4927e6b6d92d120c7c1bba3421':spare_size = (self.quota_user_size - file_obj_size)self.request.send(b'-bash: [+] Not Enough Space Spare_size is %smb'%str(round(spare_size/1024/1024)).encode())else:self.request.send(b'-bash:Command or File Not Found ')#异常处理except Exception and UnicodeDecodeError:traceback.print_exc()def handle(self):print("[+] Server is running on port:62000", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))while True:try:#调整一下socket.socket()的位置每次重新连接都生成新的socket实例,避免因为意外而导致socket断开连接print("[+] Connect success -> %s at ", self.client_address, time.strftime("%Y%m%d %H:%M:%S", time.localtime()))self.request.send(b'\033[34;1mWelcome,-bash version 0.0.1-release \033[0m ')while True:self.data = self.request.recv(1024)data = self.dataself.parsecmd(data)if not self.data:print("[+]Error: Client is lost")breakexcept socket.error :print("[+]Error get connect error")breakcontinue
View Code

total_size_class.py:磁盘配额类

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Colin Yao
import os,sys
BASE_DIR  = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)
from conf import settingclass quota(object):def __init__(self,file_obj):self.file_obj = file_objself.t1_size = 0def directory_size(self):rootdir = self.file_obj  # 获取当前路径t_size = 0for dirname in os.listdir(rootdir):  #获取当前路径所有文件和文件夹if setting.os_res == 'Windows':Dir = os.path.join(rootdir+'\\'+ dirname)  # 路径补齐elif setting.os_res == 'Linux':Dir = os.path.join(rootdir + '/' + dirname)  # 路径补齐if (os.path.isdir(Dir)):for r, ds, files in os.walk(Dir):for file in files:  # 遍历所有文件size = os.path.getsize(os.path.join(r, file))  # 获取文件大小self.t1_size += sizesize = os.path.getsize(Dir)t_size += sizetotal_size = (self.t1_size+t_size)return  total_size
View Code

auth.py:用户认证

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Colin Yao
import os,sys,shelve,random
BASE_DIR  = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)
from conf import  setting
from src.auth_class import Auth
from core.commands import  Commandsclass Auth_ftp(object):def __init__(self,username,user_passwd):self.user_data = {}self.username = usernameself.username_passwd = user_passwdos_res = setting.platform.system()#使用相对路径适合迁移if os_res == 'Windows':  # 用户密码文件self.passwd_data_path = os.path.join('\\' + username + '\\' + username + '.' + 'dat')self.passwd_data = os.path.join('\\' + username + '\\' + username)self.file_object = os.path.join( '\\' + self.username)else:self.passwd_data_path = \os.path.join('/' + username + '/' + username + '.' + 'dat')self.passwd_data = \os.path.join('/' + username + '/' + username)self.file_object = os.path.join( '/' + username)#磁盘配额512M-1024M用户名key,写入用户名密码路径磁盘配额到字典user_obj = (self.username,self.username_passwd,self.passwd_data,random.randint(536870912, 1073741824))self.user_data[self.username] = user_obj#验证用户是否存在def auth_user_passwd(self):#根据用户字典文件判断用户是否存在os_res = os.path.exists(setting.data_path+self.passwd_data_path)if os_res !=False:user_file = shelve.open(setting.data_path+self.passwd_data)if self.user_data[self.username][0] in user_file \and  user_file[self.username][1] == self.username_passwd:print("Welcome,%s,您的身份验证成功"%self.username)user_file.close()else:return Falseelse:return Truedef add_user_passwd(self):res = os.path.exists(setting.data_path+self.file_object)if res != True:Commands(setting.data_path+self.file_object).mkdir() #用户账号密码文件Commands(setting.data_path+self.passwd_data).mkdir()  #用户上传下载目录user_file = shelve.open(setting.data_path+self.passwd_data)user_file.update(self.user_data)print("用户创建成功")else:print("账号信息出现问题,请联系管理员....")
View Code

main.py:登陆认证主交互程序

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Colin Yao
import os
import sys
import json
import hashlib
BASE_DIR  = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)
from conf import  setting
from core.auth import Auth_ftp
from core.ftp_client import Ftp_clientclass Admin(object):def run(self):exit_flag = Falseprint('欢迎来到简单FTP程序,本地测试请启动server')#用户加密认证方法密码进行md5加密与服务器做验证def md5(user_passwd):md5 = hashlib.md5()md5.update(user_passwd.encode())passwd = md5.hexdigest()return  passwdmenu = u'''\033[32;1m1.登陆2.注册3.退出\033[0m'''while not exit_flag:print(menu)user_option = input('请输入您的操作,输入q退出>>>:').strip()#登陆if user_option == '1':user_name = input('请输入用户名>>>:').strip()new_user_passwd = input('请输入您的密码>>>:').strip()user_passwd = md5(new_user_passwd)file_object = (Auth_ftp(user_name, user_passwd).passwd_data)  #传入路径变量res = Auth_ftp(user_name,user_passwd).auth_user_passwd()if res ==  None:with open(setting.path_file, 'w',encoding='utf-8') as f:f.write(file_object);f.close()os.chdir(setting.data_path +file_object)Ftp_client().link()elif res == False:print('%s用户密码不正确' % user_name)else:print('请先注册')elif user_option == '2':user_name = input('请输入用户名>>>:').strip()new_user_passwd = input('请输入您的密码>>>:').strip()user_passwd = md5(new_user_passwd)user_res = Auth_ftp(user_name, user_passwd).auth_user_passwd()if user_res ==  None:print("%s用户不需要注册"%user_name)file_object = (Auth_ftp(user_name, user_passwd).passwd_data)with open(setting.path_file, 'w',encoding='utf-8') as f:f.write(file_object);f.close()Ftp_client().link()elif user_res == False:print("%已注册用户,密码不正确" % user_name)elif user_res == True:Auth_ftp(user_name, user_passwd).add_user_passwd()  #创建用户名密码文件等file_object = (Auth_ftp(user_name, user_passwd).passwd_data)with open(setting.path_file, 'w',encoding='utf-8') as f:f.write(file_object);f.close()Ftp_client().link()else:sys.exit('异常退出')elif user_option == 'q' or user_option == '3':sys.exit()else:print('输入的选项不正确,请重新输入')
#Admin().run()
View Code

setting.py:配置文件

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Colin Yao
import os
import sys
import platform
import logging
BASE_DIR  = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
Relative_Path = os.path.dirname(os.path.abspath(__file__))
sys.path.append(BASE_DIR)
os_res = platform.system()if  os_res == 'Windows':data_path = os.path.join(BASE_DIR + '\db')ftp_path = os.path.join(BASE_DIR + '\db\pub')path_file = os.path.join(BASE_DIR + '\db\\user_path\path')bin_path = os.path.join(BASE_DIR+'\\bin')
else:data_path = os.path.join(BASE_DIR + '/db')ftp_path = os.path.join(BASE_DIR + '\db\pub')path_file = os.path.join(BASE_DIR + '/db/user_path/path')bin_path = os.path.join(BASE_DIR + '/bin')
#路径文件判断
if os.path.exists(path_file):with open(path_file, 'r', encoding='utf-8')as f:file = f.readlines()if len(file):file_object=file[0]else:with open(path_file, 'w', encoding='utf-8')as f:f.write('touch something');f.close()
else:with open(path_file, 'w', encoding='utf-8')as f:f.write('touch something');f.close()
View Code

 

程序测试样图

1. 断点续传

创造断点文件

续传文件

2. 下载进度条和Md5校验

3. 上传进度条和Md5校验

 

4. 用户可以登陆server后,可切换目录,可查看文件

 

5. 查看用户家目录文件(粘包处理)

 

转载于:https://www.cnblogs.com/sean-yao/p/7882638.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/281804.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Edge 浏览器被爆存在 XSS 绕过漏洞

来自知名安全测试套件Burp Suite厂商PortSwigger的安全专家Gareth Heyes近日在微软Edge浏览器的内置XSS过滤器存在绕过漏洞&#xff0c;这就意味着尽管微软在Edge浏览器中进行了大量的安全策略部署&#xff0c;但用户浏览网页的时候依然有可能让攻击者通过这种方式在Edge浏览器…

来了!十大更新

面向 Windows 10 正式版用户&#xff0c;微软发布了 2022 年 10 月更新。Windows 10 版本 21H1 更新后操作系统内部版本升级至 Build 190432130/2132&#xff08;带外更新&#xff09;。Windows 10 版本 21H2 更新后操作系统内部版本升级至 Build 19044.2130/2132&#xff08;带…

使用ansible 批量分发SSH Key

先确保你电脑有ansible&#xff0c;我是mac的用brew安装,其他可用yum安装brew search ansiblebrew install ansible我已经安装好2.7了生成下自己的key&#xff0c;一路回车ssh-keygen -t rsa编辑host&#xff0c;添加需要增加ssh key的机器vi /etc/ansible/hosts【hostgroup】我…

使用Git简单笔记

这里只是作为简单的笔记整理&#xff0c;第一次使用的推荐先看一下廖大的教程&#xff0c;内容很多很细&#xff0c;可以边看边练、看不懂的地方先记着、争取七七八八看下来。 心情不佳的分割线 廖雪峰的git教程&#xff1a; https://www.liaoxuefeng.com/wiki/001373951630592…

部署站点支持Https访问的方法

1、申请公钥和私钥&#xff0c;放到服务器 2、编辑default配置文件 改为 加上证书路径 ps:泛域名支持admin.xxx.com、demo.xxx.com等等&#xff0c;而免费的Lets Encrypt仅支持www.xxx.com和xxx.com 整理自www.laravist.com转载于:https://www.cnblogs.com/lamp01/p/6952464.ht…

Hadoop/HDFS命令

Hadoop中文手册&#xff1a;http://hadoop.apache.org/docs/r1.0.4/cn/commands_manual.html 英文手册&#xff1a;http://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/CommandsManual.html Hadoop fs命令 hadoop fs: 该命令可以用于其他文件系统&#x…

内置IOC容器ServiceCollection

.NetCore内置IOC容器ServiceCollection一、IOC介绍IOC&#xff1a;全名(Inversion of Control)-控制反转IOC意味着我们将对象的创建控制权交给了外部容器&#xff0c;我们不管它是如何创建的&#xff0c;我们只需要知道&#xff0c;当我们想要某个实例时&#xff0c;我们可以直…

React+Redux仿Web追书神器

引言 由于 10 月份做的 React Native 项目没有使用到 Redux 等库&#xff0c;写了一段时间想深入学习 React&#xff0c;有个想法想做个 demo 练手下&#xff0c;那时候其实还没想好要做哪一个类型的&#xff0c;也看了些动漫的&#xff0c;小说阅读&#xff0c;聚合资源的开源…

windows配置solr5.5.2(不通过tomcat,使用内置jetty)

一、windows下配置solr5.5.2(不通过tomcat,使用内置jetty) 第一步&#xff1a;安装Jdk1.7 Solr5.5好像只支持Jdk1.7及以上的版本&#xff0c;没亲测&#xff0c;solr6.0是只支持jdk1.8及以上的&#xff0c;下图为启动solr时的截图&#xff1a; 如何在windows环境下配置jdk及验证…

【C# Personal Handbook】运行环境

一、CLR、CLI、CTS、CLS、BCL、FCL简介CLI&#xff08;公共语言基础&#xff09;CLI是微软公司向ECMA提交的一份语言和数据格式规范&#xff0c;CLR是目前为止唯一一个公共语言基础的实现版本。CLI包括了公共类型系统&#xff08;CTS&#xff09;、公共中间语言&#xff08;CIL…

如何完善自己的知识结构

★领域 &#xff08;本来想用“学科”这个词&#xff0c;后来觉得“学科”的范畴还是偏小&#xff0c;就改用“领域”&#xff09;  按照传统的习惯&#xff0c;通常会把知识归类到不同的领域&#xff08;比如&#xff1a;文学、数学、计算机、烹调、等等&#xff09;。 ◇领…

java调c++代码_Java中调用C++代码的实现 | 学步园

JNI为 Java Native Interface 即Java本地接口&#xff0c;使用此种方式可以对C/C代码进行调用&#xff0c;其在本质上是对C/C生成的动态库进行调用而不是直接对C/C代码进行调用Java代码如下&#xff1a;public class TestJNI{//JNI在本质上是调用C/C的动态库来实现的&#xff…

jeesite1.X 集成多数据源

2019独角兽企业重金招聘Python工程师标准>>> 网上看了几个例子&#xff0c;都是相同数据源的动态切换&#xff0c;如果不是同一种数据库类型&#xff0c;分页查询就出问题。经过研究解决问题。 jeesite.properties配置多数数据源地址,这里以mysql5.7和sqlserver2008…

k8s HPA(HorizontalPodAutoscaler)-自动水平伸缩

Horizontal Pod Autoscaling in Kubernetes写在前面我们平时部署web服务&#xff0c;当服务压力大撑不住的时候&#xff0c;我们会加机器(加钱)&#xff1b;一般没有上容器编排是手动加的&#xff0c;临时加的机器&#xff0c;临时部署的服务还要改Nginx的配置&#xff0c;最后…

spark java 逻辑回归_逻辑回归分类技术分享,使用Java和Spark区分垃圾邮件

原标题&#xff1a;逻辑回归分类技术分享&#xff0c;使用Java和Spark区分垃圾邮件由于最近的工作原因&#xff0c;小鸟很久没给大家分享技术了。今天小鸟就给大家介绍一种比较火的机器学习算法&#xff0c;逻辑回归分类算法。回归是一种监督式学习的方式&#xff0c;与分类类似…

jQuery.extend()方法

定义和用法jQuery.extend()函数用于将一个或多个对象的内容合并到目标对象。 注意&#xff1a; 1. 如果只为$.extend()指定了一个参数&#xff0c;则意味着参数target被省略。此时&#xff0c;target就是jQuery对象本身。通过这种方式&#xff0c;我们可以为全局对象jQuery添加…

Wget用法、参数解释的比较好的一个文章

一个语句就可以下载cvpr2016的全部论文&#xff1a; wget -c -N --no-clobber --convert-links --random-wait -r -p -E -e robotsoff -U mozilla http://www.cv-foundation.org/openaccess/CVPR2016.py 其中&#xff0c;-c表示断点续传&#xff1b;-N表示已经下载的内容不再重…

.NET VS智能提示汉化 (.Net6)

先上现成的.net6汉化文件&#xff0c;可以手动下载后参照 [如何为 .NET 安装本地化的 IntelliSense 文件 ](https://learn.microsoft.com/zh-cn/dotnet/core/install/localized-intellisense)进行安装。或者使用后文的工具进行自动安装。无对照英文在前中文在前汉化内容来自 官…

老人寻求到一名程序员,用2W行代码给自己打造了一幅肖像画

今天翻墙看了下国外的论坛&#xff0c;看到了一位版主给一位老人描绘肖像画的文章&#xff0c;不得不说这位大佬是真的厉害&#xff0c;近20000行代码&#xff0c;而且还画的很像&#xff0c;像小编我这种手残党&#xff0c;用笔也不能画出来&#xff0c;不得不服&#xff0c;今…

一题多解,ASP.NET Core应用启动初始化的N种方案[下篇]

[接上篇]“天下大势&#xff0c;分久必合&#xff0c;合久必分”&#xff0c;ASP.NET应用通过GenericWebHostService这个承载服务被整合到基于IHostBuilder/IHost的服务承载系统中之后&#xff0c;也许微软还是意识到Web应用和后台服务的承载方式还是应该加以区分&#xff0c;于…