一、作业需求

  1. 用户加密认证(已完成)

  2. 多用户同时登陆(已完成)

  3. 每个用户有自己的家目录且只能访问自己的家目录(已完成)

  4. 对用户进行磁盘配额、不同用户配额可不同(已完成)

  5. 用户可以登陆server后,可切换目录(已完成)

  6. 查看当前目录下文件(已完成)

  7. 上传下载文件,保证文件一致性(已完成)

  8. 传输过程中现实进度条(已完成)

  9. 支持断点续传(未完成)

readme:
一、作业需求:

  1. 用户加密认证(已完成)

  2. 多用户同时登陆(已完成)

  3. 每个用户有自己的家目录且只能访问自己的家目录(已完成)

  4. 对用户进行磁盘配额、不同用户配额可不同(已完成)

  5. 用户可以登陆server后,可切换目录(已完成)

  6. 查看当前目录下文件(已完成)

  7. 上传下载文件,保证文件一致性(已完成)

  8. 传输过程中现实进度条(已完成)

  9. 支持断点续传(未完成)

二、博客地址:http://www.cnblogs.com/catepython/p/8616018.html

三、运行环境

操作系统:Win10

Python:3.6.2rcl

Pycharm:2017.1.14

四、功能实现

1)多用户同时登录,并做了用户不得重复登录判断(现为测试方便此调用方法已注释)

2)区分不同用户不同的文件目录

3)可在当前目录下上传/下载文件并保存

4)上传/下载文件进度显示

5)区分了用户本地/服务端文件目录

6)只能移动到自己家目录下的目录

cd /:移动到根目录下 cd …:返回上一级目录 cd + 目录名:移动到指定目录下

7)新增pwd查看当前路径操作

8)查看当前目录下文件信息

新增dir home:查看用户本地目录文件信息 dir server:查看用户服务端目录文件信息

9)每个用户有不同的磁盘配额

10)上传/下载文件后进行加密认证

11)新增mkdir操作:在当前目录下创建新目录文件
六、备注
1、断点续传功能有空时可以新增并完善

二、流程图


三、目录结构图

四、代码区

bin目录下程序开始文件
start_client.py

#
#-*- Coding:utf-8 -*-
# Author: D.Gray
import os,sys
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)from core import ftp_client
fc = ftp_client.Ftp_client()

start_server.py

#-*- Coding:utf-8 -*-
# Author: D.Gray
import os,sys
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)from core import ftp_server
fs = ftp_server.Ftp_server()

conf目下的setting.py系统配置文件

#-*- Coding:utf-8 -*-
# Author: D.Gray
import os,sys,socket
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)#IP和端口信息
IP_PORT = ("localhost",6969)#用户数据文件
USER_FILE = os.path.join(BASE_DIR,'db\\user_info')
#用户文件目录
USER_HOME = BASE_DIR

core目录下主程序文件ftp_client.py

#-*- Coding:utf-8 -*-
# Author: D.Gray
import sys,os,socket,hashlib,time,json
from conf import setting
from core import users
class Ftp_client(object):'''FTP客服端'''def __init__(self):'''构造函数'''self.client = setting.socket.socket()self.client.connect(setting.IP_PORT)self.help_info = """\033[33;1m请用'put'+'空格'+'文件名'的格式下载文件请用'get'+'空格'+'文件名'的格式上传文件请用'cd'+'空格'+'目录名'的格式进入家目录下的子文件夹请用'cd'+'空格'+'..'的格式返回上级目录请用'mkdir'+'空格'+'目录名'的格式创建家目录的文件夹输入'dir'+'空格'+'home'查看用户家目录输入'dir'+'空格'+'server'查看用户服务端家目录\033[0m"""if self.auth():self.start()def auth(self):'''用户登录认证函数1、用户输入账号密码2、序列化用户信息字典发送给服务端3、接收服务端用户登录认证消息4、认证成功返回True给构造函数5、用户进入start()函数进行指令操作:return:'''while True:username = input("请输入账户名>>>:").strip()password = input('请输入用户密码>>>:').strip()#auth = 'auth %s %s'%(username,password)mesg = {"action":'auth',"username":username,"password":password}self.client.send(json.dumps(mesg).encode())self.user_obj = users.Users(username)back_res = self.client.recv(1024).decode()if back_res == 'ok':print("\033[32;1m认证成功\033[0m")user = self.user_obj.get_user()self.user_name = user["username"]self.user_type = user["type"]self.user_path = user['home']self.disk_quota = user["disk_quota"]self.pwd_path = os.path.join(setting.USER_HOME,self.user_path,"user_home") #定义一个默认路径return Trueelif back_res == "302":print("\033[31;1m密码错误\033[0m")elif back_res == "301":print("\033[31;1m该用户已登录\033[0m")else:print("\033[31;1m用户不存在\033[0m")def start(self):'''用户操作函数1、用户输入操作指令2、判断操作指令是否有效3、反射指令:return:'''while True:user_inport = input("%s>>>:"%(self.user_name)).strip()if len(user_inport) == 0 :continueuser_inport = user_inport.split()if user_inport[0] == 'q':breakif hasattr(self,user_inport[0]):func = getattr(self,user_inport[0])func(user_inport)else:print("\033[31;1m请输入有效指令:\033[0m",self.help_info)continuedef put(self,cmd):'''下载服务端文件函数1、接收服务端回调信息(305 = 服务端文件不存在或下载文件大小)2、判断磁盘配额和文件大小3、接收服务端回调信息4、开始接收文件并打印进度条5、加密认证6、重新计算磁盘配额  调用Users类中update_disk_quota()方法将最新磁盘配额参数重新写入用户文件中:param cmd::return:'''if len(cmd) < 2:print("\033[31;1m请输入有效指令:\033[0m", self.help_info)else:'''下载服务端文件'''mesg = {"action": cmd[0],"file_name": cmd[1],"disk_quota": self.disk_quota}self.client.send(json.dumps(mesg).encode())server_back = self.client.recv(1024).decode()print("\033[32;1m收到服务器回调:\033[0m",server_back)if server_back == '305':print("\033[31;1m文件不存在\033[0m")else:file_total_size = int(server_back)print("\033[32;1m下载文件总大小:\033[0m", file_total_size)print("\033[32;1m磁盘配额还剩:%sM\033[0m" % mesg["disk_quota"])if file_total_size >= mesg["disk_quota"] * (2 ** 20):print('\033[31;1m磁盘配额不够无法下载文件\033[0m')else:revered_size = 0# file_path = os.path.join(setting.USER_HOME,self.user_path,"user_home",cmd[1])file_path = os.path.join(self.pwd_path,cmd[1])print('in the put_pwd_path:',file_path)self.client.send(b"ok")self.m = hashlib.md5()i = 0with open(file_path,'wb') as f:while revered_size < file_total_size:if file_total_size - revered_size < 1024:size = file_total_size - revered_sizeelse:size = 1024data = self.client.recv(size)revered_size += len(data)'''打印进度条'''str1 = "已接受 %sByte"%revered_sizestr2 = '%s%s'%(round((revered_size/file_total_size)*100,2),'%')str3 = '[%s%s]'%('*'*i,str2)sys.stdout.write('\033[32;1m\r%s%s\033[0m'%(str1,str3))sys.stdout.flush()i += 2time.sleep(0.3)'''加密认证'''self.m.update(data)f.write(data)self.encryption()'''磁盘配额'''new_disk_quota = round((mesg["disk_quota"] * (2 ** 20) - file_total_size) / (2 ** 20), 2)# mesg["disk_quota"]* (2 ** 20)  将用户文件中磁盘参数转成相应的Bytes数值self.user_obj.update_disk_quota(new_disk_quota)print("\033[32;1m磁盘配额还剩:%sM\033[0m"%new_disk_quota)def get(self,cmd):'''客户端上传文件至服务端函数1、判断指令格式是否正确2、上传文件或文件路径是否有效和存在3、获取文件大小4、判断磁盘配额是否大于文件大小5、获取服务端上传文件回调请求6、发送文件并打印进度条7、加密认证8、重新计算磁盘配额  调用Users类中update_disk_quota()方法将最新磁盘配额参数重新写入用户文件中:param cmd::return:'''if len(cmd) < 2:print("\033[31;1m请输入有效指令:\033[0m", self.help_info)else:'''上传文件'''file_path = os.path.join(self.pwd_path,cmd[1])if os.path.isfile(file_path):file_total_size = os.stat(file_path).st_sizemesg = {"action": cmd[0],"file_name": cmd[1],"disk_quota": self.disk_quota,"file_size" : file_total_size}print("\033[32;1m磁盘配额还剩:%sM\033[0m" % mesg["disk_quota"])if file_total_size >= mesg["disk_quota"]*(2**20):print("\033[31;1m磁盘配额不够无法上传文件\033[0m")else:self.client.send(json.dumps(mesg).encode())print("\033[32;1m上传文件总大小:\033[0m", file_total_size)self.client.recv(1024)print("开始发送文件")self.m = hashlib.md5()send_size = 0i = 0with open(file_path,'rb')as f:while send_size < file_total_size:if file_total_size - send_size <1024:size = file_total_size - send_sizedata = f.read(size)send_size += len(data)else:data = f.read(1024)send_size += len(data)self.client.send(data)'''打印进度条'''str1 = "已上传 %sByte:" %send_sizestr2 = '%s%s' % (round((send_size / file_total_size) * 100, 2), '%')str3 = '[%s%s]' % ('*'*i, str2)sys.stdout.write('\033[32;1m\r%s%s\033[0m' % (str1, str3))sys.stdout.flush()i += 2time.sleep(0.3)'''文件加密'''self.m.update(data)self.encryption()'''磁盘配额'''new_disk_quota = round((mesg["disk_quota"]*(2**20) - file_total_size)/(2**20),2)self.user_obj.update_disk_quota(new_disk_quota)print("\033[32;1m磁盘配额还剩:%sM\033[0m"%new_disk_quota)else:print("\033[31;1m文件不存在\033[0m")def encryption(self):'''文件加密函数1、判断用户是否需要加密2、取消加密发送'401'信息给服务端3、确认加密发送'400'信息给服务端4、接收服务端文件加密信息5、判断客户端和服务端文件加密信息是否一致:return:'''encryption = input("\n文件已接收是否需要加密认证...按q取消加密>>>")if encryption != 'q':self.client.send(b'400')print('\033[32;1m确认加密\033[0m')file_md5 = self.m.hexdigest()server_back_md5 = self.client.recv(1024).decode()print("\033[32;1m本地文件加密:%s\n服务端文件加密:%s\033[0m" % (file_md5, server_back_md5))if file_md5 == server_back_md5:print("\033[32;1m加密认证成功\033[0m")else:print("加密认证失败")else:self.client.send(b'401')print("\033[32;1m\n已取消加密.文件接收成功\033[0m")def dir(self,cmd):'''查看根目录下文件信息函数1、dir_home 查看用户本地文件内容2、dir_server 查看用户服务器文件内容3、接收服务端指令文件大小4、发送接收目录信息指令5、接收目录信息:param cmd::return:'''if len(cmd) < 2:print("\033[31;1m请输入有效指令:\033[0m", self.help_info)else:if cmd[1] == "home" or cmd[1] == 'server':mesg = {"action":cmd[0],"object":cmd[1]}self.client.send(json.dumps(mesg).encode())server_back = self.client.recv(1024).decode()print('\033[32;1m收到服务端回调指令大小:\033[0m',server_back)self.client.send("ok".encode())revered_size = 0revered_data = b''while revered_size < int(server_back):data = self.client.recv(1024)revered_data += datarevered_size = len(data)print('\033[32;1m实际收到指令大小:\033[0m',revered_size)else:print(revered_data.decode())else:print("\033[31;1m请输入有效指令:\033[0m", self.help_info)def mkdir(self,cmd):'''添加目录文件函数1、判断指令是否正确2、先获取当前路径3、判断所添加目录是否已存在4、使用os.mkdir()函数添加新目录5、新目录添加成功:param cmd::return:'''if len(cmd) < 2:print("\033[31;1m请输入有效指令:\033[0m", self.help_info)else:# file_path = os.path.join(setting.USER_HOME,self.user_path,"user_home",cmd[1])file_path = os.path.join(self.pwd_path,cmd[1])print("当前路径:", file_path)if os.path.exists(file_path):print("\033[31;1m该目录文件夹已存在\033[0m")else:os.mkdir(file_path)print("该目录文件夹创建成功")def cd(self,cmd):'''CD:移动到指定目录函数1、先判断指令是否正确2、判断路径是否有效3、根据输入做相应操作如:cd ..:移动到上一级目录   cd / :移动到根目录  cd 目录名:移动到指定目录4、拆分路径重新拼接新路径5、返回self.pwd_path当前所在目录:param cmd::return:'''if len(cmd) < 2:print("\033[31;1m请输入有效指令:\033[0m", self.help_info)else:if cmd[1] == '..':list = []pwd_path = os.path.join(self.pwd_path)for index in pwd_path.split('\\'):  #列表形式拆分当前目录路径以'\\'分隔list.append(index)          #将目录路径参数添加至list列表中list[0] = '%s%s'%(list[0],'/')  #将列表第一个元素 E: 字符串拼接成 E:/if list[-1] == "user_home":print("已在根目录下")else:del list[-1]    #删除列表最后个元素也就是上一级目录路径self.pwd_path = ''for item in list:       #重新拼接成新的路径self.pwd_path = os.path.join(self.pwd_path,item)print("当前路径:",self.pwd_path)#print(os.listdir(self.pwd_path))elif cmd[1] == '/':self.pwd_path = os.path.join(setting.USER_HOME,self.user_path,"user_home")print("已返回根目录:", self.pwd_path)else:pwd_path = os.path.join(self.pwd_path,cmd[1])   #移动到指定目录  cmd[1]目录名if os.path.isdir(pwd_path):#print(os.listdir(pwd_path))self.pwd_path = pwd_path    #返回用户当前路径print("当前路径:", self.pwd_path)else:print("\033[31;1m系统找不到指定的路径\033[0m")def pwd(self,cmd):'''显示当前目录路径:param cmd::return:'''print("当前路径:", self.pwd_path)print(os.listdir(self.pwd_path))def help(self,cmd):'''帮助文档函数:param cmd::return:'''print(self.help_info)

ftp_server.py

#-*- Coding:utf-8 -*-
# Author: D.Gray
import os,sys,json
from conf import setting
class Users(object):'''用户类'''def __init__(self,username):self.username = usernameself.user_file = setting.USER_FILE + "\\%s.json"%(self.username)#print(self.user_file)self.users_read = self.read_users()def read_users(self):'''读取用户文件信息函数1、判断用户文件是否存在(用户是否存在)2、遍历用户json文件中内容3、返回遍历内容:return:'''if os.path.exists(self.user_file):with open(self.user_file, 'r') as f:user_read = eval(f.read())return user_readdef get_user(self):'''1、判断服务端传过来的用户名参数是否与文件中用户名参数2、异常处理:用户名与用户文件参数类型不一直:return:'''#print('in the User_get_user:',user)try:if self.users_read["username"] == self.username:return self.users_readexcept TypeError as e:passdef update_status_close(self):'''修改用户登录状态函数0-未登录1-已登录无法重复登录:return:'''with open(self.user_file,'r') as f:fr = f.read()fd = eval(fr)with open(self.user_file,'w') as fw:res = fr.replace(str(fd['status']),str(1))fw.write(res)def update_status_open(self):'''修改用户登录状态函数0-未登录1-已登录无法重复登录:return:'''with open(self.user_file, 'r') as f:fr = f.read()fd = eval(fr)with open(self.user_file, 'w') as fw:res = fr.replace(str(fd['status']), str(0))fw.write(res)def update_disk_quota(self,new_disk_quota):'''更改用户磁盘配额函数:param new_disk_quota:接收客户端新磁盘配额参数:return:'''with open(self.user_file,'r') as f:fr = f.read()fd = eval(fr)with open(self.user_file,'w') as fw:res = fr.replace(str(fd["disk_quota"]),str(new_disk_quota))fw.write(res)

user

db/user_info/alex.json目录下的数据文件

{"username":"alex","password":"admin","status":0,"type":0,"home":"home\\alex","disk_quota":0.97
}

Python(高级FTP作业)相关推荐

  1. python作业高级FTP(第八周)

    作业需求: 1. 用户加密认证 2. 多用户同时登陆 3. 每个用户有自己的家目录且只能访问自己的家目录 4. 对用户进行磁盘配额.不同用户配额可不同 5. 用户可以登陆server后,可切换目录 6 ...

  2. Python学习笔记——基础篇【第七周】———FTP作业(面向对象编程进阶 Socket编程基础)...

    FTP作业 本节内容: 面向对象高级语法部分 Socket开发基础 作业:开发一个支持多用户在线的FTP程序 面向对象高级语法部分 参考:http://www.cnblogs.com/wupeiqi/ ...

  3. Python高级教程:玩转Linux操作系统

    Python高级教程:玩转Linux操作系统 操作系统发展史 只有硬件没有软件的计算机系统被称之为"裸机",我们很难用"裸机"来完成计算机日常的工作(如存储和运 ...

  4. python高级编程之网络编程

    Python高级之网络编程 端口 端口分类 知名端口 动态端口 查看端口 socket简介 电脑上进程之间的通信 什么是socket 创建socket 使用UDP套接字发送数据 使用UDP套接字接受数 ...

  5. 北理 嵩天老师 Python程序设计 课后作业易错题总结

    Python程序设计课后作业易错题总结 最近小c君在学习北理的嵩天老师在中国大学MOOC上发布的,<Python程序设计>课程. 下面是我总结该课程课后作业的易错题,分享给大家.同时非常推 ...

  6. 【Python高级之定时器】

    Python高级之定时器 定时器 定时器 如果需要使用定时器去触发一些事件,Python中通过线程实现定时器timer,定时器的意思也是:一段时间后调用一个函数. 用法: import threadi ...

  7. Python第四周作业之选择题

    Python第四周作业之选择题 1. 以下关于递归函数基例的说法错误的是: 2. 以下选项不是函数作用的是: 3. 以下关于Python函数说法错误的是: 4. 以下关于模块化设计描述错误的是: 5. ...

  8. python的运维开发课程_老男孩python高级运维开发课程

    L老男孩培训-python培训二期lesson01(11节) 01-第一天内容介绍及课前思想 02-python介绍及发展 03-python 发展 04-python安装 05-python编程风格 ...

  9. Python学习day5作业-ATM和购物商城

    Python学习day5作业 Python学习day5作业 ATM和购物商城 作业需求 ATM: 指定最大透支额度 可取款 定期还款(每月指定日期还款,如15号) 可存款 定期出账单 支持多用户登陆, ...

  10. Python高级特性:切片、迭代、列表生成式、生成器与迭代器

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 接着廖雪峰老师的学习教程,小编要开始加快推进Python的学习进程 ...

最新文章

  1. 【直播】张晋:心跳信号分类模型融合
  2. [云炬创业基础笔记]第一章创业环境测试2
  3. java八大排序算法
  4. 数组指定位置添加元素_数据结构--顺序表的9种基本运算,初始化,销毁,判断是否为空表,长度,求指定位置的元素值......
  5. 这几天的安排,先做个列表
  6. Android 分析监听器上的参数position和id(二)
  7. 开店软件透彻分析推荐
  8. java用dda算法绘制直线_使用DDA算法画出给定两点的直线
  9. [原创]解决Heritrix镜像方式存储路径中文乱码的解决方案
  10. 淘宝为什么放弃SpringCloud、Dubbo,选择了这个牛逼的神仙框架!贼爽
  11. Mac一体机忘记开机密码怎么办?
  12. 年包150万的腾讯程序员,深圳房产一千万,同学聚会只能排名第16!
  13. [教你做小游戏] 《五子棋》怎么判断输赢?你能5分钟交出代码吗?
  14. python怎么播放视频教程_python怎样播放视频?
  15. Kali Linux中Unable to locate package错误的解决方法2-1
  16. Ubuntu 21.04 如何进入命令行的登录界面
  17. 开源无国界?你开源出去的代码, 可能有一天你自己都不能用了!
  18. 前端工作过程遇到的问题总结(九)
  19. python中的Nonetype如何处理
  20. MosFET/FinFET/GAFET ——鳍式晶体管还能走多远

热门文章

  1. PaaS平台案例汇,企业PaaS平台搭建思路
  2. pyspark 读mysql数据_spark读mysql数据
  3. 利用iframe覆盖windowed plugin
  4. 一看就懂【来自英雄联盟盖伦的怒吼】与 Python 详解设计模式(二)观察者模式...
  5. linux网络设备驱动(一)
  6. 计算机网络10种,(完整版)计算机网络10种硬件设备介绍.doc
  7. 豆瓣电影TOP250和书籍TOP250爬虫
  8. 大陆资金港股打新股亲身体验全流程
  9. 修炼你的《九阳神功》行走江湖
  10. 网址在QQ微信被拦截怎么办?怎么样才能让被微信屏蔽的网址正常访问