声明:

  该项目参考学习地址:

    http://www.cnblogs.com/lianzhilei/p/5869205.html , 感谢博主分享,如有侵权,立即删除。

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

要求:

  1. 用户加密认证
  2. 允许同时多用户登录
  3. 每个用户有自己的家目录 ,且只能访问自己的家目录
  4. 对用户进行磁盘配额,每个用户的可用空间不同
  5. 允许用户在ftp server上随意切换目录
  6. 允许用户查看当前目录下文件
  7. 允许上传和下载文件,保证文件一致性
  8. 文件传输过程中显示进度条
  9. 附加功能:支持文件的断点续传

程序:

1、README

# 作者介绍:
author: hkey # 博客地址: # 功能实现: 作业:开发一个支持多用户在线的FTP程序 要求: 用户加密认证
允许同时多用户登录
每个用户有自己的家目录 ,且只能访问自己的家目录
对用户进行磁盘配额,每个用户的可用空间不同
允许用户在ftp server上随意切换目录
允许用户查看当前目录下文件
允许上传和下载文件,保证文件一致性
文件传输过程中显示进度条
附加功能:支持文件的断点续传 # 目录结构: FTP/
├── ftp_client/ # ftp客户端程序
│   ├── ftp_client.py # 客户端主程序
│   ├── __init__.py
└── ftp_server/ # ftp服务端程序
├── conf/ # 配置文件目录
│   ├── __init__.py
│   └── settings.py
├── database/ # 用户数据库
│   ├── hkey.db
│   └── xiaofei.db
├── ftp_server.py
├── home/ # 用户家目录
│   ├── hkey/
│   └── xiaofei/
├── __init__.py
├── log/
└── modules/ # 程序核心功能目录
├── auth_user.py
├── __init__.py
└── socket_server.py # 功能实现: 1. 初始化配置在conf下的settings.py 文件里声明,第一次运行创建用户家目录(home/)和数据文件(database/)
2. 每个用户的磁盘配额为10M, 在conf/settings.py 中声明, 可以修改
3. 本程序适用于windows,命令:cd / mkdir / pwd / dir / put / get
4. 实现了get下载续传的功能:
服务器存在文件, 客户端不存在,直接下载;
服务器存在文件, 客户端也存在文件,比较大小, 一致则不传,不一致则追加续传; # 状态码:
400 用户认证失败
401 命令不正确
402 文件不存在
403 创建文件已经存在
404 磁盘空间不够
405 不续传 200 用户认证成功
201 命令可以执行
202 磁盘空间够用
203 文件具有一致性
205 续传
# 作者介绍:
author: hkey # 博客地址:
http://www.cnblogs.com/hukey/p/8909046.html # 功能实现: 作业:开发一个支持多用户在线的FTP程序 要求: 用户加密认证
允许同时多用户登录
每个用户有自己的家目录 ,且只能访问自己的家目录
对用户进行磁盘配额,每个用户的可用空间不同
允许用户在ftp server上随意切换目录
允许用户查看当前目录下文件
允许上传和下载文件,保证文件一致性
文件传输过程中显示进度条
附加功能:支持文件的断点续传 # 目录结构: FTP/
├── ftp_client/ # ftp客户端程序
│   ├── ftp_client.py # 客户端主程序
│   ├── __init__.py
└── ftp_server/ # ftp服务端程序
├── conf/ # 配置文件目录
│   ├── __init__.py
│   └── settings.py
├── database/ # 用户数据库
│   ├── hkey.db
│   └── xiaofei.db
├── ftp_server.py
├── home/ # 用户家目录
│   ├── hkey/
│   └── xiaofei/
├── __init__.py
├── log/
└── modules/ # 程序核心功能目录
├── auth_user.py
├── __init__.py
└── socket_server.py # 功能实现: 1. 初始化配置在conf下的settings.py 文件里声明,第一次运行创建用户家目录(home/)和数据文件(database/)
2. 每个用户的磁盘配额为10M, 在conf/settings.py 中声明, 可以修改
3. 本程序适用于windows,命令:cd / mkdir / pwd / dir / put / get
4. 实现了get下载续传的功能:
服务器存在文件, 客户端不存在,直接下载;
服务器存在文件, 客户端也存在文件,比较大小, 一致则不传,不一致则追加续传; # 状态码:
400 用户认证失败
401 命令不正确
402 文件不存在
403 创建文件已经存在
404 磁盘空间不够
405 不续传 200 用户认证成功
201 命令可以执行
202 磁盘空间够用
203 文件具有一致性
205 续传 000 系统交互码

README

2、程序的结构

3、ftp客户端程序

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
import os, json, sys class Myclient(object):
def __init__(self, ip_port):
self.client = socket.socket()
self.ip_port = ip_port def connect(self):
self.client.connect(self.ip_port) def start(self):
self.connect()
while True:
username = input('输入用户名:').strip()
password = input('输入密码:').strip()
login_info = '%s:%s' % (username, password)
self.client.sendall(login_info.encode())
status_code = self.client.recv(1024).decode()
if status_code == '':
print('[%s] 用户密码错误!' % status_code)
elif status_code == '':
print('[%s] 登录成功!' % status_code)
self.interactive() def interactive(self):
while True:
command = input('-->').strip()
if not command: continue
command_str = command.split()[0]
if hasattr(self, command_str):
func = getattr(self, command_str)
func(command) def get(self, command):
self.client.sendall(command.encode())
status_code = self.client.recv(1024).decode()
if status_code == '':
filename = command.split()[1]
print(filename)
if os.path.isfile(filename):
self.client.sendall(''.encode())
revice_size = os.stat(filename).st_size
response = self.client.recv(1024)
self.client.sendall(str(revice_size).encode())
status_code = self.client.recv(1024).decode()
print('-----------------')
if status_code == '':
print('[%s] 续传' % status_code)
self.client.sendall(''.encode())
elif status_code == '':
print('[%s] 文件一致。' % status_code)
return
else:
self.client.sendall(''.encode())
revice_size = 0 file_size = self.client.recv(1024).decode()
file_size = int(file_size)
response = self.client.sendall(''.encode())
with open(filename, 'ab') as file:
while revice_size != file_size:
data = self.client.recv(1024)
revice_size += len(data)
file.write(data)
self.__progress(revice_size, file_size, '下载中')
else:
print('[%s] Error!' % status_code) def put(self, command):
if len(command.split()) > 1:
filename = command.split()[1]
if os.path.isfile(filename):
self.client.sendall(command.encode())
response = self.client.recv(1024)
file_size = os.stat(filename).st_size
self.client.sendall(str(file_size).encode())
status_code = self.client.recv(1024).decode()
if status_code == '':
with open(filename, 'rb') as file:
for line in file:
send_size = file.tell()
self.client.sendall(line)
self.__progress(send_size, file_size, '上传中') elif status_code == '':
print('[%s] Error!' % status_code) else:
print('[401] Error!') def dir(self, command):
self.__universal_method_data(command) def pwd(self, command):
self.__universal_method_data(command) def mkdir(self, command):
self.__universal_method_none(command) def cd(self, command):
self.__universal_method_none(command) def __universal_method_none(self, command):
self.client.sendall(command.encode())
status_code = self.client.recv(1024).decode()
if status_code == '':
self.client.sendall(''.encode()) else:
print('[%s] Error!' % status_code) def __universal_method_data(self, command):
self.client.sendall(command.encode())
status_code = self.client.recv(1024).decode()
print(status_code)
if status_code == '':
self.client.sendall(''.encode())
result = self.client.recv(1024).decode()
print(result)
else:
print('[%s] Error!' % status_code) def __progress(self, trans_size, file_size, mode):
bar_length = 100
percent = float(trans_size) / float(file_size)
hashes = '=' * int(percent * bar_length)
spaces = ' ' * int(bar_length - len(hashes))
sys.stdout.write('\r%s:%.2fM/%.2fM %d%% [%s]' \
% (mode, trans_size/1048576, file_size/1048576, percent*100, hashes+spaces)) if __name__ == '__main__':
client = Myclient(('localhost', 8000))
client.start()

ftp_client.py

4、ftp服务端启动程序

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import os, json
from conf import settings
from modules import socket_server
def create_db():
user_database = {}
limit_size = settings.LIMIT_SIZE
for k, v in settings.USER_INFO.items():
username = k
password = v
user_home_path = settings.HOME_DIR + r'\%s' % username
user_db_path = settings.DATABASE_DIR + r'\%s.db' % username
user_database['username'] = username
user_database['password'] = password
user_database['limitsize'] = limit_size
user_database['homepath'] = user_home_path
if not os.path.isfile(user_db_path):
with open(user_db_path, 'w') as file:
file.write(json.dumps(user_database)) def create_home():
for username in settings.USER_INFO:
user_home_path = settings.HOME_DIR + r'\%s' % username
if not os.path.isdir(user_home_path):
os.popen('mkdir %s' % user_home_path) if __name__ == '__main__':
create_db()
create_home()
server = socket_server.socketserver.ThreadingTCPServer(settings.IP_PORT, socket_server.Myserver)
server.serve_forever()

ftp_server.py

5、conf配置文件

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import os, sys # 程序主目录
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, BASE_DIR) # 数据库目录
DATABASE_DIR = os.path.join(BASE_DIR, 'database') # 用户家目录
HOME_DIR = os.path.join(BASE_DIR, 'home') # 用户配额
LIMIT_SIZE = 10240000 # 用户信息
USER_INFO = {'hkey': '', 'xiaofei': 'abc'} # ip and port
IP_PORT = ('localhost', 8000)

settings.py

6、database 用户数据库(初始化生成,生成程序 - conf/settings.py)

{"homepath": "E:\\py_code\\FTP\\ftp_server\\home\\hkey", "username": "hkey", "limitsize": 10240000, "password": ""}

hkey.db

{"homepath": "E:\\py_code\\FTP\\ftp_server\\home\\xiaofei", "username": "xiaofei", "limitsize": 10240000, "password": "abc"}

xiaofei.db

7、modules 核心功能模块

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import json, os
from conf import settings
class User_operation(object):
def authentication(self, login_info):
username, password = login_info.split(':')
DB_FILE = settings.DATABASE_DIR + r'\%s' % username
if os.path.isfile(DB_FILE):
user_database = self.cat_database(DB_FILE)
if username == user_database['username'] and \
password == user_database['password']:
return user_database def cat_database(self, DB_FILE):
with open(DB_FILE, 'r') as file:
data = json.loads(file.read())
return data

auth_user.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socketserver
import os, json
from os.path import getsize, join
from modules import auth_user
from conf import settings
class Myserver(socketserver.BaseRequestHandler):
def handle(self):
try:
while True:
login_info = self.request.recv(1024).decode()
print(login_info)
result = self.authenticat(login_info)
status_code = result[0]
self.request.sendall(status_code.encode())
if status_code == '':
continue
self.user_db = result[1]
self.user_current_path = self.user_db['homepath']
self.user_home_path = self.user_db['homepath']
while True:
command = self.request.recv(1024).decode()
command_str = command.split()[0]
if hasattr(self, command_str):
func = getattr(self, command_str)
func(command)
else:
self.request.sendall(''.encode()) except ConnectionResetError as e:
self.request.close()
print('Error:', e) def authenticat(self, login_info):
auth = auth_user.User_operation()
result = auth.authentication(login_info)
if result:
return '', result
else:
return '', result def get(self, command):
print('func: get()')
if len(command.split()) > 1:
filename = command.split()[1]
user_file_path = self.user_current_path + r'\%s' % filename
if os.path.isfile(user_file_path):
print(user_file_path)
self.request.sendall(''.encode())
file_size = os.stat(user_file_path).st_size
status_code = self.request.recv(1024).decode()
if status_code == '':
self.request.sendall(''.encode())
has_send_data = int(self.request.recv(1024).decode())
if has_send_data < file_size:
self.request.sendall(''.encode())
response = self.request.recv(1024).decode()
else:
self.request.sendall(''.encode())
return else:
has_send_data = 0 self.request.sendall(str(file_size).encode())
response = self.request.recv(1024)
with open(user_file_path, 'rb') as file:
file.seek(has_send_data)
self.request.sendall(file.read()) else:
self.request.sendall(''.encode())
else:
self.request.sendall(''.encode()) def put(self, command):
filename = command.split()[1]
file_path = self.user_current_path + r'\%s' % filename
self.request.sendall(''.encode())
file_size = self.request.recv(1024).decode()
file_size = int(file_size)
limit_size = self.user_db['limitsize']
used_size = self.__getdirsize(self.user_home_path)
if limit_size >= file_size + used_size:
self.request.sendall(''.encode())
revice_size = 0
with open(file_path, 'wb') as file:
while revice_size != file_size:
data = self.request.recv(1024)
revice_size += len(data)
file.write(data) else:
self.request.sendall(''.encode()) def dir(self, command):
if len(command.split()) == 1:
self.request.sendall(''.encode())
response = self.request.recv(1024)
send_data = os.popen('dir %s' % self.user_current_path)
self.request.sendall(send_data.read().encode())
else:
self.request.sendall(''.encode()) def pwd(self, command):
if len(command.split()) == 1:
self.request.sendall(''.encode())
response = self.request.recv(1024)
self.request.sendall(self.user_current_path.encode())
else:
self.request.sendall(''.encode()) def mkdir(self, command):
if len(command.split()) > 1:
dir_name = command.split()[1]
dir_path = self.user_current_path + r'\%s' % dir_name
if not os.path.isdir(dir_path):
self.request.sendall(''.encode())
response = self.request.recv(1024)
os.popen('mkdir %s' % dir_path)
else:
self.request.sendall(''.encode())
else:
self.request.sendall(''.encode()) def cd(self, command):
print(command)
if len(command.split()) > 1:
dir_name = command.split()[1]
dir_path = self.user_current_path + r'\%s' % dir_name
user_home_path = settings.HOME_DIR + r'\%s' % self.user_db['username']
if dir_name == '..' and len(self.user_current_path) > len(user_home_path):
self.request.sendall(''.encode())
response = self.request.recv(1024)
self.user_current_path = os.path.dirname(self.user_current_path)
elif os.path.isdir(dir_path):
self.request.sendall(''.encode())
response = self.request.recv(1024)
if dir_name != '.' and dir_name != '..':
self.user_current_path += r'\%s' % dir_name
else:
self.request.sendall(''.encode())
else:
self.request.sendall(''.encode()) def __getdirsize(self, home_path):
size = 0
for root, dirs, files in os.walk(home_path):
size += sum([getsize(join(root, name)) for name in files])
return size

socket_server.py

程序运行效果图:

get 续传功能

[ python ] 项目一:FTP程序的更多相关文章

  1. 用python开发简单ftp程序

    根据alex老师视频开发的简单ftp程序,只能实现简单的get功能 ftp客户端程序: #!/usr/bin/env python #_*_ coding:utf-8 _*_ import socke ...

  2. python简单的ftp程序

    服务器端 '''1.读取文件名2.检测文件是否存在3.打开文件4.检测文件大小5.发送文件大小给客户端6.等客户端确认7.开始边读边发数据8.发送md5'''import socket,os,time ...

  3. Python开发【项目】:FTP程序

    作业:开发一个支持多用户在线的FTP程序 要求: 用户加密认证 允许同时多用户登录 每个用户有自己的家目录 ,且只能访问自己的家目录 对用户进行磁盘配额,每个用户的可用空间不同 允许用户在ftp se ...

  4. Python开发程序:FTP程序

    作业:开发一个支持多用户在线的FTP程序 要求: 用户加密认证 允许同时多用户登录 每个用户有自己的家目录 ,且只能访问自己的家目录 对用户进行磁盘配额,每个用户的可用空间不同 允许用户在ftp se ...

  5. python实现FTP程序

    python实现FTP程序 程序源码 上传功能 查看文件 cd功能 创建目录 程序源码 目录结构 服务端 主程序 import optparse import socketserver import ...

  6. (转)Python开发程序:支持多用户在线的FTP程序

    原文链接:http://www.itnose.net/detail/6642756.html 作业:开发一个支持多用户在线的FTP程序 要求: 用户加密认证 允许同时多用户登录 每个用户有自己的家目录 ...

  7. Jenkins构建Python项目提示:'python' 不是内部或外部命令,也不是可运行的程序

    问题描述: jenkin集成python项目,立即构建后,发现未执行成功,查看Console Output 提示:'Python' 不是内部或外部命令,也不是可运行的程序,如下图: 1.在 Windo ...

  8. 【Jenkins】jenkins构建python项目提示:'python' 不是内部或外部命令,也不是可运行的程序或批处理文件

    一.问题:jenkins构建python项目提示:'python' 不是内部或外部命令,也不是可运行的程序或批处理文件 二.原因:要在jenkins配置本地环境变量 三.解决方案:添加python.e ...

  9. 老男孩python作业7-开发一个支持多用户在线的FTP程序

    作业6:开发一个支持多用户在线的FTP程序 要求: 用户加密认证 允许同时多用户登录 每个用户有自己的家目录 ,且只能访问自己的家目录 对用户进行磁盘配额,每个用户的可用空间不同 允许用户在ftp s ...

随机推荐

  1. 【刷题】BZOJ 3140 [Hnoi2013]消毒

    Description 最近在生物实验室工作的小T遇到了大麻烦. 由于实验室最近升级的缘故,他的分格实验皿是一个长方体,其尺寸为abc,a.b.c 均为正整数.为了实验的方便,它被划分为abc个单位立 ...

  2. 洛谷4525 & 4526:【模板】自适应辛普森法——题解

    参考:https://phqghume.github.io/2018/05/19/%E8%87%AA%E9%80%82%E5%BA%94%E8%BE%9B%E6%99%AE%E6%A3%AE%E6%B ...

  3. bzoj1025: [SCOI2009]游戏(DP)

    题目大意:将长度为n的排列作为1,2,3,...,n的置换,有可能置换x次之后,序列又回到了1,2,3,...,n,求所有可能的x的个数. 看见这种一脸懵逼的题第一要务当然是简化题意...我们可以发现 ...

  4. HDU 2094 拓扑排序

    产生冠军 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submi ...

  5. 学习tcpIp必备的抓包工具wireshark

    wireshark是一个优秀的抓包工具 ip.src=192.168.10.123  发送http的一端 ip.dst=192.168.10.126 接收http的一端 如下图所示:

  6. syslog大小限制

    位置 /etc/logrotate.d/rsyslog 相关配置信息察看man logrotate size k/M/G /var/log/syslog { rotate daily missingo ...

  7. css 实现元素水平垂直居中总结5中方法

    个人总结,如有错误请指出,有好的建议请留言.o(^▽^)o 一.margin:0 auto:text-align:center:line-height方法 <div id="divAu ...

  8. Windows/Linux javac/java编译运行引入所需的jar包

    > Windows 假设要引用的jar放在D:/test目录下,名字为t1.jar, java源文件放在D:/test/src目录下,名字为t2.java. 编译: javac  -cp  d: ...

  9. Creating a new dynamic form project, business modeling.

    The domain logic is like there are a bunch of objects, as well as a lot of configurations, according ...

  10. 大聊Python-----网络编程

    什么是Socket? socket本质上就是在2台网络互通的电脑之间,架设一个通道,两台电脑通过这个通道来实现数据的互相传递. 我们知道网络 通信 都 是基于 ip+port 方能定位到目标的具体机器 ...