【网络云盘客户端】——上传文件的功能的实现

news2024/9/28 17:22:47

目录

上传文件功能的实现

uploadtask的设计

设置上传的槽函数

uploadFileAction接口

 uploadFile接口

定时上传文件

进度条的设计


上传文件功能的实现

上传文件功能实现

1.双击 ”上传文件 “的 QListWidgetItem 或者 点击 “上传” 菜单项 都会弹出一个文件对话框

 2.在文件对话框选中对应的文件,保存所有文件的路径(在对话框中可以选中多个文件)

3.将对应的文件存放到  uploadtask* m_uploadtask中(uploadtask 是一个上传文件队列类型

4.定义m_uploadTimer定时器,每0.5秒调用 uploadFileAction 进行上传文件。

5.uploadFileAction中先发送一个 md5 http请求给服务器

  • 如果文件存在服务器,则 秒传成功,不需要将文件内容发送到服务器上
  • 如果文件不存在服务器,则再发送一个upload的请求,将文件的内容上传到服务器上。

6.妙传失败,调用 uploadFile 接口,将文件内容上传到服务器上。

uploadtask的设计

1.UploadFileInfo 是上传文件的信息的结构体


#define UPLOAD_NOT          0   //未上传
#define UPLOADING           1   //正在上传
#define UPLOAD_FINISHED     2   //上传完成
#define UPLOAD_FAILD        3   //上传失败
#define UPLOAF_FILE_EXISTE  4   //上传的文件已存在


struct UploadFileInfo
{
    QString md5;            //文件的md5值
    QString fileName;       //文件名称
    QString filePath;       //文件路径
    qint64 size;            //文件大小
    FileDataProgress *fdp;  //进度条
    int uploadStatus;       //0.未上传,1.正在上传,2.上传完成
};

2.UploadTask上传任务 列表类,该 任务列表 存储的是即将上传的文件的信息 UploadFileInfo

//uploadtask.h文件

//上传任务列表类, 单例模式
class UploadTask
{
public :
    //获取UploadTask唯一实例对象
    static UploadTask* getInstance();

    //添加文件到上传任务列表中
    int appendUploadTask(QString filePath);

    //判断任务列表是否为空
    bool isEmpty();

    //取出任务
    UploadFileInfo* takeTask();

    //删除任务
    void delUploadTask();

    //清空上传任务列表列表
    void clearList();
private:
    UploadTask();
    ~UploadTask();
    //静态数据成员,类中声明,类外必须定义
    static UploadTask* m_instance;
private:
    QList<UploadFileInfo*> m_fileList;
};



//uploadtask.cpp文件

//添加文件到上传任务列表中
int uploadtask::appendUploadTask(QString filePath)
{

    QFileInfo* fileinfo=new QFileInfo(filePath);
   if(fileinfo->size()>30*1024*1024)
   {
       //如果文件大于30mb,则放弃该文件
       delete fileinfo;
       return -1;
   }
    
    //新建一个UploadFileInfo对象
    UploadFileInfo* uploadFileInfo=new UploadFileInfo;
    uploadFileInfo->size=fileinfo->size();
    uploadFileInfo->fileName=fileinfo->fileName();
    uploadFileInfo->filePath=filePath;
    uploadFileInfo->md5=Common::getInstant()->getFileMd5(filePath);

    uploadFileInfo->uploadStatus=UPLOAD_NOT;//设置未上传状态
    filedataProgress* upProgress=new filedataProgress;
 
    upProgress->setName(uploadFileInfo->fileName);
    
    uploadFileInfo->fdp=upProgress;
   //将进度条插入到上传列表中
    uploadlayout::getInstant()->getVLayout()->insertWidget(0,upProgress);
    m_filelist.append(uploadFileInfo);
}


//判断任务列表是否为空
bool uploadtask::isEmpty()
{
    return m_filelist.empty();
}

//取出任务
UploadFileInfo* uploadtask::takeTask()
{
    if(m_filelist.empty()){
        return nullptr;
    }

   UploadFileInfo* temp=m_filelist.at(0);
   return temp;
}

//删除任务
void uploadtask::delUploadTask()
{
    //1.取出任务,判断该任务的状态,如果是上传完成,上传失败,上传成功,该任务是会被删除掉
    //2.将该任务从列表中移除
    //3.删除进度条,并删除该任务的资源
    if(isEmpty()){
        return;
    }

    UploadFileInfo* temp=m_filelist.at(0);
    if(temp->uploadStatus==UPLOAD_FINISHED||
       temp->uploadStatus==UPLOAD_FAILD||
       temp->uploadStatus==UPLOAF_FILE_EXISTE  )
    {
        //删除该任务
        qDebug()<<"删除任务";
        m_filelist.removeAt(0);

        //将进度条从布局中移除
        uploadlayout::getInstant()->getVLayout()->removeWidget(temp->fdp);
        delete temp->fdp;
        delete temp;
    }
}

//清空任务列表
void uploadtask::clearList()
{
    int len=m_filelist.size();
    for(int i=0;i<len;i++)
    {
        UploadFileInfo* temp=m_filelist.at(0);
        m_filelist.removeAt(0);
        uploadlayout::getInstant()->getVLayout()->removeWidget(temp->fdp);//将进度条从布局中移除
        delete temp->fdp;
        delete temp;
    }
}

设置上传的槽函数

点击 “上传文件" 的QListWidgetItem,将文件的上传任务 添加 到 上传队列中。

//设置“上传文件”QListWidgetItem的槽函数
//双击QListWidget窗口中的控件,则会发出一个itemDoubleClicked信号
connect(ui->listWidget,&QListWidget::itemDoubleClicked,this,[=](QListWidgetItem *item){
        if(item->text()=="上传文件")
        {
            addUploadFile();
        }
    });

//设置“上传”菜单项的槽函数
 connect(m_uploadAction,&QAction::triggered,this,[=]{
             addUploadFile();
             qDebug()<<"上传";
});

//添加上传文件
void myfile::addUploadFile()
{
    //getOpenFileNames可以获取多个文件的路径
    QStringList filelist=QFileDialog::getOpenFileNames();
    for(int i=0;i<filelist.size();i++)
    {
        //将文件添加到m_uploadtask中
        int res=m_uploadtask->appendUploadTask(filelist[i]);
        if(res==-1){
            //服务器设置最多只能上传30m大小的文件
            QMessageBox::warning(this,"警告","文件大小大于30m");
        }
    }
}

uploadFileAction接口

  • uploadFileActionm_uploadtask获取一个UploadFileInfo对象
  • 在根据UploadFileInfo对象信2息发送一个 HTTP请求给服务器去验证文件是否存在
POST http://119.23.41.13:80/md5 HTTP/1.1
Content-Type: application/json

{
    "fileName": "111.rtf", //文件名
    "md5": "8274425de767b30b2fff1124ab54abb5",//文件md5值,标识文件的唯一性
    "token": "7b4b4922958c5cbb153df2668b714144",//验证用户身份
    "user": "zhangsan"//用户信息
}

客户端根据服务器返回的 code 做出相应的动作。 

​
"code":"005"  //上传的文件已存在(别的用户正在上传该文件)
"code":"006"  //秒传成功,文件已经存在
"code":"007"  // 秒传失败,文件不存在,需要调用uploadFile上传文件,
"code":"011"   //"Token 验证失败,身份过期

​

 

//获取m_uploadtask中的一个上传任务
void myfile::uploadFileAction()
{
    if(m_uploadtask->isEmpty()){
        //任务列表为空
        // qDebug()<<"任务列表为空";
        return;
    }
    UploadFileInfo* uploadFileInfo=m_uploadtask->takeTask();
    //先进行快传,判断文件是否已经上传成功
    //封装http请求

    //如果该文件没有上传,则发送请求
    if(uploadFileInfo->uploadStatus==UPLOAD_NOT)
    {
        uploadFileInfo->uploadStatus=UPLOADING;
        QNetworkRequest request;
        //从配置文件中获取到ip地址和port端口号
        QString ip=Common::getInstant()->getConfValue("web_server","ip");
        QString port=Common::getInstant()->getConfValue("web_server","port");
        QString url = QString("http://%1:%2/md5").arg(ip).arg(port);
        request.setUrl(QUrl(url));

        //设置文件类型
        request.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("application/json"));
        //将data数据以QJson的格式发送给服务器

        QJsonObject object;
        object.insert("user", m_logininfo->user());
        object.insert("token", m_logininfo->token());
        object.insert("md5",uploadFileInfo->md5);
        object.insert("fileName",uploadFileInfo->fileName);

        QJsonDocument doc(object);
        QByteArray data=doc.toJson();
        //发送请求
       // qDebug()<<data;
        QNetworkReply* rely=m_manager->post(request,data);

        connect(rely,&QNetworkReply::readyRead,this,[=]{
            //响应到达,读取所有的数据
            QByteArray s=rely->readAll();
             qDebug() << "服务器返回数据:" << QString(s);
             //将s数据转换为Json对象
             QJsonParseError err;
             QJsonDocument document=QJsonDocument::fromJson(s,&err);

            if(err.error!=QJsonParseError::NoError){
                qDebug()<<"QJson格式错误";
                return;
            }
            //将QJson字符串转换为QJson对象
            QJsonObject object1;
            object1=document.object();

            //获取状态码
            QString value1=object1["code"].toString();
            if(value1=="005"){

                qDebug()<<"文件已经存在";
                //文件正在上传
                uploadFileInfo->uploadStatus=UPLOAF_FILE_EXISTE;
                Common::getInstant()->writeRecord(m_logininfo->user(),uploadFileInfo->fileName,value1);
                m_uploadtask->delUploadTask();

                return;
            }
            if(value1=="006"){
                uploadFileInfo->uploadStatus=UPLOAF_FILE_EXISTE;
                 Common::getInstant()->writeRecord(m_logininfo->user(),uploadFileInfo->fileName,value1);
                m_uploadtask->delUploadTask();

                qDebug()<<"妙传成功";
            }
            if(value1=="007"){
                //qDebug()<<"上传文件";
                //文件没有上传到服务器上,需要将文件上传到服务器上
                uploadFile(uploadFileInfo);

            }
            if(value1=="111")
            {
                Common::getInstant()->writeRecord(m_logininfo->user(),uploadFileInfo->fileName,value1);
                qDebug()<<"token验证失败";
            }
        });
    }
}

 uploadFile接口

uploadFile是发送一个 http请求 将文件中数据发送给服务器,服务器会将其进行保存。

POST http://119.23.41.13:80/upload HTTP/1.1
Content-Type: application/json

------WebKitFormBoundaryNr0Jm9D3w0GCiG9g //文件边界线
Content-Disposition: form-data; user="zhangsan" filename="111.rtf" md5="8274425de767b30b2fff1124ab54abb5" size=7
Content-Type: application/octet-stream

文件数据
------WebKitFormBoundaryNr0Jm9D3w0GCiG9g //文件边界线

服务器返回值:

"code":"008"
"code":"009"

 

//将文件上传到服务器上
/*
------WebKitFormBoundaryDQAR0QX1ojAyzAre\r\n
Content-Disposition: form-data; name="file"; filename="logo.png"\r\n
Content-Type: image/png\r\n
\r\n
真正的文件内容\r\n
------WebKitFormBoundaryDQAR0QX1ojAyzAre
*/
//将文件内容上传到服务器上
void myfile::uploadFile(UploadFileInfo *uploadFileInfo)
{

        QFile file(uploadFileInfo->filePath);
        file.open(QIODevice::ReadOnly | QIODevice::Text);

        logininfoinstance *login = logininfoinstance::getInstant();
        //getBoundary接口是随机生成一个文件边界线
        QString boundary = m_common->getBoundary();
        QByteArray data;
        data.append(boundary);
        data.append("\r\n");

        data.append("Content-Disposition: form-data; ");
        data.append(QString("user=\"%1\" filename=\"%2\" md5=\"%3\" size=%4")
                    .arg(login->user())
                    .arg(uploadFileInfo->fileName)
                    .arg(uploadFileInfo->md5)
                    .arg(uploadFileInfo->size));
        data.append("\r\n");
        data.append("Content-Type: application/octet-stream");
        data.append("\r\n");
        data.append("\r\n");


        //上传中的数据
        data.append(file.readAll());
        data.append("\r\n");
        data.append(boundary);//文件边界线

        if (file.isOpen()) {
            file.close();
        }

        QString url = QString("http://%1:%2/upload").arg(login->ip()).arg(login->port());
        QNetworkRequest request;
        request.setUrl(url);
        request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));

        //发送http请求
        QNetworkReply *reply = m_manager->post(request, data);
        if (reply == NULL) {
            qDebug() << "请求失败";
            return;
        }


        //显示文件上传进度
        connect(reply, &QNetworkReply::uploadProgress, this, [=](qint64 bytesSent, qint64 bytesTotal){
            //bytesSent 上传的字节数
            //bytesTotal 文件需要上传的总字节数

           if (bytesTotal != 0) {
                //显示进度条(设置进度条)
             uploadFileInfo->fdp->setValue(bytesSent, bytesTotal);
           }

        });

        connect(reply, &QNetworkReply::finished, this, [=](){
            //文件上传完成后
            if (reply->error() != QNetworkReply::NoError) {
                qDebug() << reply->errorString();
            } else {
                QByteArray s=reply->readAll();

    /*
    008: 上传成功
    009: 上传失败
    */
                //响应到达,读取所有的数据

                // qDebug() << "服务器返回数据:" << QString(s);
                 //将s数据转换为Json对象
                 QJsonParseError err;
                 QJsonDocument document=QJsonDocument::fromJson(s,&err);

                if(err.error!=QJsonParseError::NoError){
                    qDebug()<<"QJson格式错误";
                    return;
                }
                //将QJson字符串转换为QJson对象
                QJsonObject object1;
                object1=document.object();

                //获取状态码
                QString value1=object1["code"].toString();

                if (value1== "008") {

                    qDebug() << "上传成功";
                    uploadFileInfo->uploadStatus=UPLOAD_FINISHED;
                    getFileCount(Normal);


                } else if (value1 == "009") {

                    uploadFileInfo->uploadStatus=UPLOAD_FAILD;
                    qDebug() << "上传失败";
                }
                //将传输记录记录到文件中
                Common::getInstant()->writeRecord(login->user(),uploadFileInfo->fileName,value1);
              //   writeRecord(QString user, QString fileName, QString code, QString path)
            }
            m_uploadtask->delUploadTask();
            reply->deleteLater();
        });
}

 

定时上传文件

定义一个定时器,定时器每0.5秒 调用一次uploadFileAction函数,

    //设置每0.5秒调用uploadFileAction
   m_uploadTimer.start(500);
    connect(&m_uploadTimer,&QTimer::timeout,this,[=]()
    {
        uploadFileAction();
    });

进度条的设计

自定义一个进度条类型filedataProgress ,包括filedataProgress.h ,filedataProgress.cpp,filedataProgress.ui 文件.

filedataProgress.ui界面设计:

        

 

//filedataProgress.cpp文件
//设置文件名
void filedataProgress::setName(QString name)
{
    ui->label->setText(name);
}
//设置进度条数据
void filedataProgress::setValue(int value,int maxValue)
{
    ui->progressBar->setValue(value*100/maxValue);
}

当上传任务添加到上传列表时,就将进度条显示到 上传列表中,等待客户端上传文件。

uploadFile接口 中需要设置 QNetWorkReply的uploadProgress信号,将上传的进度显示到进度条上。。QNetWorkReply每 当上传一次文件数据,则会发出uploadProgress信号 

注意: bytesTotal 不能为 0,如果为 0,则会崩溃。

        //显示文件上传进度
        connect(reply, &QNetworkReply::uploadProgress, this, [=](qint64 bytesSent, qint64 bytesTotal){
            //bytesSent 上传的字节数
            //bytesTotal 文件需要上传的总字节数

           if (bytesTotal != 0) {
                //显示进度条(设置进度条)
             uploadFileInfo->fdp->setValue(bytesSent, bytesTotal);
           }

        });

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/803924.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

关于Java中的Lambda变量捕获

博主简介&#xff1a;想进大厂的打工人博主主页&#xff1a;xyk:所属专栏: JavaEE进阶 目录 一、Lambda表达式语法 二、Lambda中变量捕获 一、Lambda表达式语法 基本语法: (parameters) -> expression 或 (parameters) ->{ statements; } Lambda表达式由三部分组成&a…

嵌入式:QT Day4

一、手动完成服务器的实现&#xff0c;并注释具体步骤 源码&#xff1a; widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> //服务器类 #include <QTcpSocket> //客户端类 #include <…

vue-echarts配置项详解

起因 最近接手了一个vue3项目&#xff0c;echarts用的是"vue-echarts": “^6.0.0”&#xff0c;每次查看文档的时候痛苦不已&#xff0c;找一个配置要花费大量时间&#xff0c;所以这篇文章&#xff0c;主要就是为了记录比较常见的一些配置。 主要会写三种图的配置…

libcomposite: Unknown symbol config_group_init (err 0)

加载libcomposite.ko 失败 问题描述 如图&#xff0c;在做USB OTG 设备模式的时候需要用到libcomposite.ko驱动&#xff0c;加载失败了。 原因&解决方法 有一个依赖叫configfs.ko的驱动没有安装。可以从内核代码的fs/configfs/configfs.ko中找到这个驱动。先加载confi…

Linux学习之自定义函数

函数是把一些重复使用的命令封装成一个集合&#xff0c;之后可以使用函数名调用。 定义函数的格式如下&#xff1a; function 函数名() {指令集&#xff08;若干条语句&#xff09; return n }要是直接在Shell中直接定义函数&#xff0c;那么直接在Shell中直接使用函数名 参数…

建筑工地为什么要做人员定位?解读技术背后的安全与效益

建筑工地是一个复杂而危险的环境&#xff0c;人员安全一直是行业亟待解决的难题。为了确保工人的安全&#xff0c;并提高工地的管理效率&#xff0c;越来越多的建筑工地开始采用人员定位技术。 对此&#xff0c;华安联大便和各位朋友一起深入探讨人员定位技术的优势和功能&…

C++(14):重载运算与类型转换

当运算符被用于类类型的对象时&#xff0c;允许我们为其指定新的含义&#xff1b;同时&#xff0c;也能自定义类类型之间的转换规则。和内置类型的转换一样&#xff0c;类类型转换隐式地将一种类型的对象转换成另一种我们所需类型的对象。 当运算符作用于类类型的运算对象时&a…

一、创建自己的docker python容器环境;支持新增python包并更新容器;离线打包、加载image

1、创建自己的docker python容器环境 参考&#xff1a;https://blog.csdn.net/weixin_42357472/article/details/118991485 首先写Dockfile&#xff0c;注意不要有txt等后缀 Dockfile # 使用 Python 3.9 镜像作为基础 FROM python:3.9# 设置工作目录 WORKDIR /app# 复制当前…

了解网络层

网络层 1. 概述2. 网络层提供的两种服务2.1 面向连接的虚电路服务2.2 无连接的数据报服务2.3 虚电路服务 VS 数据报服务 3. IPv4地址及其应用3.1 分类编址的IPv4地址3.1.1 A类地址3.1.2 B类地址3.1.3 C类地址 3.2 划分子网的IPv4地址3.3 无分类编址的IPv4地址3.4 IPv4地址的应用…

出海品牌整合营销指南:打造全球化成功的关键策略

随着全球化的不断深入&#xff0c;越来越多的企业开始将目光投向海外市场&#xff0c;希望在国际舞台上展现自己的实力和魅力。然而&#xff0c;出海市场的竞争激烈&#xff0c;如何在陌生的土地上建立起品牌影响力&#xff0c;成为摆在出海企业面前的一大难题。在这样的背景下…

TD1850多用表校准系统参考标准

参考标准 分类 标准名称 国家标准 GB/T 13978-2008 数字多用表 GB/T 15637-2012 数字多用表校准仪通用规范 计量法规 JJF 1075-2015 钳形电流表校准规范 JJF 1284-2011 交直流电表校验仪校准规范 JJF 1587-2016 数字多用表校准规范 JJG 124-2005 电流表、电压表、功率表及…

苍穹外卖day02项目日志

1. 描述清楚新增员工的实现流程 1.1需求分析与设计 参考产品原型&#xff0c;设计表和接口。 1.1.1设计表 看员工管理的产品原型&#xff1a; 有员工姓名、账号、手机号、账号状态、最后操作时间等。 注意&#xff0c;操作一栏不是字段&#xff0c;其中的启用禁用才是。 再…

沦为囚犯的“烟草女王”卢平的管理口诀:大胆设计,小心求证

沦为囚犯的“烟草女王”卢平的管理口诀&#xff1a; 大胆设计&#xff0c;小心求证 卢平是当初是湖南烟草界女强人 大致2003年听到了一句话 在管理知识稀缺的年代 当初听了有点小激动 趣讲大白话&#xff1a;管理口诀有意思 【趣讲信息科技239期】 ***************************…

面试了一个在字节工作2年的“大佬”,我蚌埠住了

昨天面试了一位在字节跳动工作2年多的开发&#xff0c;简历上写的工作截止时间是“至今”。特意问了一下&#xff0c;才知道实际是六月份已经不在职了。面试也就进行了十多分钟&#xff0c;但想跟大家分享一些站在选人的视角如何看待面试中的一些问题。 先说说面试 首先肯定是…

不断学习和提高写作水平,使公文写作更加得心应手和高效精准

不断学习和提高写作水平&#xff0c;积累经验和技巧&#xff0c;是提高公文写作能力的重要方法。 具体来说&#xff0c;可以采取以下几个方面的工作&#xff1a; 1.学习范例&#xff1a;阅读优秀的公文范例&#xff0c;学习其写作技巧和语言风格&#xff0c;以丰富自己的写作经…

低代码开发平台源码

什么是低代码开发平台&#xff1f; 低代码来源于英文“Low Code&#xff0c;它意指一种快速开发的方式&#xff0c;使用最少的代码、以最快的速度来交付应用程序。通俗的来说&#xff0c;就是所需代码数量低&#xff0c;开发人员门槛低&#xff0c;操作难度低。一般采用简单的图…

如何将 LoRaWAN 用于比赛场景

如何将 LoRaWAN 用于比赛场景 关键词 LoRaWAN 实时上报 下行同步 不丢包 组播 应用场景 学生/运动员比赛&#xff0c;射击比武&#xff0c;同步采集等 摘要 为了将 LoRaWAN 应用于&#xff1a;比赛&#xff0c;比武&#xff0c;同步采集等场景&#xff0c;应对下行同步和…

cURL error 1: Protocol “https“ not supported or disabled in libcurl

1、php项目composer update报错 2、curl -V检查 发现curl已经支持了https了 3、php版本检查 4、php插件检查 插件也已经含有openssl组件了 5、phpinfo检查 curl是否开启ssl 定位到问题所在&#xff0c;php7.4的 curl扩展不支持 https 需要重装 php7.4的curl扩展 6、curl下载 下…

Feign API模块导入的两种方式

说明&#xff1a;在微服务框架中&#xff0c;会把其他微服务用到的FeignClient统一放到一个模块里面&#xff0c;称为FeignAPI&#xff0c;其他微服务需要使用FeignClient&#xff0c;引入FeignClient的Maven坐标就可以使用。 但是只引入FeignAPI的坐标还不行&#xff0c;Feig…

【分布式】分布式唯一 ID 的 几种生成方案以及优缺点snowflake优化方案

在互联网的业务系统中&#xff0c;涉及到各种各样的ID&#xff0c;如在支付系统中就会有支付ID、退款ID等。那一般生成ID都有哪些解决方案呢&#xff1f;特别是在复杂的分布式系统业务场景中&#xff0c;我们应该采用哪种适合自己的解决方案是十分重要的。下面我们一一来列举一…