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

目录

上传文件功能的实现

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);
           }

        });

猜你喜欢

转载自blog.csdn.net/sjp11/article/details/131820264