[Network Cloud Disk Client] - Implementation of the function of uploading files

Table of contents

Implementation of upload file function

Design of uploadtask

Set upload slot function

uploadFileAction interface

 uploadFile interface

Upload files regularly

Progress bar design


Implementation of upload file function

Implementation of upload file function

1. Double-click the QListWidgetItem of "Upload File" or click the "Upload" menu item, a file dialog box will pop up.

 2. Select the corresponding file in the file dialog box and save the paths of all files (multiple files can be selected in the dialog box)

3. Store the corresponding files in  uploadtask* m_uploadtask ( uploadtask is an upload file queue type )

4. Define the m_uploadTimer timer and call uploadFileAction every 0.5 seconds to upload files.

5. In uploadFileAction, first send an md5 http request to the server

  • If the file exists on the server , the transfer will be successful in seconds and there is no need to send the file content to the server.
  • If the file does not exist on the server , then send an upload request to upload the contents of the file to the server .

6. If the transfer fails, call the uploadFile  interface to upload the file content to the server .

Design of uploadtask

1. UploadFileInfo  is a structure of uploaded file information.


#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 is an upload task list class. This task list stores the information of the files to be uploaded  . 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;
    }
}

Set upload slot function

Click the QListWidgetItem of " Upload File " to add the file upload task to the upload queue .

//设置“上传文件”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 interface

  • uploadFileAction gets an UploadFileInfo object from m_uploadtask
  • After sending an HTTP request to the server based on the UploadFileInfo object information to verify whether the file exists .
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"//用户信息
}

The client takes corresponding actions based on the code returned by the server. 


"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 interface

uploadFile sends an http request to send the data in the file to the server, and the server saves it.

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 //文件边界线

Server return value:

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

 

Upload files regularly

Define a timer that calls the uploadFileAction function every 0.5 seconds.

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

Progress bar design

Customize a progress bar type filedataProgress , including filedataProgress.h, filedataProgress.cpp, filedataProgress.ui files.

filedataProgress.ui interface design:

        

 

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

When the upload task is added to the upload list, the progress bar is displayed in the upload list and waits for the client to upload the file.

In the uploadFile interface,  you need to set the uploadProgress signal of QNetWorkReply to display the upload progress on the progress bar. . Whenever QNetWorkReply uploads file data, it will send out the uploadProgress signal. 

Note: bytesTotal cannot be 0, if it is 0, it will crash.

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

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

        });

Guess you like

Origin blog.csdn.net/sjp11/article/details/131820264
Recommended