python项目开发:ftp server开发
程序要求:
1.用户加密认证 (对用户名密码进行MD5验证)
2.允许同时多用户登陆 (使用socket server方法,为每个用户都创建一个信息文件)
3.每个用户有自己的家目录,且只能访问自己的家目录(每个用户都建立一个单独的目录)
4.对用户进行磁盘配额,每个用户的可用空间不同(用户信息字典中添加磁盘配额这一参数)
5.允许用户在ftp server 上随意切换目录
6.允许用户查看当前目录下的文件
7.允许上传和下载文件,保证文件的一致性(MD5验证)
8.文件传输过程中显示进度条
9.附加功能:支持文件的断点续传 分析:
数据存储:为每个用户都建立一个文件存储用户名、密码(MD5)、磁盘配额大小,为每一个用户都建立一个家目录
业务逻辑功能:可切换目录、创建目录、可查看目录下的文件、可上传、下载文件(支持断点续传、显示进度条) 开发过程中遇到的问题:
客户端和服务端需要互相传递消息,这样编写一端的程序的时候需要知道另一端程序的结构,
于是要先建立好两端之间大致传递的消息,编写好一端后,再编写另一端 程序目录框架:
程序代码:
README:
程序框架:
|--ftp
|--ftpclinet
|--bin
|--start #程序入口
|--core
|--ftpclient #程序主要逻辑
|--ftpserver
|--bin
|--start #程序入口
|--core
|--main #程序主要逻辑
|--conf
|--settings #定义database、homes的路径
|--homes
|--database
|--modules
|--authendencate #登陆模块
|--socket_server #socket通信模块
|--README ## 状态码
400 用户认证失败
401 命令不正确
402 文件不存在
403 创建文件已经存在
404 磁盘空间不够
405 不续传 200 用户认证成功
201 命令可以执行
202 磁盘空间够用
203 文件具有一致性
205 续传 000 系统交互码
切换目录:cd .. 返回上一级目录 cd dirname 进入dirname
注:用户登录后默认进入家目录,只可在家目录下随意切换 创建目录:mkdir dirname 查看当前路径: pwd 查看当前路径下的文件名和目录名: dir 初始化程序是自动创建用户: {"东云博士":"","马孔多":"","江南":"","白早":""}
ftpclient:
bin目录下的start:
# -*- coding:utf-8 -*-
#!/user/bin/env.python
#Author:Mr Wu import os,sys
BASE_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_dir)
from core import ftpclient if __name__ == '__main__':
obj = ftpclient.ftp_client(("127.0.0.1",9999))
obj.start()
core目录下的ftpclient:
# -*- coding:utf-8 -*-
#!/user/bin/env.python
#Author:Mr Wu
import os,hashlib,socket,json,sys,time class ftp_client(object):
'''ftp client客户端'''
def __init__(self,ip_port):
self.ip_port = ip_port def connect(self):
'''连接套接字地址'''
self.client = socket.socket()
self.client.connect(self.ip_port) def start(self):
'''开始运行'''
self.connect()
while True:
user_name = input("input your name>>>:")
user_password = input("input your password>>>:")
user_msg = "%s:%s"%(user_name,user_password)
self.client.send(user_msg.encode("utf-8"))
status_code = self.client.recv(1024).decode()
if status_code == "":
print("[400]认证失败!")
continue
elif status_code == "":
print("[200]认证成功!")
#self.user_name = user_name
self.interaction() def interaction(self):
'''开始交互'''
while True:
command = input("input command>>>:")
if not command: continue
get_command = command.split()[0]
if hasattr(self,get_command):
func = getattr(self,get_command)
func(command)
else:
print("[401]命令错误!") def get(self,command):
'''下载文件'''
self.client.send(command.encode())
status_code = self.client.recv(1024).decode()
if status_code == "":
print("[401]命令错误")
elif status_code == "":
filename = command.split()[1]
if os.path.isfile(filename):
self.client.send("".encode())
self.client.recv(1024)
received_size = os.stat(filename).st_size
self.client.send(str(received_size).encode())
status_code = self.client.recv(1024).decode()
if status_code == "":
print("[205]可续传!")
self.client.send("".encode())
elif status_code == "":
print("[405]文件完整,不可续传!")
return
else:
self.client.send("".encode())
received_size = 0
print("[205]可下载!")
m = hashlib.md5()
f = open(filename,"wb")
file_total_size = int(self.client.recv(1024).decode())
self.client.send("".encode())
f.seek(received_size)
while received_size < file_total_size:
last_size = file_total_size - received_size
if last_size < 1024:
size = last_size
else:
size = 1024
data = self.client.recv(size)
m.update(data)
f.write(data)
self.__progress_bar(received_size,file_total_size,"下载中")
received_size += len(data)
f.close()
received_md5 = self.client.recv(1024).decode()
md5 = m.hexdigest()
if received_md5 == md5:
print("[203]文件具有一致性!")
else:
print("[403]文件不一致!")
def put(self,command):
'''上传文件'''
self.client.send(command.encode("utf-8"))
status_code = self.client.recv(1024).decode()
if status_code == "":
filename = command.split()[1]
if os.path.isfile(filename):
file_total_size = os.stat(filename).st_size
self.client.send(str(file_total_size).encode())
status_code = self.client.recv(1024).decode()
if status_code == "":
self.client.send("".encode())
status_code = self.client.recv(1024).decode()
if status_code == "":
print("[205]可上传")
elif status_code == "":
print("[404]目录空间不足~")
return
elif status_code == "":
print("[405]文件完整,不续传~")
return
else:
print("[402]文件不存在!")
return self.client.send("".encode())
received_size = int(self.client.recv(1024).decode())
m = hashlib.md5()
f = open(filename,"rb")
f.seek(received_size)
for line in f:
self.client.send(line)
m.update(line)
received_size += len(line)
self.__progress_bar(received_size,file_total_size,"上传中")
f.close()
self.client.send(m.hexdigest().encode())
status_code = self.client.recv(1024).decode()
if status_code == "":
print("[203]文件具有一致性")
else:
print("[403]传输文件不一致!")
elif status_code == "":
print("[401]命令错误!")
def cd(self,command):
'''切换目录'''
self.client.send(command.encode("utf-8"))
status_code = self.client.recv(1024).decode()
if status_code == "":
print("[201]命令执行成功")
elif status_code == "":
print("[401]命令错误")
def dir(self,command):
'''查看当前目录下的文件'''
self.client.send(command.encode("utf-8"))
status_code = self.client.recv(1024).decode()
if status_code == "":
self.client.send("".encode())
data = self.client.recv(1024).decode()
print(data)
elif status_code == "":
print("[401]命令错误!")
def mkdir(self,command):
'''创建目录'''
self.client.send(command.encode())
status_code = self.client.recv(1024).decode()
if status_code == "":
print("[401]命令错误")
elif status_code == "":
print("[403]创建目录已存在!")
elif status_code == "":
print("[201]创建目录成功!")
def pwd(self,command):
'''查看当前用户路径'''
self.client.send(command.encode())
status_code = self.client.recv(1024).decode()
if status_code == "":
self.client.send("".encode())
data = self.client.recv(1024).decode()
print(data)
elif status_code == "":
print("[401]命令错误") def __progress_bar(self,received_size,file_total_size,mode):
width = 50 #进度条长度
percent = received_size/file_total_size #进度条百分比
use_num = int(percent*width) #已使用的进度条长度
space_num = int(width - use_num) #未使用的进度条长度
percent = percent*100
sys.stdout.write("%s[%s%s]%d%%\r"%(mode,use_num*"#",space_num*" ",percent))
sys.stdout.flush()
return
ftpserver:
bin目录下的start:
dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_dir)
from core import ftpserver if __name__ == '__main__':
ftpserver.My_ftp_server()
core目录下的ftpserver:
# -*- coding:utf-8 -*-
#!/user/bin/env.python
#Author:Mr Wu
import sys,os,json,hashlib,socketserver from conf import settings
from modules import socket_server class My_ftp_server(object):
'''ftp初始化'''
def __init__(self):
self.interaction()
def interaction(self):
self.create_user()
self.create_dir()
server = socketserver.ThreadingTCPServer(settings.Ip_port,socket_server.ftp_server)
server.serve_forever()
def create_user(self):
'''创建用户信息文件'''
for key in settings.users_dict:
user_database = {}
name = key
password = settings.users_dict[name]
password = self.hash(password)
user_database["name"] = name
user_database["password"] = password
user_database["limit_size"] = settings.Disk_quota #默认磁盘配额
user_database["home_dir"] = settings.homes_dir + r"\%s"%name #用户家目录
user_db = settings.database_dir + r"\%s.db"%name
if not os.path.isfile(user_db):
f = open(user_db,"w")
json.dump(user_database,f)
f.close() def create_dir(self):
'''创建用户家目录'''
for key in settings.users_dict:
name = key
user_dir = settings.homes_dir + r"\%s"%name
if not os.path.isdir(user_dir):
os.popen("mkdir %s"%user_dir) def hash(self,password):
'''MD5加密'''
m = hashlib.md5()
m.update(password.encode())
return m.hexdigest()
modules目录下的authendencate:
# -*- coding:utf-8 -*-
#!/user/bin/env.python
#Author:Mr Wu
import sys,os,json,hashlib from conf import settings class authendencate(object):
'''用户登录模块'''
def __init__(self,user_msg):
self.user_msg = user_msg def auth_user(self):
'''验证用户登陆'''
msg_list = self.user_msg.split(":")
user_name = msg_list[0]
user_password = self.hash(msg_list[1])
user_database = self.get_user_msg(user_name)
if user_database:
if user_name == user_database["name"] and user_password == user_database["password"]:
return user_database def get_user_msg(self,user_name):
'''获取用户信息'''
user_db = settings.database_dir + r"\%s.db"%user_name
if os.path.isfile(user_db):
f = open(user_db,"r")
user_database = json.load(f)
f.close()
return user_database
def hash(self,passwd):
'''md5加密'''
m = hashlib.md5()
m.update(passwd.encode())
return m.hexdigest()
modules目录下的socket_server:
# -*- coding:utf-8 -*-
#!/user/bin/env.python
#Author:Mr Wu
import sys,os,socketserver,hashlib
from conf import settings
from modules import authendencate class ftp_server(socketserver.BaseRequestHandler):
'''ftp server端'''
def handle(self):
try:
self.conn = self.request
while True:
user_msg = self.conn.recv(1024).decode()
auth_result = self.auth(user_msg)
status_code = auth_result[0]
self.conn.send(status_code.encode("utf-8"))
if status_code == "": continue
elif status_code == "":
user_database = auth_result[1]
self.user_dir = user_database["home_dir"] #用户默认目录
self.home_dir = user_database["home_dir"] #用户家目录
self.limit_size = user_database["limit_size"] #用户磁盘配额
while True:
command = self.conn.recv(1024).decode()
get_command = command.split()[0]
if hasattr(self,get_command):
func = getattr(self,get_command)
func(command)
else:
self.conn.send("".encode("utf-8"))
except ConnectionResetError as e:
print(e) def get(self,command):
'''下载文件'''
if len(command.split()) > 1:
filename = command.split()[1]
file_db = self.user_dir + r"\%s"%filename
if os.path.isfile(file_db):
file_total_size = os.stat(file_db).st_size
self.conn.send("".encode("utf-8"))
status_code = self.conn.recv(1024).decode()
if status_code == "":
received_size = 0
elif status_code == "":
self.conn.send("".encode())
received_size = int(self.conn.recv(1024).decode())
if received_size < file_total_size:
self.conn.send("".encode())
self.conn.recv(1024)
else:
self.conn.send("".encode())
return
f = open(file_db,"rb")
m = hashlib.md5()
self.conn.send(str(file_total_size).encode())
self.conn.recv(1024)
f.seek(received_size)
for line in f:
self.conn.send(line)
m.update(line)
f.close()
self.conn.send(m.hexdigest().encode())
else:
self.conn.send("".encode("utf-8"))
else:
self.conn.send("".encode("utf-8")) def put(self,command):
'''上传文件'''
if len(command) > 1:
self.conn.send("".encode())
file_name = command.split()[1]
file_db = self.user_dir + r"\%s"%file_name
file_total_size = int(self.conn.recv(1024).decode())
if os.path.isfile(file_db):
received_size = os.stat(file_db).st_size
if received_size < file_total_size:
self.conn.send("".encode())
else:
self.conn.send("".encode())
return
else:
received_size = 0
self.conn.send("".encode())
put_size = file_total_size - received_size
status_code = self.__get_size(put_size)
self.conn.recv(1024)
if status_code == "":
self.conn.send("".encode())
else:
self.conn.send("".encode())
return
self.conn.recv(1024)
self.conn.send(str(received_size).encode())
m = hashlib.md5()
f = open(file_db,"wb")
f.seek(received_size)
while received_size < file_total_size:
last_size = file_total_size - received_size
if last_size < 1024:
size = last_size
else:
size = 1024
data = self.conn.recv(1024)
received_size += len(data)
f.write(data)
m.update(data)
f.close()
md5 = m.hexdigest()
new_md5 = self.conn.recv(1024).decode()
if md5 == new_md5:
self.conn.send("".encode())
else:
self.conn.send("".encode()) def cd(self,command):
'''切换目录'''
if len(command) > 1:
dir_name = command.split()[1]
dir_path = self.user_dir + r"\%s"%dir_name
if dir_name == ".." :
if len(self.user_dir) > len(self.home_dir):
self.conn.send("".encode())
self.user_dir = os.path.dirname(self.user_dir)
else:
self.conn.send("".encode())
elif os.path.isdir(dir_path):
self.user_dir = dir_path
self.conn.send("".encode()) else:
self.conn.send("".encode())
else:
self.conn.send("".encode())
def dir(self,command):
'''查看当前目录下的文件'''
if command == "dir":
self.conn.send("".encode())
self.conn.recv(1024)
data = os.popen("dir %s"%self.user_dir)
self.conn.send(data.read().encode())
else:
print("".encode())
def mkdir(self,command):
'''创建目录'''
if len(command) > 1:
filename = command.split()[1]
dir_path = self.user_dir + r"\%s"%filename
if not os.path.isdir(dir_path):
self.conn.send("".encode())
os.popen("mkdir %s"%dir_path)
else:
self.conn.send("".encode())
else:
self.conn.send("".encode())
def pwd(self,command):
'''查看当前用户路径'''
if command == "pwd":
self.conn.send("".encode())
data = self.user_dir
self.conn.recv(1024)
self.conn.send(data.encode())
else:
self.conn.send("".encode()) def auth(self,user_msg):
'''用户登陆'''
obj = authendencate.authendencate(user_msg)
auth_result = obj.auth_user()
if not auth_result:
return ""
else:
return "",auth_result def __get_size(self,file_size):
'''计算目录空间大小'''
dir_size = os.stat(self.home_dir).st_size + file_size
if dir_size < self.limit_size:
return ""
else:
return ""
conf目录下的settings:
# -*- coding:utf-8 -*-
#!/user/bin/env.python
#Author:Mr Wu
import os,sys
BASE_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
'''用户信息的属主目录'''
database_dir = os.path.join(BASE_dir,"database")
'''用户目录的属主目录'''
homes_dir = os.path.join(BASE_dir,"homes")
'''用户信息字典'''
users_dict = {"东云博士":"","马孔多":"","江南":"","白早":""}
'''默认磁盘配额'''
Disk_quota = 102400
'''ip地址和端口'''
Ip_port = ("0.0.0.0",9999)
程序运行示例:
断点续传:
python项目开发:ftp server开发的更多相关文章
- 【ArcGIS Server 开发系列】Flyingis六大系列讲座精品PDF奉献
转自:http://www.cnblogs.com/gispeng/archive/2008/07/24/1250116.html [ArcGIS Server 开发系列]Flyingis六大系列讲座 ...
- 笔记14:Docker 部署Python项目
Docker 部署Python项目 导读: 软件开发最大的麻烦事之一就是环境配置,操作系统设置,各种库和组件的安装.只有它们都正确,软件才能运行.如果从一种操作系统里面运行另一种操作系统,通常我们采取 ...
- (数据科学学习手札121)Python+Dash快速web应用开发——项目结构篇
本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...
- Eclipse开发Python项目
最近倒腾python自带的开发工具idle,用的很不习惯,还是用Eclipse编写python项目方便(自动补齐,智能报错,调试方便),下面就说说怎么用Eclipse编写python代码吧~ 1.安装 ...
- Python+USB+Vnet+FTP传输文件开发记录
做一个Python+USB+Vnet+FTP传输文件开发记录
- python项目开发视频
精品Python项目开发学习视频 所属网站分类: 资源下载 > python视频教程 作者:乐天派 链接:http://www.pythonheidong.com/blog/article/44 ...
- IDEA 学习笔记之 Python项目开发
Python项目开发: 下载Python: https://www.python.org/downloads/release/python-363/ 安装Python: 配置环境变量(path): C ...
- Node.js 从零开发 web server博客项目[express重构博客项目]
web server博客项目 Node.js 从零开发 web server博客项目[项目介绍] Node.js 从零开发 web server博客项目[接口] Node.js 从零开发 web se ...
- Node.js 从零开发 web server博客项目[数据存储]
web server博客项目 Node.js 从零开发 web server博客项目[项目介绍] Node.js 从零开发 web server博客项目[接口] Node.js 从零开发 web se ...
随机推荐
- 游戏人生(一),我的lua之旅:那些坑爹的CCBReaderLoad
首先,我们说说这个CCBReaderLoad. 这个脚本是cocos2dx自带的一个lua+cocosbuilder 的工具,详细功能呐,往下看. 先来看下我遇到的一个问题: ----美工给了我一个. ...
- 我们的一个已投产项目的高可用数据库实战 - mongo 副本集的搭建具体过程
我们的 mongo 副本集有三台 mongo 服务器:一台主库两台从库. 主库进行写操作,两台从库进行读操作(至于某次读操作到底路由给了哪台,仲裁决定).实现了读写分离.这还不止,假设主库宕掉,还能实 ...
- crm使用soap启用和停用记录
function demo() { //操作记录的id var targetId = "a8a46444-ba10-e411-8a04-00155d002f02"; ...
- 【Linux】Ubuntu 开机默认亮度改动方法
换了ubuntu 之后.发现开机屏幕都是"最大亮度",每次都要到设置中手动调节,非常麻烦.于是想到去改动这个设置.Google一通,别人可行的办法到我这就没用了.郁闷.最后是在st ...
- 【Ubuntu QQ】记如何在Ubuntu上安装QQ(附下载)
什么困扰着一批批的ubuntu桌面用户?是麻花藤.哦不,是QQ,怎么在ubuntu上安装完美无瑕的QQ. 最佳解决方案在“三”部分,当然前两个也不失为解决方案 一.尝试的开始 配置: 双系统:Wind ...
- poi读写Excel
poi读写Excel 对于一个程序员来说,文件操作是经常遇到的,尤其是对Excel文件的操作. 在这里介绍一下我在项目中用到的一个操作Excel的工具——POI.关于POI的一些概念,网络上很多,详细 ...
- lucene 范围过滤
Lucene里面有关于Filter的整体知识 下面,我们来看下具体的在代码里怎么实现,先来看下我们的测试数据 Java代码 id score bookname ena ...
- Win7系统专栏
1.去掉Win7快捷方式小箭头的方法如下: 使用普通方法会使系统出现异常,比如开始菜单程序无法删除.收藏夹无法展开等,网上流传使用透明图标的方法会在快捷方式上留下一块黑痣,下面的方法是小君研究出来的, ...
- JavaScript学习三
2019-05-30 20:38:50 逻辑运算符 && || ! !如果对非布尔值取反,则将会把数值变成布尔值,然后再取反 隐式类型转化 为任意的数据类型做两次非运算,既可将其转换成 ...
- nginx编译安装新模块
nginx的模块是需要重新编译nginx,而不是像apache一样配置文件引用.so 这里以安装第三方ngx_http_google_filter_module模块为例 下载第三方扩展模块ngx_ht ...