嘘~ 正在从服务器偷取页面 . . .

多线程FTP项目(4)—— Mysql数据库 + FTP


项目开发目录

FTPClient

bin文件夹 Client.py

# -*- coding=gbk -*-
# @author   : aoteman
# @time     : 2022/7/10 10:36

import socket, os, sys

PATH = os.path.dirname(os.path.dirname(__file__))
sys.path.append(PATH)

class FTPclient():
    def __init__(self):
        from optparse import OptionParser

        opt = OptionParser()
        opt.add_option("-H", dest="HOST", help="FTP Server HOST")
        opt.add_option("-P", dest="PORT", type="int", help="FTP Serve PORT")

        values, args = opt.parse_args()

        # print(values, args)

        self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 实例化 socket
        self.connect(values)

        # 登录的用户名
        self.user = None
        # 展示给用户的信息头
        self.show = None

    def connect(self, values):
        HOST = values.HOST
        PORT = values.PORT

        # print(host, port)

        if HOST and PORT:  # 输入的 HOST 和 PORT 都不能为空
            self.client.connect((HOST, PORT))
            print("connect succeed")
            self.login()  # 连接完成后,进行用户登录
        else:
            exit("ERROR: should supply HOST and PORT !")  # 退出并打印提示

    def login(self):
        count = 3
        while count > 0:
            username = input("please enter your username>>: ").strip()
            password = input("enter your password>>: ").strip()

            sql = "select password from user where username = %s"
            from core import connect_database
            cursor = connect_database.connect()

            rows = cursor.execute(sql, (username))

            if rows == 0: # 输入的用户名不存在
                count -= 1
                print("用户名错误,请重新输入,您还有{}次机会重新输入".format(count))
            elif rows != 0: # 密码错误
                res = cursor.fetchall()[0].get("password")
                if res != password: # 密码错误
                    count -= 1
                    print("密码错误,请重新输入,您还有{}次机会重新输入".format(count))
                else: # 登录成功
                    print("登录成功")
                    self.user = username

                    from log import log
                    log.log(self.user, "login", self.user)

                    msg_dic = self.make_dict(action="log", username=username)
                    self.send_msg(msg_dic)

                    msg_dic = self.recv_msg()
                    self.show = msg_dic.get("show")

                    self.handle()

    def make_dict(self, action,**kwargs)-> dict:
        msg_dic = {
            "action": action
        }
        msg_dic.update(**kwargs)

        return msg_dic

    def send_msg(self, msg_dic)->None:
        import struct,json
        str_msg = json.dumps(msg_dic)

        length = struct.pack("q", len(str_msg))

        self.client.send(length)

        self.client.send(str_msg.encode("utf-8"))

    def recv_msg(self)->dict:
        length_dic = self.client.recv(8)

        import struct,json
        str_dic_length = struct.unpack("q", length_dic)[0]

        str_dic = self.client.recv(str_dic_length)

        dic = json.loads(str_dic)

        return dic

    def handle(self):
        """和服务端交互"""
        while True:
            cmd = input("{}>>: ".format(self.show)).strip()
            cmd_list = cmd.split(" ")

            if len(cmd_list) > 1:
                action = cmd_list[0]
                content = cmd_list[1]
            else:
                action = cmd_list[0]
                content = ""

            if hasattr(self, action): # 有该方法
                func = getattr(self, action)
                func(content)

    def md(self, content):
        """在服务端家目录"""
        msg_dic = self.make_dict(action="md", content=content)
        self.send_msg(msg_dic=msg_dic)

        recv_dic = self.recv_msg()

        print(recv_dic.get("stdout"), recv_dic.get("stderr"))

        from log import log
        log.log(self.user, "md", content)

    def dir(self, content):
        """查看服务端用户当前路径"""
        msg_dic = self.make_dict(action="dir", content=content)
        self.send_msg(msg_dic=msg_dic)

        recv_dic = self.recv_msg()

        print(recv_dic.get("stdout"), recv_dic.get("stderr"))

        from log import log
        log.log(self.user, "dir", content)

    def cd(self, content):
        """切换用户在服务端目录"""
        msg_dic = self.make_dict(action="cd", content=content)
        self.send_msg(msg_dic=msg_dic)

        recv_dic = self.recv_msg()

        # print(recv_dic.get("stdout"), recv_dic.get("stderr"))

        if recv_dic.get("status_code") == 100:
            self.show = recv_dic.get("show")
            from log import log
            log.log(self.user, "cd", content)
        else:
            print(recv_dic.get("status_msg"))

    def download(self, content):
        """从服务端下载文件"""
        msg_dic= self.make_dict(action="download", file_name=content)
        self.send_msg(msg_dic=msg_dic)

        recv_dic = self.recv_msg()

        if recv_dic.get("status_code") == 102:
            file_path = recv_dic.get("file_path")
            file_size = recv_dic.get("file_size")

            import time
            from config import setting
            now = str(time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(time.time())))
            download_file_path = setting.DOWNLOAD_PATH + "\\" + content + "." + now + ".download"

            with open(download_file_path, "wb") as f:
                sql = "insert into UNDOWNLOAD_FILE(username, file_name, file_path, file_size) values(%s, %s, %s, %s)"
                from core import connect_database
                cursor = connect_database.connect()

                cursor.execute(sql, (self.user, content + "." + now + ".download", file_path, file_size))

                recv_size = 0

                while recv_size < file_size:
                    recv_content = self.client.recv(1024)
                    f.write(recv_content)
                    recv_size += len(recv_content)
                    print("下载进度:{}{}%".format("#" * int(int(int(recv_size) / int(file_size)) * 100 / 2), int(int(recv_size) / int(file_size)) * 100), flush=True, end="\r")

                print("\n下载完成!!!")

            sql = "delete from UNDOWNLOAD_FILE where username = %s and file_name = %s"
            cursor.execute(sql, (self.user, content + "." + now + ".download"))
            # print(content + "." + now + ".download")

            if not os.path.exists(setting.DOWNLOAD_PATH + "\\" + content):
                os.rename(download_file_path, setting.DOWNLOAD_PATH + "\\" + content)
            else:
                download_file_path_ = download_file_path.replace(".download", "")
                os.rename(download_file_path, download_file_path_)

            from log import log
            log.log(self.user, "download", content)

        else:
            print(recv_dic.get("status_msg"))

    def download_bar(self, recv_size, file_size):
        # recv_size = recv_size
        recv_size = int(recv_size)
        file_size = int(file_size)

        if recv_size < file_size:
            print("下载进度:{}{}%".format("#" * int(int(int(recv_size) / int(file_size)) * 100 / 2), int(int(recv_size) / int(file_size)) * 100), flush=True, end="\r")

    def re_get(self, content):
        """断点续存"""
        while True:
            from core import connect_database
            cursor = connect_database.connect()
            sql = "select id,file_name from undownload_file where username = %s"
            cursor.execute(sql, (self.user))
            res = cursor.fetchall()

            tips = """--------------------------
            id            file_name"""
            print(tips)

            for i in res:
                id = i.get("id")
                file_name = i.get("file_name")

                tips = """--------------------------
            {}            {}""".format(id, file_name)

                print(tips)

            input_id = input("please enter the id to download file(enter 'exit' to exit):>> ").strip()
            if input_id == "exit":
                break
            elif input_id.isdigit(): # 如果是数字
                input_id = int(input_id)

                sql = "select file_name,file_path,file_size from undownload_file where id = %d" % input_id
                cursor.execute(sql)
                res = cursor.fetchall()[0]

                file_name = res.get("file_name")
                file_path = res.get("file_path")
                file_size = res.get("file_size")

                from config import setting

                if os.path.exists(setting.DOWNLOAD_PATH + "\\" + file_name): # 如果未下载完成的文件未被删除
                    recv_size = os.path.getsize(setting.DOWNLOAD_PATH + "\\" + file_name)

                    msg_dic = self.make_dict(action="re_get", file_name=file_name, file_path=file_path, file_size=file_size, recv_size=recv_size)
                    self.send_msg(msg_dic=msg_dic)

                    recv_dic = self.recv_msg()
                    if recv_dic.get("status_code") == 102:
                        with open(setting.DOWNLOAD_PATH + "\\" + file_name, "wb") as f:

                            while int(recv_size) < int(file_size):
                                recv_content = self.client.recv(1024)
                                f.write(recv_content)
                                recv_size += len(recv_content)
                                print("下载进度:{}{}%".format("#" * int(int(int(recv_size) / int(file_size)) * 100 / 2), int(int(recv_size) / int(file_size)) * 100), flush=True, end="\r")

                            print("\n下载完成!!!")
                        if not os.path.exists(setting.DOWNLOAD_PATH + "\\" + ".".join(file_name.split(".")[0:2])):
                            os.rename(setting.DOWNLOAD_PATH + "\\" + file_name, setting.DOWNLOAD_PATH + "\\" + ".".join(file_name.split(".")[0:2]))
                        else:
                            download_file_path_ = (setting.DOWNLOAD_PATH + "\\" + file_name).replace(".download", "")
                            os.rename((setting.DOWNLOAD_PATH + "\\" + file_name), download_file_path_)

                    else:
                        print(recv_dic.get("status_msg"))

                sql = "delete from undownload_file where id = %d" % input_id
                cursor.execute(sql)

                from log import log
                log.log(self.user, "re_get", file_name)
            else:
                print("enter wrong")
                continue

client = FTPclient()

config 文件夹 setting.py 文件

import os

DOWNLOAD_PATH = os.path.dirname(os.path.dirname(__file__)) + "\\" + "download"

core 文件夹 connect_database.py 文件

# -*- coding=gbk -*-
# @author   : aoteman
# @time     : 2022/7/11 10:09

def connect():
    import pymysql
    conn = pymysql.connect(
        host="localhost",
        port=3306,
        database="FTP",
        user="root",
        password="",
        charset='utf8',
        autocommit=True
    )

    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)

    return cursor

log文件夹 log.py 文件

# -*- coding=gbk -*-
# @author   : aoteman
# @time     : 2022/7/11 10:07

def log(username, action, msg):
    from core import connect_database
    cursor = connect_database.connect()

    sql = "insert into user_log(username, action, msg) values(%s, %s, %s)"

    cursor.execute(sql, (username, action, msg))

if __name__ == '__main__':
    import pymysql

    conn = pymysql.connect(
        host="localhost",
        port=3306,
        database="FTP",
        user="root",
        password="",
        charset='utf8',
        autocommit=True
    )

    cursor = conn.cursor()

    import xlwt
    book = xlwt.Workbook(encoding="utf-8", style_compression=0)

    sheet = book.add_sheet("日志", cell_overwrite_ok=True)

    col = ["用户名", "进行的操作", "操作的信息", "操作时间"]
    for i in range(0, len(col)):
        sheet.write(0, i, col[i])

    sql = 'select username,action,msg,time from user_log'
    cursor.execute(sql)
    res = cursor.fetchall()

    import datetime
    for i in range(0, len(res)):
        for j in range(0, 4):
            sheet.write(i+1, j, res[i][j])
            if j == 3:
                sheet.write(i+1, j, res[i][j].strftime('%Y-%m-%d %H:%M:%S'))

    book.save("log.xls")

FTPServer

bin文件夹 main.py文件

import sys,os

path = os.path.dirname(os.path.dirname(__file__)) # FTPServer 所在文件夹路径

if __name__ == '__main__':
    sys.path.append(path)  # 添加到环境变量

    from lib import management
    server = management.Management(sys.argv)

config 文件夹 setting.py 文件

HOST = "127.0.0.1"
PORT = 8080

import os
USER_HOME = os.path.dirname(os.path.dirname(__file__)) + "\\home"
# print(USER_HOME)

core 文件夹 connect_database 文件

def connect():
    import pymysql
    conn = pymysql.connect(
        host = "localhost",
        port = 3306,
        database = "FTP",
        user = "root",
        password = "",
        charset='utf8',
        autocommit=True
    )

    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)

    return cursor

core 文件夹 FTP.sql 文件

create database FTP;

use FTP;

create table USER(
    id int primary key auto_increment,
    username varchar(16),
    password varchar(16)
);

create table LOG(
    id int primary key auto_increment,
    username varchar(16),
    action varchar(16),
    msg varchar(64),
    time timestamp not null default CURRENT_TIMESTAMP
);

create table USER_LOG(
    id int primary key  auto_increment,
    username varchar(16),
    action varchar(16),
    msg varchar(64),
    time timestamp not null default CURRENT_TIMESTAMP
);

create table UNDOWNLOAD_FILE(
    id int primary key  auto_increment,
    username varchar(16),
    file_name varchar(16),
    file_path varchar(1024),
    file_size varchar(32)
);

lib文件夹 managment.py文件

import socketserver

class Management():
    def __init__(self, argv):
        cmd = argv[1]

        if hasattr(self, cmd): # 如果该命令存在
            func = getattr(self, cmd)
            func()
        else: # 否则运行提示函数
            self.tips()

    def tips(self):
        tip = """
        ----------------------------------------
                  FTP Project Command List
        ----------------------------------------
        run                  start up FTP Server
        ----------------------------------------
        create                   create FTP User
        ----------------------------------------
        delete                   delete FTP User
        ----------------------------------------
        tips                     FTP Command tip
        ----------------------------------------
        """

        exit(tip)

    def run(self):
        from lib import Server
        from config import setting

        print("{}FTPServer Run {} {} {}".format("-"*25, setting.HOST, setting.PORT, "-"*25))

        server = socketserver.ThreadingTCPServer(("127.0.0.1", 8080), Server.FTPserver)  # 开启 TCP服务 多线程
        server.serve_forever()

    def create(self):
        from core import connect_database
        cursor = connect_database.connect()

        username = input("username: ").strip("") # 要创建的用户的用户名
        password = input("password: ").strip("") # 要创建的用户的密码

        # print(username, password)

        sql = "select * from user where username = %s"
        res = cursor.execute(sql, (username))

        if res == 0: # 该用户不存在,可以创建
            sql = "insert into user(username, password) values(%s, %s)"
            cursor.execute(sql, (username, password))
            print("用户创建成功!!!")

            from log import log
            action = "login"
            log.log(username, action, username)

        else:
            print("该用户已存在!!!")

    def delete(self):
        from core import connect_database
        cursor = connect_database.connect()

        username = input("username: ").strip("")  # 要删除的用户的用户名
        sql = "select * from user where username = %s"
        rows = cursor.execute(sql, (username))

        if rows != 0: # 要删除的用户存在
            sql = "delete from user where username = %s"
            cursor.execute(sql, (username))

            print("删除用户成功!!!")

            from log import log
            action = "delete"
            log.log("", action, username)
        else:
            print("要删除的用户不存在!!!")

lib 文件夹 Server.py 文件

import socketserver,time

STATUS_MSG = {
    000: "nothing",
    100: "cd dir success!!!",
    101: "the dir not exist!!!",
    102: "file exits!!!",
    103: "file not exits!!!",
}

class FTPserver(socketserver.BaseRequestHandler):
    def handle(self) -> None: # 一定要是 handle
        # print(self.request) # 相当于 conn
        # print(self.client_address) # 相当于 addr

        print("{}连接成功".format(self.client_address))

        while True: # 通信循环
            msg_dic = self.recv_msg()

            action = msg_dic.get("action")
            if hasattr(self, "_%s" % action):
                func = getattr(self, "_%s" % action)
                func(msg_dic)

    def recv_msg(self)->dict:
        length_dic = self.request.recv(8)

        import struct,json
        str_dic_length = struct.unpack("q", length_dic)[0]

        str_dic = self.request.recv(str_dic_length)

        dic = json.loads(str_dic)

        return dic

    def make_dict(self, status_code, **kwargs)-> dict:
        msg_dic = {
            "status_code": status_code,
            "status_msg": STATUS_MSG.get(status_code)
        }
        msg_dic.update(**kwargs)

        return msg_dic

    def send_msg(self, msg_dic)->None:
        import struct,json
        str_msg = json.dumps(msg_dic)

        length = struct.pack("q", len(str_msg))

        self.request.send(length)

        self.request.send(str_msg.encode("utf-8"))

    def _log(self, msg_dic):
        self.username = msg_dic.get("username")

        from config import setting
        user_path = setting.USER_HOME + "\\" + self.username

        self.current_path = user_path # 用户此时在服务端的路径位置

        import os
        if not os.path.exists(user_path): # 该路径不存在
            os.makedirs(user_path)

        self.show = user_path.replace(setting.USER_HOME, "")

        dic = self.make_dict(status_code=000, show=self.show)

        self.send_msg(msg_dic=dic)

    def _md(self, msg_dic):
        content = msg_dic.get("content")
        # print(content)

        import subprocess
        s = subprocess.Popen("md {}".format(self.current_path + "\\" + content), shell=True,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE)

        stdout = s.stdout.read()
        stderr = s.stderr.read()

        msg_dic = self.make_dict(status_code=000, stdout=stdout.decode("gbk"), stderr=stderr.decode("gbk"))

        self.send_msg(msg_dic=msg_dic)

    def _dir(self, msg_dic):
        import subprocess
        s = subprocess.Popen("dir {}".format(self.current_path), shell=True,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)

        stdout = s.stdout.read()
        stderr = s.stderr.read()

        msg_dic = self.make_dict(status_code=000, stdout=stdout.decode("gbk"), stderr=stderr.decode("gbk"))

        self.send_msg(msg_dic=msg_dic)

    def _cd(self, msg_dic):
        path = self.current_path + "\\" + msg_dic.get("content")

        import os
        path = os.path.abspath(path)

        from config import setting
        if (setting.USER_HOME + "\\" + self.username) in path and os.path.exists(path):
            import subprocess
            s = subprocess.Popen("cd {}".format(path), shell=True,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE)

            stdout = s.stdout.read()
            stderr = s.stderr.read()

            self.show = path.replace(setting.USER_HOME + "\\" + self.username, "")

            msg_dic = self.make_dict(status_code=100, stdout=stdout.decode("gbk"), stderr=stderr.decode("gbk"), show=self.show)

            self.send_msg(msg_dic=msg_dic)

            self.current_path = path
        else:
            msg_dic = self.make_dict(status_code=101)

            self.send_msg(msg_dic=msg_dic)

    def _download(self, msg_dic):
        """下载文件"""
        file_name = msg_dic.get("file_name")

        file_path = self.current_path + "\\" + file_name

        import os
        if os.path.exists(file_path): # 如果该文件存在
            file_size = os.path.getsize(file_path)
            msg_dic = self.make_dict(status_code=102, file_path=file_path, file_size=file_size)
            self.send_msg(msg_dic=msg_dic)

            with open(file_path, "rb") as f:
                for line in f:
                    self.request.send(line)
        else:
            msg_dic = self.make_dict(status_code=103)
            self.send_msg(msg_dic=msg_dic)

    def _re_get(self, msg_dic):
        """断点续存"""
        file_path = msg_dic.get("file_path")
        file_size = msg_dic.get("file_size")
        recv_szie = msg_dic.get("recv_size")

        import os
        if os.path.exists(file_path) and int(file_size) == int(os.path.getsize(file_path)):
            msg_dic = self.make_dict(status_code=102)
            self.send_msg(msg_dic=msg_dic)

            with open(file_path, "rb") as f:
                f.seek(recv_szie)
                for line in f:
                    self.request.send(line)
        else:
            msg_dic = self.make_dict(status_code=103)
            self.send_msg(msg_dic=msg_dic)

log 文件夹 log.py 文件

def log(username, action, msg):
    from core import connect_database
    cursor = connect_database.connect()

    sql = "insert into log(username, action, msg) values(%s, %s, %s)"

    cursor.execute(sql, (username, action, msg))

代码演示

创建用户

用户登录

删除用户

切换目录、下载文件、断点续存

生成日志文件


文章作者: New Ass
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 New Ass !
  目录