[ python ] 项目一:FTP程序

声明:

  该项目参考学习地址:

    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、程序的结构
[ python ] 项目一:FTP程序

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

程序运行效果图:

[ python ] 项目一:FTP程序

get 续传功能
[ python ] 项目一:FTP程序

上一篇:Docker for windows WIN版本,主板特性问题


下一篇:linux复制文件命令scp