zl程序教程

您现在的位置是:首页 >  工具

当前栏目

FTP

ftp
2023-09-14 09:01:25 时间

python网络编程阶段基础项目

需求分析

请基于TCP协议实现一个网盘系统,包含客户端、服务端,各自需求如下:
- 客户端
  - 用户注册,注册成功之后,在服务端的指定目录下为此用户创建一个文件夹,该文件夹下以后存储当前用户的数据(类似于网盘)。
  - 用户登录
  - 查看网盘目录下的所有文件(一级即可),ls命令
  - 上传文件,如果网盘已存在则重新上传(覆盖)。
  - 下载文件(进度条)
      先判断要下载本地路径中是否存在该文件。
          - 不存在,直接下载
          - 存在,则让用户选择是否续传(继续下载)。
	          - 续传,在上次的基础上继续下载。
	          - 不续传,从头开始下载。
服务端
- 支持注册,并为用户初始化相关目录。
     注册成功之后,将所有用户信息存储到特定的Excel文件中
- 支持登录
- 支持查看当前用户网盘目录下的所有文件。
- 支持上传
- 支持下载

README

                    FTP

环境要求:python3.8
使用方法:
     - 启动client.py
     - 启动sever.py
功能:
     - 注册:适用于新手用户
     - 登录:适用于有号者
     - 上传文件和下载文件及查看文件前需要登录

代码

客户端

# 客户端
import socket
import json
import os
import time
from conf import setting

user = None # 记录用户是否登录
# 注册功能
def register(client_conn):
    name = input('your name:').strip()
    pwd = input('your password:').strip()
    client_conn.sendall(f'{name}-{pwd}'.encode('utf-8'))
    res = json.loads(client_conn.recv(1024).decode('utf-8'))
    return res

# 登录功能
def login(client_conn):
    global user
    name = input('your name:').strip()
    pwd = input('your password:').strip()
    client_conn.sendall(f'{name}-{pwd}'.encode('utf-8'))
    res = json.loads(client_conn.recv(1024).decode('utf-8'))
    if res[0] == True:
        user = name
        return res[1]
    return res[1]


# 上传文件
def upload(client_conn):
    global user
    if not user:
        print('请先登录!')
        client_conn.sendall('要先登录'.encode('utf-8'))
        res = login(client_conn)
        return res
    else:
        file_path = input('请输入要上传的文件路径:').strip()
        if not os.path.exists(file_path):
            print('文件不存在!')
            return
        file_name = input('请输入要保存的文件名:').strip()
        client_conn.sendall(file_name.encode('utf-8'))
        time.sleep(1)
        content_size = os.stat(file_path).st_size
        client_conn.sendall(json.dumps([user, content_size]).encode('utf-8'))
        with open(file_path, mode='rb') as f:
            send_size = 0
            while True:
                cotent = f.read(1024)
                client_conn.sendall(cotent)
                send_size += len(cotent)
                if send_size >= content_size:
                    break
            return client_conn.recv(1024).decode('utf-8')


# 下载文件
def download(client_conn):
    global user
    if not user:
        print('请先登录!')
        client_conn.sendall('要先登录'.encode('utf-8'))
        res = login(client_conn)
        return res
    # 发送暗语表示不需要登录
    client_conn.sendall('****'.encode('utf-8'))
    file_name = input('请输入你要下载的文件名:').strip()
    if not os.path.exists(os.path.join(setting.BASE_PATH, '本地下载')):
        os.makedirs(os.path.join(setting.BASE_PATH, '本地下载'))

    file_path = os.path.join(setting.BASE_PATH, '本地下载', file_name)
    client_conn.sendall(json.dumps([file_name, user]).encode('utf-8'))
    res = client_conn.recv(1024).decode('utf-8')
    print(res)

    if res == '没有该文件':
        print('网盘内没有该文件!')
        return
    file_size = int(client_conn.recv(1024).decode('utf-8'))
    if not os.path.exists(file_path):
        client_conn.sendall('1'.encode('utf-8'))
        with open(file_path, mode='wb') as f:
            size = 0
            while True:
                data = client_conn.recv(1024)
                f.write(data)
                f.flush()
                size += len(data)
                print('\r{}:{}%'.format(100 * int(size / file_size) * '>', 100 * int(size / file_size)),end='')
                if size >= file_size:
                    break
            return client_conn.recv(1024).decode('utf-8')
    # 断点续传
    else:
        client_conn.sendall('2'.encode('utf-8'))
        time.sleep(0.5)
        exist_size = os.stat(file_path).st_size
        client_conn.sendall(str(exist_size).encode('utf-8'))
        with open(file_path, mode='ab') as f:
            while True:
                data = client_conn.recv(1024)
                f.write(data)
                exist_size += len(data)
                print('\r{}:{}%'.format(100 * int(exist_size / file_size)*'>', 100 * int(exist_size / file_size)),end='')
                if exist_size == file_size:
                    break
            return client_conn.recv(1024).decode('utf-8')




# 查看文件
def check(client_conn):
    if not user:
        print('请先登录!')
        client_conn.sendall('要先登录'.encode('utf-8'))
        login(client_conn)
    else:
        client_conn.sendall(user.encode('utf-8'))
        file_res = json.loads(client_conn.recv(1024).decode('utf-8'))
        for file in file_res:
            print(file)


# 功能字典
client_dic = {
    '0': ['注册功能', register],
    '1': ['登录功能', login],
    '2': ['上传文件', upload],
    '3': ['下载文件', download],
    'ls':['查看网盘下文件', check],
}



if __name__ == '__main__':
    print('欢迎来到网盘系统'.center(50, '*'))
    # 建立链接
    while True:
        print('正在尝试建立链接...')
        try:
            client_conn = socket.socket()
            client_conn.connect(('127.0.0.1', 8080))
            print('连接成功!')
            break
        except Exception as e:
            print('连接失败...')
    # 展示可选功能
    while True:
        for k, v in client_dic.items():
            print(f'{v[0]}:{k}')

        choice = input('请输入你的选择(输入q退出系统):').strip()

        if choice.upper() == 'Q':
            print('退出系统!')
            break

        # 判断用户输入的合法性
        if choice not in client_dic or len(choice)==0:
            print('请重新输入!')
            continue

        client_conn.sendall(choice.encode('utf-8'))
        if choice == '0':
            res = register(client_conn)
            print(res[1])
        elif choice == '1':
            res = login(client_conn)
            print(res)
        elif choice == '2':
            res = upload(client_conn)
            print(res)
        elif choice == '3':
            res = download(client_conn)
            print(res)
        else:
            check(client_conn)


服务端

# 服务端功能
import socket
import os
import json
import time
from conf import setting
from db import db_handler


# 注册功能
def register(conn):
    res = conn.recv(1024).decode('utf-8')
    print(res)
    name, pwd = res.split('-')  # 'name-pwd'
    dir_path = os.path.join(setting.BASE_PATH, '用户', f'{name}')
    # 判断用户文件夹是否存在
    if not os.path.exists(dir_path):
        os.makedirs(dir_path)

        # 将用户注册信息写入文件
        file_path = os.path.join(dir_path, 'info.xlsx')
        print(file_path)
        db_handler.save(name,pwd,file_path)
        conn.sendall(json.dumps([True, '注册成功!']).encode('utf-8'))
        return

    # 用户文件夹存在则表示用户已经注册过
    conn.sendall(json.dumps([False, '用户已存在!']).encode('utf-8'))


# 登录功能
def login(conn):
    res = conn.recv(1024).decode('utf-8')
    print(res)
    name, pwd = res.split('-')  # 'name-pwd'
    dir_path = os.path.join(setting.BASE_PATH, '用户', f'{name}')
    # 判断用户文件夹是否存在
    if not os.path.exists(dir_path):
        conn.sendall(json.dumps(['False', '登陆失败,该用户不存在']).encode('utf-8'))
        return '该用户不存在!'
    file_path = os.path.join(dir_path, 'info.xlsx')
    # 获取用户信息

    fname, fpwd = db_handler.check_info(file_path)
    if fpwd == pwd:
        conn.sendall(json.dumps([True, '登录成功!']).encode('utf-8'))
        return True
    else:
        conn.sendall(json.dumps([False, '登录失败,密码错误!']).encode('utf-8'))
        return False

# 上传文件
def upload(conn):
    file_name = conn.recv(1024).decode('utf-8')
    if file_name == '要先登录':
        res = login(conn)
        return res
    time.sleep(1)
    name_size = json.loads(conn.recv(1024).decode('utf-8'))
    name, size = name_size[0], name_size[1]
    print(name,size)
    file_path = os.path.join(setting.BASE_PATH, '用户', name, file_name)
    with open(file_path, mode='wb') as f:
        recv_size = 0
        while True:
            data = conn.recv(1024)
            f.write(data)
            recv_size += len(data)

            if recv_size >= size:
                conn.sendall('ok!'.encode('utf-8'))
                return

# 下载文件
def download(conn):
    file_name = conn.recv(1024).decode('utf-8')
    if file_name == '要先登录':
        res = login(conn)
        return res
    # 接收文件名、用户名
    file_name, user = json.loads(conn.recv(1024).decode('utf-8'))
    print(file_name,user)
    file_path = os.path.join(setting.BASE_PATH, '用户', user, file_name)

    # 判断服务器是否有该文件
    if not os.path.exists(file_path):
        conn.sendall('没有该文件'.encode('utf-8'))
        return
    # 发送暗语表示有文件
    conn.sendall('*****'.encode('utf-8'))
    # 发送文件大小
    content_size = os.stat(file_path).st_size
    conn.sendall(str(content_size).encode('utf-8'))
    send_size = 0
    # 区别客户端情况作出不同应答
    if conn.recv(1024).decode('utf-8') == '1':
        with open(file_path, mode='rb') as f:

            while True:
                content = f.read(1024)
                conn.sendall(content)
                send_size += len(content)
                if send_size >= content_size:
                    conn.sendall('OK!'.encode('utf-8'))
                    break
    else:
        exist_size = int(conn.recv(1024).decode('utf-8'))
        print(exist_size)
        with open(file_path, mode='rb') as f:
            # 断点续传
            f.seek(exist_size)
            while True:
                data = f.read(1)
                conn.sendall(data)
                send_size += len(data)
                if send_size >= content_size - exist_size:
                    conn.sendall('ok!'.encode('utf-8'))
                    return

# 查看文件
def check(conn):
    name = conn.recv(1024).decode('utf-8')
    print(name)
    if name == '要先登录':
        login(conn)
    else:
        file_path = os.path.join(setting.BASE_PATH, '用户', name)
        print(file_path)
        file_list = os.listdir(file_path)
        conn.sendall(json.dumps(file_list).encode('utf-8'))


if __name__ == '__main__':
    # 建立TCP链接
    client = socket.socket()
    client.bind(('127.0.0.1', 8080))
    client.listen(5)
    conn, addr = client.accept()

    while True:
        # 接受客户端的命令
        choice = conn.recv(1024).decode('utf-8')
        print(choice)
        if choice == '0':
            register(conn)
        elif choice == '1':
            login(conn)
        elif choice == '2':
            upload(conn)
        elif choice == '3':
            download(conn)
        else:
            check(conn)

注意:仅展示了部分代码,需要私