python-优酷系统管理员视图粗糙版(无详细注释)

Tank-YouKu(仅管理员功能粗糙版)

优酷系统管理员视图功能

  1. 注册
  2. 登录
  3. 上传视频
  4. 删除视频
  5. 发布公告

前期准备

先创建好数据库以及各数据表

安装 pymysql 模块 、安装 DBUtils 模块

配置好服务端的 db_pool 中的数据库信息

创库创表语句

手动创建数据库 youku_demo, 需配置数据库编码 utf8 (安装时配置好了命令行中就不用管)

创建数据库:create database youku_demo;

选择该数据库:use youku_demo,然后执行下面的一堆sql代码,或者手动导入

创表代码及测试数据

测试用户:tank 密码: 123

/*
 Navicat MySQL Data Transfer

 Source Server         : localhost-E
 Source Server Type    : MySQL
 Source Server Version : 50645
 Source Host           : localhost:3306
 Source Schema         : youku_demo

 Target Server Type    : MySQL
 Target Server Version : 50645
 File Encoding         : 65001

 Date: 28/08/2019 21:22:47
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for download_record
-- ----------------------------
DROP TABLE IF EXISTS `download_record`;
CREATE TABLE `download_record`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NULL DEFAULT NULL,
  `movie_id` int(11) NULL DEFAULT NULL,
  `download_time` datetime(0) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Table structure for movie
-- ----------------------------
DROP TABLE IF EXISTS `movie`;
CREATE TABLE `movie`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `path` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `is_free` int(11) NULL DEFAULT NULL,
  `file_md5` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `user_id` int(11) NULL DEFAULT NULL,
  `is_delete` int(11) NULL DEFAULT NULL,
  `upload_time` datetime(0) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Table structure for notice
-- ----------------------------
DROP TABLE IF EXISTS `notice`;
CREATE TABLE `notice`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `content` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `user_id` int(11) NULL DEFAULT NULL,
  `create_time` datetime(0) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of notice
-- ----------------------------
INSERT INTO `notice` VALUES (1, 'test1', '测试发布公告是否正常', 1, '2019-08-28 21:18:38');

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `pwd` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `is_vip` int(11) NULL DEFAULT NULL,
  `is_locked` int(11) NULL DEFAULT NULL,
  `user_type` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `register_time` datetime(0) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'tank', 'e23087636dfcd7addf39e32f89e25d44', 0, 0, 'admin', '2019-08-28 21:18:10');

SET FOREIGN_KEY_CHECKS = 1;

数据库结构

安装pymysql模块

参见博客: MySQL-注释-Navicat基本使用-复杂查询练习题-解题思路-pymysql操作数据库-SQL注入-05 的安装部分

安装DBUtils模块

在命令行输入pip3 install DBUtils 开始安装

配置 db_pool

根据自己电脑的mysql 情况配置

项目架构与数据流向

目录结构

server目录结构

client目录结构

服务端代码

start.py

import os
import sys
from tcp_server.socket_server import SocketServer

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

if __name__ == '__main__':
    server = SocketServer()
    server.run()

tcp_server/socket_server.py

import socket
import struct
import json
from interface import common_interface
from interface import admin_interface
from concurrent.futures import ThreadPoolExecutor
from threading import Lock
from lib import lock_file

lock = Lock()

lock_file.mutex = lock

func_dic = {
    'register': common_interface.register_interface,
    'login': common_interface.login_interface,

    'check_movie': admin_interface.check_movie_interface,
    'upload_movie': admin_interface.upload_movie_interface,
    'get_movie_list': common_interface.get_movie_list_interface,
    'delete_movie': admin_interface.delete_movie_interface,
    'put_notice': admin_interface.put_notice_interface
}


class SocketServer:
    def __init__(self):
        self.server = socket.socket()
        self.server.bind(('127.0.0.1', 9527))
        self.server.listen(5)
        self.pool = ThreadPoolExecutor(50)

    def run(self):
        print('启动服务端...')
        while True:
            conn, addr = self.server.accept()

            self.pool.submit(self.working, conn, addr)

    # 任务分发
    def dispatcher(self, client_back_dic, conn):
        # # 判断功能的类型
        # if client_back_dic.get('type') == 'register':
        #     common_interface.register_interface(client_back_dic, conn)
        #
        # elif client_back_dic.get('type') == 'login':
        #     common_interface.login_interface(client_back_dic, conn)

        _type = client_back_dic.get('type')

        if _type in func_dic:  # register
            func_dic.get(_type)(client_back_dic, conn)

    # 用于执行客户端连接任务
    def working(self, conn, addr):
        while True:
            try:
                # 每一个客户端访问服务端都会经过此处
                # 此处用于接收客户端传入的数据
                headers = conn.recv(4)
                data_len = struct.unpack('i', headers)[0]
                data_bytes = conn.recv(data_len)
                client_back_dic = json.loads(data_bytes.decode('utf-8'))
                client_back_dic['addr'] = str(addr)
                self.dispatcher(client_back_dic, conn)

            except Exception as e:
                print(e)
                conn.close()
                break

interface/common_interface.py

from db import models
from lib import common, lock_file
from db import user_data


def register_interface(client_back_dic, conn):
    # 写业务逻辑
    # 1.判断用户名是否存在
    username = client_back_dic.get('username')
    # 通过用户名当作条件查询
    user_obj_list = models.User.select(name=username)

    # 若存在,给客户端返回数据, 告诉用户,用户已存在!
    if user_obj_list:
        send_dic = {'flag': False, 'msg': '用户已存在!'}

    # 若不存在,保存数据到MySQL数据库中, 返回注册成功给客户端
    else:
        password = client_back_dic.get('password')
        user_obj = models.User(
            name=username,
            #  pwd, is_vip, is_locked, user_type, register_time
            pwd=common.get_md5_pwd(password),
            is_vip=0,  # 0表示不是VIP, 1表示VIP
            is_locked=0,  # 0表示不锁定, 1表示锁定
            user_type=client_back_dic.get('user_type'),
            register_time=common.get_time())

        user_obj.save()

        send_dic = {'flag': True, 'msg': '注册成功'}

    common.send_data(send_dic, conn)


def login_interface(client_back_dic, conn):
    username = client_back_dic.get('username')
    user_list = models.User.select(name=username)

    if not user_list:
        send_dic = {'flag': False, 'msg': '用户不存在'}

    else:
        user_obj = user_list[0]
        password = client_back_dic.get('password')
        # 1.判断客户端传入的密码与数据库中的密码是否相等
        if user_obj.pwd == common.get_md5_pwd(password):

            # 产生一个随机字符串,作为session值
            session = common.get_random_code()
            addr = client_back_dic.get('addr')
            # 保存session值到服务端,session + user_id一同保存到服务端本地
            # 使用锁写入数据
            lock_file.mutex.acquire()
            user_data.user_online[addr] = [session, user_obj.id]
            lock_file.mutex.release()
            send_dic = {'flag': True, 'msg': '登录成功!', 'session': session}
        else:
            send_dic = {'flag': False, 'msg': '密码错误!'}

    common.send_data(send_dic, conn)


# 获取电影接口
@common.login_auth
def get_movie_list_interface(client_back_dic, conn):
    # 获取所有电影对象
    movie_obj_list = models.Movie.select()
    back_movie_list = []
    if movie_obj_list:

        # 过滤已删除的电影
        for movie_obj in movie_obj_list:
            # 没有删除则返回
            if not movie_obj.is_delete:
                back_movie_list.append(
                    # [电影名称、是否免费、电影ID]
                    [movie_obj.name, '免费' if movie_obj.is_free else "收费", movie_obj.id]
                )

        if back_movie_list:

            send_dic = {'flag': True, 'back_movie_list': back_movie_list}

        else:
            send_dic = {'flag': False, 'msg': '没有可删除的电影!'}
    else:

        send_dic = {'flag': False, 'msg': '没有电影!'}

    common.send_data(send_dic, conn)

db/models.py

from orm.orm import Models, StringField, IntegerField


# 用户表
class User(Models):
    # 表名
    table_name = 'user'
    # 字段
    id = IntegerField(name='id', primary_key=True)
    name = StringField(name='name')
    #  pwd, is_vip, is_locked, user_type, register_time
    pwd = StringField(name='pwd')
    is_vip = IntegerField(name='is_vip')
    is_locked = IntegerField(name='is_locked')
    user_type = StringField(name='user_type')
    register_time = StringField(name='register_time')


# 电影表
class Movie(Models):
    # 表名
    table_name = 'movie'
    # 字段
    id = IntegerField(name='id', primary_key=True)
    name = StringField(name='name')
    # path, is_free, file_md5, user_id, is_delete, upload_time
    path = StringField(name='path')
    is_free = IntegerField(name='is_free')  # 1   0
    file_md5 = StringField(name='file_md5')
    user_id = IntegerField(name='user_id')
    is_delete = IntegerField(name='is_delete')
    upload_time = StringField(name='upload_time')


# 公告表
class Notice(Models):
    table_name = 'notice'
    # 字段
    id = IntegerField(name='id', primary_key=True)
    title = StringField(name='title')
    content = StringField(name='content')
    user_id = IntegerField(name='user_id')
    create_time = StringField(name='create_time')


# 下载记录表
class DownloadRecord(Models):
    table_name = 'download_record'
    # 字段
    id = IntegerField(name='id', primary_key=True)
    user_id = IntegerField(name='user_id')
    movie_id = IntegerField(name='movie_id')
    download_time = StringField(name='download_time')

orm/orm.py

'''
定义字段类
'''
from orm.mysql_control import Mysql


class Field:
    def __init__(self, name, column_type, primary_key, default):
        self.name = name
        self.column_type = column_type
        self.primary_key = primary_key
        self.default = default


# varchar
class StringField(Field):
    def __init__(self, name, column_type='varchar(255)', primary_key=False, default=None):
        super().__init__(name, column_type, primary_key, default)


# int
class IntegerField(Field):
    def __init__(self, name, column_type='int', primary_key=False, default=0):
        super().__init__(name, column_type, primary_key, default)


# 元类控制表模型类的创建
class OrmMetaClass(type):

    # 类名, 类的基类, 类的名称空间
    def __new__(cls, class_name, class_bases, class_attr):
        # print(class_name, class_bases, class_attr)
        # 1.过滤Models类
        if class_name == 'Models':
            return type.__new__(cls, class_name, class_bases, class_attr)

        # 2.控制模型表中: 表名, 主键, 表的字段
        # 如果模型表类中没有定义table_name,把类名当做表名

        # 获取表名
        table_name = class_attr.get('table_name', class_name)  # user_info, User

        # 3.判断是否只有一个主键
        primary_key = None

        # 用来存放所有的表字段, 存不是目的,目的是为了取值方便
        mappings = {}

        '''
        __main__: xxxx
        'id': <__main__.IntegerField object at 0x000001E067D48B00>, 
        'name': <__main__.StringField object at 0x000001E067D48AC8>}
        '''
        for key, value in class_attr.items():

            # 判断value是否是字段类的对象
            if isinstance(value, Field):

                # 把所有字段都添加到mappings中
                mappings[key] = value

                if value.primary_key:

                    if primary_key:
                        raise TypeError('主键只能有一个')

                    # 获取主键
                    primary_key = value.name

        # 删除class_attr中与mappings重复的属性, 节省资源
        for key in mappings.keys():
            class_attr.pop(key)

        # 判断是否有主键
        if not primary_key:
            raise TypeError('必须要有一个主键')

        class_attr['table_name'] = table_name
        class_attr['primary_key'] = primary_key
        class_attr['mappings'] = mappings
        '''
               'table_name': table_name
               'primary_key': primary_key
               'mappings': {'id': <__main__.IntegerField object at 0x000001E067D48B00>,
                            'name': <__main__.StringField object at 0x000001E067D48AC8>}
                            }
       '''
        return type.__new__(cls, class_name, class_bases, class_attr)


# 继承字典类,
class Models(dict, metaclass=OrmMetaClass):
    def __init__(self, **kwargs):
        # print(kwargs)  # 接收关键字参数
        super().__init__(**kwargs)

    # 在对象.属性没有的时候触发
    def __getattr__(self, item):
        # print(item)
        return self.get(item, '没有这个key')

    # 在对象.属性 = 属性值 时触发
    def __setattr__(self, key, value):

        # 字典赋值操作
        self[key] = value

    # 查
    @classmethod
    def select(cls, **kwargs):

        # 获取数据库链接对象
        ms = Mysql()

        # 若没有kwargs代表没有条件查询
        if not kwargs:
            # select * from table;
            sql = 'select * from %s' % cls.table_name

            res = ms.my_select(sql)

        # 若有kwargs代表有条件
        else:
            # print(kwargs)  # {id:1}
            key = list(kwargs.keys())[0]  # id
            value = kwargs.get(key)  # 1

            # select * from table where id=1;
            sql = 'select * from %s where %s=?' % (cls.table_name, key)

            sql = sql.replace('?', '%s')

            res = ms.my_select(sql, value)

        if res:
            # [{},{}, {}]   ---->  [obj1, obj2, obj3]
            # 把mysql返回来的 列表套 字典 ---> 列表套 对象
            # l1 = []
            # # 遍历mysql返回所有的字典
            # for d in res:
            #     # 把每一个字典传给cls实例化成一个个的r1对象
            #     r1 = cls(**d)
            #     # 追加到l1列表中
            #     l1.append(r1)

            return [cls(**result) for result in res]

    # 插入
    def save(self):
        ms = Mysql()
        # insert into table(x,x,x) values(x,x,x);

        # 字段名
        fields = []
        # 字段的值
        values = []
        # 存放对应字段的?号
        args = []

        for k, v in self.mappings.items():
            # 把主键过滤掉
            if not v.primary_key:
                fields.append(v.name)
                values.append(getattr(self, v.name, v.default))
                args.append('?')

        # insert into table(x,x,x) values(?, ?, ?);
        sql = 'insert into %s(%s) values(%s)' % (
            self.table_name, ','.join(fields), ','.join(args)
        )

        sql = sql.replace('?', '%s')

        ms.my_execute(sql, values)

    # 更新
    def sql_update(self):
        ms = Mysql()

        fields = []
        primary_key = None
        values = []

        for k, v in self.mappings.items():
            # 获取主键的值
            if v.primary_key:
                primary_key = getattr(self, v.name, v.default)

            else:

                # 获取 字段名=?, 字段名=?,字段名=?
                fields.append(v.name + '=?')

                # 获取所有字段的值
                values.append(getattr(self, v.name, v.default))

        # update table set %s=?,... where id=1;  把主键当做where条件
        sql = 'update %s set %s where %s=%s' % (
            self.table_name, ','.join(fields), self.primary_key, primary_key
        )

        # print(sql)  # update User set name=? where id=3

        sql = sql.replace('?', '%s')

        ms.my_execute(sql, values)

orm/mysql_control.py

import pymysql
from orm.db_pool import POOL


class Mysql:

    def __init__(self):
        # 建立链接
        self.conn = POOL.connection()

        # 获取游标
        self.cursor = self.conn.cursor(pymysql.cursors.DictCursor)

    # 关闭游标链接方法
    def close_db(self):
        self.cursor.close()
        self.conn.close()

    # 查看
    def my_select(self, sql, args=None):

        self.cursor.execute(sql, args)

        res = self.cursor.fetchall()
        # [{}, {}, {}]
        # print(res)
        return res

    # 提交
    def my_execute(self, sql, args):
        try:
            # 把insert , update...一系列sql提交到mysql中
            self.cursor.execute(sql, args)

        except Exception as e:
            print(e)

orm/db_pool.py

from DBUtils.PooledDB import PooledDB
import pymysql

# pip3 install DBUtils

POOL = PooledDB(
    creator=pymysql,  # 使用链接数据库的模块
    maxconnections=6,  # 连接池允许的最大连接数,0和None表示不限制连接数
    mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
    maxcached=5,  # 链接池中最多闲置的链接,0和None不限制
    maxshared=3,
    # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
    blocking=True,  # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
    maxusage=None,  # 一个链接最多被重复使用的次数,None表示无限制
    setsession=[],  # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
    ping=0,
    # ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
    host='127.0.0.1',
    port=3306,
    user='root',
    password='000000',
    database='youku_demo',
    charset='utf8',
    autocommit='True'
)

lib/common.py

import time
import hashlib
import json
import struct
import uuid
from functools import wraps
from db import user_data


def get_time():
    now_time = time.strftime('%Y-%m-%d %X')

    return now_time


def get_md5_pwd(pwd):
    md = hashlib.md5()

    md.update(pwd.encode('utf-8'))
    md.update('虹桥炮王,Jason是也!'.encode('utf-8'))

    return md.hexdigest()


def send_data(send_dic, conn):
    data_bytes = json.dumps(send_dic).encode('utf-8')
    headers = struct.pack('i', len(data_bytes))
    conn.send(headers)
    conn.send(data_bytes)


def get_random_code():
    # uuid可以产生一个世界上唯一的字符串
    md5 = hashlib.md5()
    md5.update(str(uuid.uuid4()).encode('utf-8'))
    return md5.hexdigest()


# 登录认证装饰器
def login_auth(func):
    @wraps(func)
    # client_back_dic, conn = args
    def inner(*args, **kwargs):
        # if args[0].get('session') == 服务端存放的session值:
        # # [session, user_id] = values

        addr = args[0].get('addr')
        # addr: [session, user_id]

        user_session = user_data.user_online.get(addr)
        if args[0].get('session') == user_session[0]:
            args[0]['user_id'] = user_session[1]
        #
        # for values in user_data.user_online.values():
        #     if args[0].get('session') == values[0]:
        #         # 添加到client_back_dic
        #         args[0]['user_id'] = values[1]  # user_id

        # 判断user_id是否存在
        if args[0].get('user_id'):
            func(*args, **kwargs)

        else:
            send_dic = {'flag': False, 'msg': '未登录,请去登录!'}
            # send_data(send_dic, conn)
            send_data(send_dic, args[1])

    return inner

# if __name__ == '__main__':
#
#     # print(get_time())
#     print(get_random_code())
# 05248e1b1a10ac08872f8dd5d9dbd814
# 161df6d362dc52b0037d938a0717963e
# aabd3987f88b2db46566cf6d9ec864e2

lib/lock_file.py

mutex = None

db/user_data.py

user_online = {
    # addr: [session, user_id]
}

interface/admin_interface.py

from lib import common
from db import models
import os
from conf import settings


@common.login_auth
def upload_movie_interface(client_back_dic, conn):
    print('炮王来交货啦!')

    # 确保电影名称是唯一的  随机字符串 + 电影名称
    movie_name = common.get_random_code() + client_back_dic.get('movie_name')  # .mp4
    movie_size = client_back_dic.get('file_size')
    movie_path = os.path.join(settings.DOWNLOAD_PATH, movie_name)

    # 1.接受上传的文件
    data_recv = 0
    with open(movie_path, 'wb') as f:
        while data_recv < movie_size:
            data = conn.recv(1024)
            f.write(data)
            data_recv += len(data)

    # 2.把电影数据保存到mysql中
    movie_obj = models.Movie(
        name=movie_name, file_md5=client_back_dic.get('file_md5'),
        is_free=client_back_dic.get('is_free'), is_delete=0,
        path=movie_path, user_id=client_back_dic.get('user_id'),
        upload_time=common.get_time()
    )
    movie_obj.save()

    send_dic = {
        'flag': True, 'msg': f'{client_back_dic.get("movie_name")}电影上传成功!'
    }

    common.send_data(send_dic, conn)


@common.login_auth
def check_movie_interface(client_back_dic, conn):
    file_md5 = client_back_dic.get('file_md5')
    movie_list = models.Movie.select(file_md5=file_md5)
    if movie_list:
        print(1111)
        send_dic = {
            'flag': False,
            'msg': '电影已存在!'
        }

    else:
        print(222)
        send_dic = {
            'flag': True,
            'msg': '电影可以上传'
        }

    common.send_data(send_dic, conn)


def delete_movie_interface(client_back_dic, conn):
    # 直接删除
    movie_obj = models.Movie.select(id=client_back_dic.get('movie_id'))[0]
    movie_obj.is_delete = 1
    # 调用更新方法
    movie_obj.sql_update()

    send_dic = {
        'flag': True,
        'msg': '电影删除成功!'
    }
    common.send_data(send_dic, conn)


@common.login_auth
def put_notice_interface(client_back_dic, conn):
    title = client_back_dic.get('title')
    content = client_back_dic.get('content')
    user_id = client_back_dic.get('user_id')
    notice_obj = models.Notice(title=title, content=content, user_id=user_id,
                               create_time=common.get_time())

    notice_obj.save()

    send_dic = {
        'msg': '公告发布成功!'
    }

    common.send_data(send_dic, conn)

conf/settings.py

import os

BASE_PATH = os.path.dirname(os.path.dirname(__file__))

DOWNLOAD_PATH = os.path.join(BASE_PATH, 'download_files')

客户端代码

start.py

import os
import sys
from core import src

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

if __name__ == '__main__':
    src.run()

core/src.py

from core import admin, user

func_dic = {
    '1': admin.admin_view,
    '2': user.user_view,
}


def run():
    while True:
        print('''
        1.管理员功能
        2.用户功能
        q.退出
        ''')

        choice = input('请选择功能编号: ').strip()

        if choice == 'q':
            break

        if choice not in func_dic:
            continue

        func_dic.get(choice)()

core/admin.py

from tcp_client import socket_client
from lib import common
import os
from conf import settings

user_info = {
    'cookies': None
}


def register(client):
    while True:
        username = input('请输入用户名:').strip()
        password = input('请输入密码:').strip()
        re_password = input('请确认密码:').strip()
        if password == re_password:
            send_dic = {'username': username,
                        'password': password,
                        'type': 'register',
                        'user_type': 'admin'}
            # {'flag': False, 'msg': '用户已存在!'}
            # {'flag': True, 'msg': '注册成功'}
            back_dic = common.send_msg_back_dic(send_dic, client)

            if back_dic.get('flag'):
                print(back_dic.get('msg'))
                break

            else:
                print(back_dic.get('msg'))


def login(client):
    while True:
        username = input('请输入用户名: ').strip()
        password = input('请输入密码:').strip()

        send_dic = {
            'type': 'login',
            'username': username,
            'password': password,
            'user_type': 'admin'
        }

        back_dic = common.send_msg_back_dic(send_dic, client)

        if back_dic.get('flag'):
            session = back_dic.get('session')
            user_info['cookies'] = session
            print(back_dic.get('msg'))
            break
        else:
            print(back_dic.get('msg'))


# 上传电影
def upload_movie(client):
    while True:
        # 1.打印电影列表
        movie_list = common.get_movie_list()
        for index, movie in enumerate(movie_list):
            print(index, movie)

        choice = input('请输入上传的电影编号:').strip()

        if not choice.isdigit():
            print('请输入数字!')
            continue

        choice = int(choice)

        if choice not in range(len(movie_list)):
            print("请选择正确编号!")
            continue

        movie_name = movie_list[choice]

        movie_path = os.path.join(settings.UPLOAD_FILES, movie_name)

        # 2.去服务端校验电影是否存在
        file_md5 = common.get_movie_md5(movie_path)

        send_dic = {
            'type': 'check_movie',
            'session': user_info.get('cookies'),
            'file_md5': file_md5
        }

        back_dic = common.send_msg_back_dic(send_dic, client)

        if back_dic.get('flag'):
            print(back_dic.get('msg'))

            send_dic = {
                'type': 'upload_movie',
                'file_md5': file_md5,
                'file_size': os.path.getsize(movie_path),
                'movie_name': movie_name,
                'session': user_info.get('cookies')
            }

            is_free = input('上传电影是否免费: y/n').strip()

            if is_free == 'y':
                send_dic['is_free'] = 1

            else:
                send_dic['is_free'] = 0

            back_dic = common.send_msg_back_dic(send_dic, client, file=movie_path)

            if back_dic.get('flag'):
                print(back_dic.get('msg'))
                break

        else:
            print(back_dic.get('msg'))

    #
    #
    # send_dic = {'type': 'upload_movie','session': user_info.get('cookies')}
    # back_dic = common.send_msg_back_dic(send_dic, client)
    # print(back_dic)


# 删除电影
def delete_movie(client):
    while True:
        # 1.从服务端获取电影列表
        send_dic = {
            'type': 'get_movie_list',
            'session': user_info.get('cookies')
        }

        # 发送获取电影请求
        back_dic = common.send_msg_back_dic(
            send_dic, client)
        if back_dic.get('flag'):
            back_movie_list = back_dic.get('back_movie_list')
            # 打印选择的电影
            for index, movie_list in enumerate(back_movie_list):
                print(index, movie_list)

            # 2.选择需要删除的电影
            choice = input('请输入需要删除的电影编号:').strip()

            if not choice.isdigit():
                continue

            choice = int(choice)

            if choice not in range(len(back_movie_list)):
                continue

            movie_id = back_movie_list[choice][2]

            send_dic = {
                'type': 'delete_movie',
                'movie_id': movie_id,
                'session': user_info.get('cookies')
            }

            # 发送删除电影请求
            back_dic = common.send_msg_back_dic(send_dic, client)
            if back_dic.get('flag'):
                print(back_dic.get('msg'))
                break
        else:
            print(back_dic.get('msg'))
            break


# 发布公告
def put_notice(client):
    title = input('请输入公告标题:').strip()
    content = input('请输入公告内容:').strip()

    send_dic = {
        'type': 'put_notice',
        'session': user_info.get('cookies'),
        'title': title,
        'content': content
    }
    back_dic = common.send_msg_back_dic(send_dic, client)
    print(back_dic.get('msg'))


func_dic = {
    '1': register,
    '2': login,
    '3': upload_movie,
    '4': delete_movie,
    '5': put_notice,
}


def admin_view():
    sk_client = socket_client.SocketClient()
    client = sk_client.get_client()

    while True:
        print('''
        	1.注册
            2.登录
            3.上传视频
            4.删除视频
            5.发布公告
            q.退出
        ''')

        choice = input('请选择功能编号:').strip()

        if choice == 'q':
            break

        if choice not in func_dic:
            continue

        func_dic.get(choice)(client)

tcp_client/socket_client.py

import socket


class SocketClient:
    def __init__(self):
        self.client = socket.socket()
        self.client.connect(('127.0.0.1', 9527))

    def get_client(self):
        return self.client

lib/common.py

import json
import struct
from conf import settings
import os
import hashlib


def send_msg_back_dic(send_dic, client, file=None):
    data_bytes = json.dumps(send_dic).encode('utf-8')
    headers = struct.pack('i', len(data_bytes))
    client.send(headers)
    client.send(data_bytes)

    # 上传电影
    if file:
        with open(file, 'rb') as f:
            for line in f:
                # print(line)
                client.send(line)

    headers = client.recv(4)
    data_len = struct.unpack('i', headers)[0]
    data_bytes = client.recv(data_len)
    back_dic = json.loads(data_bytes.decode('utf-8'))
    return back_dic


def get_movie_list():
    if os.path.exists(settings.UPLOAD_FILES):
        movie_list = os.listdir(settings.UPLOAD_FILES)
        if movie_list:
            return movie_list


# 获取电影的md5值
def get_movie_md5(movie_path):
    md5 = hashlib.md5()
    # 截取电影的4个位置的md5值
    movie_size = os.path.getsize(movie_path)

    # 从电影的4个位置个截取10个bytes数据
    current_index = [0, movie_size // 3, (movie_size // 3) * 2, movie_size - 10]

    with open(movie_path, 'rb') as f:
        for index in current_index:
            f.seek(index)
            data = f.read(10)
            md5.update(data)

    return md5.hexdigest()

conf/settings.py

import os

BASE_PATH = os.path.dirname(os.path.dirname(__file__))

UPLOAD_FILES = os.path.join(BASE_PATH, 'upload_files')
原文地址:https://www.cnblogs.com/suwanbin/p/11426720.html