项目构架
1、每台web server部署的东西应该完全相同,web server中部署nginx处理静态请求,对于其他请求,nginx会转发给fastcgi程序来处理。
2、一个fastcgi程序处理一个特定的http请求(比如上传图片请求需要有一个上传的cgi程序,比如登录请求需要有一个登录的cgi程序)。上图中绿色的application对应的就是cgi程序
3、使用fastDFS进行分布式存储。fastDFS中有三个角色,client、tracker、storage。上图中绿色的application对应fastDFS中的client。当客户端发送请求,nginx将请求反向代理到一台web server上,web server上的application访问tracker服务来获取相应的storage进程的ip地址和端口号。application根据获取到的ip地址和端口号来与对应的storage进程进行通信(即client从storage中存储资源或者获取资源)
4、mysql中存储用户相应的文件,我们需要在数据库中传存储每个用户名下的文件都是什么。使用cgi程序来控制mysql。
5、redis是缓存数据库,在web端有一些访问量十分拼房的数据,可以将该数据存储在redis中(因为mysql有连接上限且mysql频繁的io操作会比较浪费时间)。比如redis中可以存储被共享的文件的下载量,因为被共享的文件的下载量对于所有用户可见,是一个访问频繁的数据。
6、mysql和redis并不是直接通信的,他们之间的数据同步依靠cgi程序来进行设置。
用户验证的原因
概念:
server端有一个用户登录,就会对该用户进行标志。在客户端用户有一些操作的时候,需要在http请求中把相应的用户标识发送给服务端进行匹配,这样可以避免有些人模拟用户名和密码来进行恶意操作。
文件下载流程梳理
修改前
- 客户端发送http请求给nginx
- nginx反向代理到web server
- 服务器上的application链接tracker来查找客户端请求的数据位置
- tracker将存储对应文件的storage服务器的ip地址和端口号返回给application
- application根据tracker返回的storage服务的ip地址和端口号去获取客户端请求的数据
- application将获取到的数据返回给客户端
修改后
- 客户端发送http请求给nginx
- nginx反向代理到web server
- application进行数据库查询,通过url查找到相应的storage
- applicationstorage服务器中相应的文件资源返回给客户端
原理
1、在storage服务器上搭建nginx。
2、由于上传之后会得到文件相应的id,客户端每次上传数据之后,将数据相应的ip+文件id串(比如http://192.168.1.3/group/M00/00/00/xxxxx.png)存储在数据库中。
3、当客户端希望获取该文件时,直接通过IP地址+文件id来进行文件获取(客户端发送由ip+文件id构成的url给nginx服务器,通过插件fastdfs-nginx-module直接连接到存储该文件的storage服务器上,“”省去了使用tracker去查询相应的文件在哪一个storage服务器上“”这一步。)
QT
昨天研究了好久qt的安装,最后终于安上了,累死。
Qt客户端整体功能
- 登录
- 新用户注册
- 服务器设置
- 展示服务器上的用户文件
- 上传文件
- 文件秒传
- 文件下载
- 文件共享
- 文件转存
- 文件下载量统计
QT界面搭建
1、掌握窗口的布局
- 水平布局 - QHBoxLayout
- 垂直布局 - QVBoxLayout
- 网格布局 - QGridLayout
2、掌握如何绘制窗口背景图
- 重写这个类的绘图事件 - paintEvent(这是一个回调的虚函数)
- 在这个函数中使用画家类完成绘图操作(在使用画家类时,需要指定的绘图设备, 即当前的对象——QPainter p(this);)
3、掌握鼠标事件(也是回调)的使用
- mouseMoveEvent()
- mousePressEvent()
4、自定义窗口
- 最终是使用这个自定义类来提升一个基类
注意
- 首先看需要提升的类的数据类型(QPushbutton)
- 自定义的类需要从QPushbutton派生
- 使用自定义的子类去完成父类的提升,即把父类提升为子类
5、Qt中样式表的使用
- 在控件对应的属性窗口中设置样式
QLabel的父类是QFrame,QFrame的父类是QWidget,以此类推
6、Qt窗口间通信
- 使用信号槽
7、数据校验 - 正则表达式
- 数字字母下划线
- 正则表达式对应的类: QRegexp
第一步,构造变量——QRegexp reg("正则表达式");
第二步——reg.exactMatch(需要验证的字符串);
setPattern(const QString &pattern); //该函数可以重新初始化正则表达式
QT窗口间通信的实现例子
实现图中按钮的相应功能(整个界面分为三个窗口,上边的控制条是一个窗口,下面的输入是一个窗口,整体是一个窗口)
窗口的结构关系
父类——login
三个子类
控制条titlewg——他也是login的子类
按钮控件所在的ui文件位置
三个按钮的功能
- 点击设置按钮,下面的输入窗口会进行切换
- 点击最小化按钮,父类窗口login会最小化
- 点击关闭按钮,只有在登录页面时才会关闭窗口,其余时候都是退出当前窗口到下一个子类的窗口。
实现步骤
第一步:titlewg.h中声明信号(信号不需要实现,只要声明即可)
技巧,如果该事件在当前窗口处理不了,就把信号发送出去。权力越多的类,能够实现的功能就越多,让外面的父类去对事件进行处理。
signals:
//做信号,用于界面右上角的通知
void showSetWg(); //设置按钮
void closeWindow(); //关闭按钮
第二步:titlewg.cpp中,设置三个按钮的触发事件
// 按钮功能实现。如果使用lambda表达式,则参数this可以省略
//因为默认lambda表达式就属于当前类,而this指针指向的是当前类对象,因此this参数可以不写
connect(ui->set, &QToolButton::clicked, this,[=]()
{
// 发送自定义信号
emit showSetWg();
});
connect(ui->min, &QToolButton::clicked, [=]()
{
//点击最小化按钮,使得父类窗口最小化
m_parent->showMinimized();
});
connect(ui->close, &QToolButton::clicked, [=]()
{
//关闭窗口按钮
emit closeWindow();
});
第三步:在login中处理子类发送过来的信号
Login::Login(QWidget *parent) :
QDialog(parent),
ui(new Ui::Login)
{
ui->setupUi(this);
// 去掉创建的边框
this->setWindowFlags(Qt::FramelessWindowHint | windowFlags());
// 设置当前窗口所有的字体
this->setFont(QFont(QString::fromLocal8Bit("新宋体"), 12, QFont::Bold, false));
// 处理titlewidget发送过来的信号
connect(ui->title_wg, &TitleWg::closeWindow, [=]()
{
// 关闭时是分情况的,只有当前页面是登录页面才能直接关闭窗口。
//判断当前stackedWidget显示的页面
//设置页面切换到登录页面
if(ui->stackedWidget->currentWidget() == ui->set_page)
{
// 切换到登录
ui->stackedWidget->setCurrentWidget(ui->login_page);
// 清空控件数据
}
//注册页面切换到登录页面
else if(ui->stackedWidget->currentWidget() == ui->reg_page)
{
// 切换到登录
ui->stackedWidget->setCurrentWidget(ui->login_page);
// 清空控件数据
ui->name_reg->clear();
}
//当前界面是登录界面,则直接关闭窗口
else
{
this->close();
}
});
//显示设置界面,即——“服务器设置”界面
connect(ui->title_wg, &TitleWg::showSetWg, [=]()
{
//setCurrentWidget是当前窗口的指针,也可以使用setCurrentIndex——当前窗口的编号
//设置切换到set_page页面
ui->stackedWidget->setCurrentWidget(ui->set_page);
});
}
补充——查看窗口编号的方法
QT中的正则表达式
使用正则表达式实现数据校验功能。检测用户注册的用户名、密码、邮箱是否合法。
1、用户名
#define USER_REG "^[a-zA-Z\\d_@#-\*]\{3,16\}$"
- ^——代表字符的开始,说明字符串开始必须是[a-zA-Z\\d_@#-\*]的其中之一
- \d——代表 0-9 (\\d对应的是一个 \d,其中一个 \是转义)
- 这句正则表达式的意思就是开头可以是大小写字母,或者0-9的数字,或者_,或者@,或者#,或者-,或者*
- *——在正则表达式里面比较特殊,因此需要加上转义
- \{ ——也是对 { 做了转义
- $ ——是结束的意思
- {3,16}—— 说明字符最少要3个,最多16个
2、密码(与用户名相似)
#define PASSWD_REG "^[a-zA-Z\\d_@#-\*]\{6,18\}$"
3、电话
#define PHONE_REG "1\\d\{10\}"
- 第一位是1,后面是0-9的10个数字
4、email
#define EMAIL_REG "^[a-zA-Z\\d\._-]\+@[a-zA-Z\\d_\.-]\+(\.[a-zA-Z0-9_-]\+)+$"
5、IP
#define IP_REG "((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)"
- ()之间是一个整体
- 2[0-4]——开头可以是2,第二个字符是0-4之间的数
- \d——代表0-9
- |——或者的艺术
- 25[0-5]——第一位是2,第二位是5,第三位是0-5
- [01]——出现0或者1
- ?——代表出现一次
- {3}表示出现三次
6、端口
#define PORT_REG "^[1-9]$|(^[1-9][0-9]$)|(^[1-9][0-9][0-9]$)|(^[1-9][0-9][0-9][0-9]$)|(^[1-6][0-5][0-5][0-3][0-5]$)"
- ^[1-9]$——字符串的第一位和结尾必须是1-9
- |——或者
- ^[1-9][0-9]$——开始是1-9,结尾是0-9
分析——在构造初始化的时候,可以构造一个正则表达式,比如验证用户名。那么在验证邮箱的时候,需要再创建一个对象吗?
不需要,有一个函数可以重新初始化正则表达式。
void setPattern(const QString &pattern);