C++ 网络编程项目fastDFS分布式文件系统(七)--qss样式表,项目文件的上传和下载。

目录

1 单例模式

2. 如何在单例类中存储数据?

3. QSS样式表

3.1 选择器类型

3.2 QSS的使用步骤

 3.3 登录窗口设置

4. 客户端post方式上传数据

        4.1 常用的四种方式

5. 上传协议    


1 单例模式

#include<iostream>
#include<vector>
#include<mutex>
#include<thread>
#include<algorithm>
using namespace std;
 
//饿汉式
 
// class Singleton{
 
//     public:
//         static Singleton *GetInstance(){
//             return &m_instance;
//         }
 
//     private:
//         Singleton(){}
 
//         //c++ 98 防止拷贝
//         // Singleton(Singleton const &);
//         // Singleton& operator=(Singleton const &);
 
//         //c++ 11 防止拷贝
//         Singleton(Singleton const &)=delete;
//         Singleton& operator=(Singleton const &)=delete;
 
//         static Singleton m_instance;
 
// };
 
// Singleton Singleton::GetInstance();//在程序入口之前就完成初始化
 
//懒汉式
class Singleton{
 
    public:
        static Singleton *GetInstance(){
// 注意这里一定要使用Double-Check的方式加锁,才能保证效率和线程安全
            if(nullptr==m_pInstance){
                m_mtx.lock();
                if(nullptr==m_pInstance){
                    m_pInstance=new Singleton();
                }
                m_mtx.unlock();
 
            }
 
            return m_pInstance;
        }
 
        //实现一个内嵌的垃圾回收类
 
        class CGarbo{
 
            public:
                ~CGarbo(){
                    if(Singleton::m_pInstance){
 
                        delete Singleton::m_pInstance;
 
                    }
                }
 
        };
 
        static CGarbo Garbo;
 
    private:
    //构造函数私有
    Singleton(){}
 
    //防止拷贝
    Singleton(Singleton const &)=delete;
    Singleton &operator=( Singleton const&)=delete;
 
    static Singleton* m_pInstance;//单例对象指针
    static mutex m_mtx; //互斥锁
 
};
Singleton * Singleton::m_pInstance=nullptr;
Singleton::CGarbo Garbo;
mutex Singleton::m_mtx;
 
int main()
{
 
thread t1([]{cout<<(Singleton::GetInstance())<<endl;});
thread t2([]{cout<<(Singleton::GetInstance())<<endl;});
 
t1.join();
t2.join();
 
cout<<(Singleton::GetInstance())<<endl;
cout<<(Singleton::GetInstance())<<endl;
 
system("pause");
return 0;
}
 

        

2. 如何在单例类中存储数据?

        

// 实现了一个单例模式的类
// 存储用户名/密码/服务器的iP/端口
class Test
{
public:
static Test* getInstance()
{
return &m_test;
}
// 设置数据
void setUserName(QString name)
{
// 多线程-> 加锁
m_user = name;
// 解锁
}
// 获取数据
QString getUserName()
{
return m_user;
}
private:
Test();
Test(const Test& t);
// 静态变量使用之前必须初始化
// static Test* m_test;
static Test m_test;
// 定义变量 -> 属于唯一的单例对象
QString m_user;
QString m_passwd;
QString m_ip;
QString m_port;
QString m_token; //相当于ID,用于区分客户端的不同。
}
// Test* Test::m_test = new Test(); // 初始化
Test Test::m_test;
在客户端登录的时候 , 服务器回复给客户端的数据
        
// 成功
{
        "code" : "000" ,
        "token" : "xxx"
}
// 失败
{
        "code" : "001" ,
        "token" : "faild"
}
token -> 客户端成功连接了服务器 , 服务器针对于客户端的个人信息生成了一个唯一的身份标识
- 可以按照每个人的身份证号理解
-         服务器将这个 token 发送给客户端
-          客户端 token 的使用和保存 :
-          使用 : 登录成功之后 , 向服务器在发送任意请求都需要携带该 token
-          保存方式 : 放到单例对象中
-          服务器端的使用和保存 :
-         使用 : 接收客户端发送的 token , 和服务器端保存的 token 进行认证
-                  认证成功 : 合法客户端 , 失败 : 客户端非法
-         保存 : 服务器需要保存所有客户端的 token
-                 数据库中
-                 配置文件 -> 效率低
-                 redis -> 效率最高
token的生成: ( 客户端信息 + 随机数 ) * des * md5 * base64

3. QSS样式表

        3.1 选择器类型

3.2 QSS的使用步骤

// QSS 是一个文件 , 样式表文件 (CSS 文件 )
// - Qt 样式表支持 css2.0, 1.0 所有的语法 , css3.0 部分样式在 qt 中不支持
// 如何使用
/*
1. 根据介绍的选择器对所有的控件样式设置 , 写入 qss 文件中
2. 在程序中读样式表文件 , 得到一个字符串 -> 样式字符串
3. 将读出的样式设置给 QT 的应用程序对象
4. qt 中有一个全局的应用程序指针 qApp
5. qApp->setStyleSheet(" 样式字符串 ");
6. QFile 读磁盘文件 , 磁盘文件的编码格式必须是 utf8
*/

 3.3 登录窗口设置

/* 登录窗口设置背景图片 */
/* 登录窗口所有控件设置字体 , 字体大小 */
/* 设置登录 / 注册 / 服务器设置窗口标题字体 , 字体大小 */
/* 设置 logo 显示的图片 */
/* 设置窗口标题字体 , 字体大小 , 加粗 */
/* 没有账号马上注册按钮 : 字体颜色和添加下划线 */
/* 登录 / 注册 /OK 按钮 : 字体颜色 , 宽度 , 高度 , 字体大小 , 显示图片 */
/* 标题栏按钮 : normal, hover, press 三种状态切换 */

/* 按钮的默认状态 */
QPushButton #loginBtn
{
        color : white ;
        width : 200 ;
        height : 50 ;
        font-size : 30px ;
        border-image : url ( :/images/balckButton.png ); /* 默认显示的图片 */
}
/* 按钮的悬停状态 */
QPushButton #loginBtn : hover
{
        border-image : url ( :/images/balckButton1.png ); /* 默认显示的图片 */
}
/* 按钮的按下状态 不是 css 中的标准状态 , qt 独有的 */
QPushButton #loginBtn : pressed
{
        border-image : url ( :/images/balckButton2.png ); /* 默认显示的图片 */
}

4. 客户端post方式上传数据

 4.1 常用的四种方式

application/x-www-form-urlencoded
        
# 请求行
POST http: //www.example.com HTTP/1.1
# 请求头
Content-Type: application/x-www-form-urlencoded;charset=utf-8
# 空行
# 请求数据 ( 向服务器提交的数据 )
title=test&user=kevin&passwd=32222
application/json
        
POST http://www.example.com HTTP/1.1
Content-Type: application/json;charset=utf-8
{"title": "test","sub":[1,2,3]}

 text/xml

POST http://www.example.com HTTP/1.1
Content-Type: text/xml
<?xml version="1.0" encoding="utf8"?>
<methodcall>
<methodname color="red">examples.getStateName</methodname>
<params>
<value><i4>41</i4></value>
</params>
</methodcall>
multipart/form-data
  tool.oschina.net
        
POST http://www.example.com HTTP/1.1
Content-Type: multipart/form-data
# 发送的数据
------WebKitFormBoundaryPpL3BfPQ4cHShsBz \r\n
Content-Disposition: form-data; name="file"; filename="qw.png"; md5="xxxxxxxxxx"
Content-Type: image/png\r\n;
\r\n
............. 文件内容 ................
............. 文件内容 ................
------WebKitFormBoundaryPpL3BfPQ4cHShsBz
Content-Disposition: form-data; name="file"; filename="qw.png"; md5="xxxxxxxxxx"
Content-Type: image/png\r\n;
\r\n
............. 文件内容 ................
............. 文件内容 ................
------WebKitFormBoundaryPpL3BfPQ4cHShsBz--

5. 上传协议    

文件上传的一般步骤 :
               1. 尝试秒传 -> 文件并没上传
                2.给服务器发送的不是文件内容, 是文件的哈希值
                3.在服务器端收到哈希值, 查询数据库
                4.查到了 -> 秒传成功
                5.没查到 -> 秒传失败 , 需要进行一个真正的上传操作
        进行真正的上传
                1.需要的时间长
                2.上传有文件内容, 文件的哈希值
                3.文件内容 -> 分布式文件系统
                4.哈希值 -> 数据库
1. 秒传 
 客户端
# url
http: //127.0.0.1:80/md5
# post 数据格式
{
        user:xxxx,
        token:xxxx,
        md5:xxx,
        fileName: xxx
}

服务器 

location / md5
{
# 转发数据
        fastcgi_pass localhost:10002;
        include fastcgi.conf;
}

int main()
{
while(FCGI_Accept() >= 0)
{
// 1. 得到post数据的长度
char* length = getenv("content-length");
int len = atoi(length);
// 2. 根据len将数据读到内存中, json对象字符串
// 3. 解析json对象, user,md5, token, fileName
// 4. token认证 , 查询redis/数据库
// -- 成功: 继续后续操作, 失败, 返回, 给客户端一个结果
// 5. 打开数据库, 并查询md5是否存在
// -- 存在 {"code":"006"}
// -- 不存在 {"code":"007"}
}
}

 2. 上传

        客户端

       

# url
http://127.0.0.1:80/upload
# post数据格式
------WebKitFormBoundary88asdgewtgewx\r\n
Content-Disposition: form-data; user="mike"; filename="xxx.jpg"; md5="xxxx";
size=10240
Content-Type: image/jpg
真正的文件内容
------WebKitFormBoundary88asdgewtgewx--
Qt 中如何组织上述 post 数据块
        
// 组织数据块 - > QHttpPart
QHttpPart::QHttpPart();
// 设置数据头
void QHttpPart::setHeader(QNetworkRequest::KnownHeaders header, const QVariant
&value);
    - header:
    - QNetworkRequest::ContentDispositionHeader
    - QNetworkRequest::ContentTypeHeader
    - value:
        "form-data; 自定义的数据, 格式 xxx=xxx, 中间以;间隔"

//###############################################
// 适合传递少量的数据

void QHttpPart::setBody(const QByteArray &body);
    - body: 传递的数据串
// 传递大文件
void QHttpPart::setBodyDevice(QIODevice *device);
    - 使用参数device, 打开一个磁盘文件
    // QHttpMulitPart

QHttpMultiPart::QHttpMultiPart(ContentType contentType, QObject *parent =
Q_NULLPTR);
- 参数contentType: QHttpMultiPart::FormDataType


// 调用该函数会自动添加分界线 -> 使用频率高的函数
void QHttpMultiPart::append(const QHttpPart &httpPart);


// 查看添加的分界线的值
QByteArray QHttpMultiPart::boundary() const;


// 自己设置分界线, 一般不需要自己设置
void QHttpMultiPart::setBoundary(const QByteArray &boundary);

// 使用post方式发送数据
QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request,
QHttpMultiPart *multiPart);
服务器
        
location /upload
{
    # 转发数据
    fastcgi_pass localhost:10003;
    include fastcgi.conf;
}
// fastCGI程序
int main()
{
// 1. 获取数据长度 1024000
// 2. 循环读post数据内容
}

 服务器端fastCGI 部分 代码

        

 // 取出 Content-Disposition 中的键值对的值, 并得到文件内容, 并将内容写入文件
     int recv_save_file(char *user, char *filename, char *md5, long *p_size)
     {
         int ret = 0;
         char *file_buf = NULL;
         char *begin = NULL;
         char *p, *q, *k;
     
         char content_text[512] = {0}; //文件头部信息
         char boundary[512] = {0};     //分界线信息
     
         //==========> 开辟存放文件的 内存 <===========
         file_buf = (char *)malloc(4096);
         if (file_buf == NULL)
         {
             return -1;
         }
     
         //从标准输入(web服务器)读取内容
         int len = fread(file_buf, 1, 4096, stdin); 
         if(len == 0)
         {
             ret = -1;
             free(file_buf);
             return ret;
         }
     
         //===========> 开始处理前端发送过来的post数据格式 <============
         begin = file_buf;    //内存起点
         p = begin;
     
         /*
            ------WebKitFormBoundary88asdgewtgewx\r\n
            Content-Disposition: form-data; user="mike"; filename="xxx.jpg"; md5="xxxx"; size=10240\r\n
            Content-Type: application/octet-stream\r\n
            ------WebKitFormBoundary88asdgewtgewx--
         */
     
         //get boundary 得到分界线, ------WebKitFormBoundary88asdgewtgewx
         p = strstr(begin, "\r\n");
         if (p == NULL)
         {
             ret = -1;
             free(file_buf);
             return ret;
         }
     
         //拷贝分界线
         strncpy(boundary, begin, p-begin);
         boundary[p-begin] = '\0';   //字符串结束符
         p += 2; //\r\n
         //已经处理了p-begin的长度
         len -= (p-begin);
         //get content text head
         begin = p;
     
         //Content-Disposition: form-data; user="mike"; filename="xxx.jpg"; md5="xxxx"; size=10240\r\n
         p = strstr(begin, "\r\n");
         if(p == NULL)
         {
             ret = -1;
             free(file_buf);
             return ret;
         }
         strncpy(content_text, begin, p-begin);
         content_text[p-begin] = '\0';
     
         p += 2;//\r\n
         len -= (p-begin);
     
         //========================================获取文件上传者
         //Content-Disposition: form-data; user="mike"; filename="xxx.jpg"; md5="xxxx"; size=10240\r\n
         q = begin;
         q = strstr(begin, "user=");
         q += strlen("user=");
         q++;    //跳过第一个"
         k = strchr(q, '"');
         strncpy(user, q, k-q);  //拷贝用户名
         user[k-q] = '\0';
     
         //========================================获取文件名字
         //"; filename="xxx.jpg"; md5="xxxx"; size=10240\r\n
         begin = k;
         q = begin;
         q = strstr(begin, "filename=");
         q += strlen("filename=");
         q++;    //跳过第一个"
         k = strchr(q, '"');
         strncpy(filename, q, k-q);  //拷贝文件名
         filename[k-q] = '\0';
     
         //========================================获取文件MD5码
         //"; md5="xxxx"; size=10240\r\n
         begin = k;
         q = begin;
         q = strstr(begin, "md5=");
         q += strlen("md5=");
         q++;    //跳过第一个"
         k = strchr(q, '"');
         strncpy(md5, q, k-q);   //拷贝文件名
         md5[k-q] = '\0';
     
         //========================================获取文件大小
         //"; size=10240\r\n
         begin = k;
         q = begin;
         q = strstr(begin, "size=");
         q += strlen("size=");
         k = strstr(q, "\r\n");
         char tmp[256] = {0};
         strncpy(tmp, q, k-q);   //内容
         tmp[k-q] = '\0';
         *p_size = strtol(tmp, NULL, 10); //字符串转long
     
         begin = p;
         p = strstr(begin, "\r\n");
         p += 2; //\r\n
         len -= (p-begin);
     
         //下面才是文件的真正内容
         /*
            ------WebKitFormBoundary88asdgewtgewx\r\n
            Content-Disposition: form-data; user="mike"; filename="xxx.jpg"; md5="xxxx"; size=10240\r\n
            Content-Type: application/octet-stream\r\n
            真正的文件内容\r\n
            ------WebKitFormBoundary88asdgewtgewx--
         */
         // begin指向正文首地址
         begin = p;
         
         // 将文件内容抠出来
         // 文件内容写如本地磁盘文件
     
         free(file_buf);
         return ret;
     }
使用 fastCGI 管理器启动 fastCGI 程序
        
spawn-fcgi -a IP 地址 -p 端口 -f ./fastcgi 程序
- 提示启动失败
- ldd fastCGI 程序

4. Http 上传下载进度
        
// QNetworkReply - 信号
void QNetworkReply::downloadProgress ( qint64 bytesReceived , qint64 bytesTotal )
- bytesReceived : 已经接收的字节数
- bytesTotal : 要接收的总字节数
void QNetworkReply::uploadProgress ( qint64 bytesSent , qint64 bytesTotal )
- bytesSent : 已经发送的字节数
- bytesTotal : 要发送的总字节数

5. 上传大文件Nginx设置

        1. 413 错误

        

服务器提示:413 Request Entity Too Large 的解决方法
原因: 上传文件太大, 请求实体太长了
解决方案:
在配置文件nginx.conf中添加: client_max_body_size 10M
10M: 用户指定的大小
2. 设置的位置 :
http{ } 中设置: client_max_body_size 20m;
所有的 server 中的所有的 location 都起作用
server{ } 中设置: client_max_body_size 20m;
对当前 server 的所有的 location 生效
location{ } 中设置: client_max_body_size 20m;
只对当前 location 生效
3. 三者的区别是:
http{} 中控制着所有 nginx 收到的 http 请求。
报文大小限制设置在 server {}中,控制该 server 收到的请求报文大小
如果配置在 location 中,则报文大小限制,只对匹配了 location 路由规则的请求生效。

6. Qt中的哈希运算

        1. 哈希算法 - QCryptographicHash

        

        // 构造哈希对象
        1 QCryptographicHash ( Algorithm method );
        // 添加数据
        // c格式添加数据
        void QCryptographicHash::addData ( const char * data , int length );
        // qt中的常用方法
        void QCryptographicHash::addData ( const QByteArray & data );
        // 适合操作大文件
        bool QCryptographicHash::addData ( QIODevice * device ); // QFile
        - 使用 device 打开一文件 , addData 进行文件的读操作
        // 计算结果
        QByteArray QCryptographicHash::result () const ;
        // 一般适合 , 哈希值都是使用 16 进制格式的数字串来表示
        QByteArray QByteArray::toHex () const ;
        [ static ] QByteArray QCryptographicHash::hash ( const QByteArray & data , Algorithm
        method );
- 参数 data : 要运算的数据
- 参数 method : 使用的哈希算法
- 返回值 : 得到的哈希值

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值