我的QT Creator学习笔记(三十四)——网络编程之HTTP与FTP

Posted 今天也要努力搬砖

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我的QT Creator学习笔记(三十四)——网络编程之HTTP与FTP相关的知识,希望对你有一定的参考价值。

 参考文献:《Qt Creator 快速入门》第三版 霍亚飞编著

网络访问接口

网络请求由QNetworkRequest类来表示,它也作为与请求有关的信息的容器。

QNetworkAccessManager类用来协调网络操作,可以调度创建好的请求,并发射信号来报告进度。此类还可以协调cookies的使用,身份验证请求和代理的使用等。

网络请求的应答使用QNetworkReply类来表示,它会在请求调度完成时由QNetworkAccessManager创建。

1.HTTP

        HTTP(HyperText Transfer Protocol,超文本传输协议)是一个客户端和服务器端直接进行请求和应答的标准。通常由HTTP客户端发起一个请求,建立一个服务器指定端口(默认是80端口)的TCP连接;HTTP服务器在指定的端口监听客户端发送来的请求,一旦收到请求,服务器端就会向客户端发回一个应答。

下面一个示例代码(记得要在.pro文件中添加“QT+=network"),实现http下载文件,其界面布局如下

然后在.h文件中添加以下4个私有成员对象

    QNetworkAccessManager* manager;
    QNetworkReply* reply;
    QUrl url;
    QFile* file;

其中manager使用其get函数发送网络请求,get函数的参数是一个QUrl对象,我们的url成员对象就是用来保存这个对象的。reply监听网络请求的应答。file保存我们下载的文件。

然后添加几个槽函数

    void httpFinished();
    void httpReadyRead();
    void updateDataReadProgress(qint64,qint64);

httpFinished做一些下载完成后的工作。httpReadyRead()函数中将reply内容写入文件file->write(reply->readAll()),updateDataReadProgress函数中更新下载进度条。

再给下载按钮添加click槽函数,槽函数中获取url并调用startRequest函数启动下载,startRequest函数是自定义的成员函数,函数中调用manger->get(QNetworkRequest(url))发送网络请求,然后进行了QNetworkReply对象的几个信号和自定义槽的关联。

最后完整mainwindow.h文件如下

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QUrl>
class QFile;
class QNetworkReply;
class QNetworkAccessManager;

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    void startRequest(QUrl url);
private slots:
    void httpFinished();
    void httpReadyRead();
    void updateDataReadProgress(qint64,qint64);

    void on_pushButton_clicked();

private:
    Ui::MainWindow *ui;
    QNetworkAccessManager* manager;
    QNetworkReply* reply;
    QUrl url;
    QFile* file;
};
#endif // MAINWINDOW_H

完整 mainwindow.cpp文件如下

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtNetwork>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    manager=new QNetworkAccessManager(this);
    ui->progressBar->hide();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::startRequest(QUrl url)
{
    reply=manager->get(QNetworkRequest(url));
    connect(reply,&QNetworkReply::readyRead,this,&MainWindow::httpReadyRead);
    connect(reply,&QNetworkReply::downloadProgress,this,&MainWindow::updateDataReadProgress);
    connect(reply,&QNetworkReply::finished,this,&MainWindow::httpFinished);

}

void MainWindow::httpFinished()
{
    ui->progressBar->hide();
    if(file)
    {
        file->close();
        delete file;
        file=0;
    }
    reply->deleteLater();
    reply=0;
}

void MainWindow::httpReadyRead()
{
    if(file)
        file->write(reply->readAll());
}

void MainWindow::updateDataReadProgress(qint64 bytesRead, qint64 totalBytes)
{
    ui->progressBar->setMaximum(totalBytes);
    ui->progressBar->setValue(bytesRead);
}


void MainWindow::on_pushButton_clicked()
{
    url=ui->lineEdit->text();
    QFileInfo info(url.path());
    QString fileName(info.fileName());
    if(fileName.isEmpty())
        fileName="index.html";
    file=new QFile(fileName);
    if(!file->open(QIODevice::WriteOnly))
    {
        delete file;
        file=0;
        return;
    }
    startRequest(url);
    ui->progressBar->setValue(0);
    ui->progressBar->show();
}

2.FTP

FTP(FIle Transfer Protocol,文件传输协议)是一个主要用于浏览远程目录和传输文件的协议。FTP使用两个网络连接,一个用来发送命令,另一个用来输出数据。FTP协议由一个状态,并且需要客户端在传输文件之前发送一些命令。FTP客户端建立一个连接,并在整个会话期间一直保持打开。在每个会话期间可以发生多个传输。

FTP的实现方式与前面的HTTP十分相似,只须在QUrl对象中设置好主机地址、用户名和密码等,然后使用get、put等函数完成文件的获取和上传。

Qt5的兼容性扩展模块QtFtp中包含了支持Qt5的QFtp类。下载地址https://github.com/qt/qtftp

下面一个示例代码实现ftp传输文件(记得要在.pro文件中添加“QT+=network"),需要将与QFtp相关的qftp.cpp、qftp.h、qurlinfo.cpp、qurlinfo.h这4个文件添加到项目中。

其界面布局如下


 

在mainwindow.h文件中添加以下几个私有成员

    QFtp* ftp;
    QHash<QString,bool> isDirectory;
    QString currentPath;
    QFile* file;

 首先是QFtp指针对象,QFtp中提供了多个函数来完成常用的操作命令,分别是connectToHost()函数使用指定的端口号连接到FTP服务器主机,默认端口号是21;login()函数使用指定的用户名和密码登录到FTP服务器;close()函数关闭到ftp服务器的连接;list()函数列出FTP服务器上指定目录的内容,默认列出当前目录的内容;cd()函数改变服务器的工作目录到指定的目录;get()函数从服务器下载指定的文件,put()函数从I/O设备中读取数据,然后写入到服务器上指定的文件;另外还有remove()、mkdir()、rmdir()、rename()等函数。这些命令函数都会返回一个唯一的ID号,可以在后面使用这些ID号来区分不同的命令。

然后是isDirectory用来存储一个路径是否为目录,currentPath存储当前路径,file表示下载的文件。

再添加几个私有槽声明。

    void ftpCommandStarted(int);
    void ftpCommandFinished(int,bool);
    //更新进度
    void updateDataTransferProgress(qint64,qint64);
    //将服务器上的文件添加到TreeWidget部件中
    void addToList(const QUrlInfo &urlInfo);
    //双击一个目录时显示其内容
    void processItem(QTreeWidgetItem*,int);

ftpCommandStarted函数后面会关联到QFtp::commandStarted信号,ftpCommandFinished函数后面会关联到QFtp::commandFinished信号,updateDataTransferProgress函数后面会关联到QFtp::dataTransferProgress信号,addToList函数后面会关联到QFtp::listInfo信号。

再添加连接、返回上一级、下载按钮的click槽函数

    void on_pushButtonConnect_clicked();
    void on_pushButtonGoParent_clicked();
    void on_pushButtonDwonLoad_clicked();

最后总结以下示例代码的主要步骤:1、点击连接按钮时,创建ftp对象,绑定相应函数到ftp的对应信号上,调用ftp->connectToHost函数连接到服务器。调用ftp->login函数登录到服务器。

2、在ftpCommandFinished函数中,登录成功后ftp->list()来显示目录内容(会发送QFtp::listInfo信号,addToList函数绑定到了该信号,addToList中添加item到treeWidget),下载完成后关闭ftp

3、当单击某个item时如果时,目录调用ftp->cd打开该目录,调用ftp->list()显示目录内容;

4、点击download按钮时调用ftp->get()接口下载文件

5、点击返回上一级目录时调用ftp->cd()以及ftp->list()显示上一级目录内容。

完整mainwindow.h内容如下

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QHash>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class QFtp;
class QFile;
class QUrlInfo;
class QTreeWidgetItem;

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
private slots:
    void ftpCommandStarted(int);
    void ftpCommandFinished(int,bool);
    //更新进度
    void updateDataTransferProgress(qint64,qint64);
    //将服务器上的文件添加到TreeWidget部件中
    void addToList(const QUrlInfo &urlInfo);
    //双击一个目录时显示其内容
    void processItem(QTreeWidgetItem*,int);
    void on_pushButtonConnect_clicked();

    void on_pushButtonGoParent_clicked();

    void on_pushButtonDwonLoad_clicked();

private:
    Ui::MainWindow *ui;
    QFtp* ftp;
    QHash<QString,bool> isDirectory;
    QString currentPath;
    QFile* file;
};
#endif // MAINWINDOW_H

完整mainwindow.cpp内容如下

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "qftp.h"
#include <QFile>
#include <QTreeWidgetItem>
#include <QTextCodec>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->progressBar->setValue(0);
    connect(ui->treeWidgetFileList,&QTreeWidget::itemActivated,this,&MainWindow::processItem);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::ftpCommandStarted(int)
{
    int id=ftp->currentCommand();
    switch(id)
    {
    case QFtp::ConnectToHost:
        ui->label->setText(QString::fromLocal8Bit("正在连接到服务器..."));
        break;
    case QFtp::Login:
        ui->label->setText(QString::fromLocal8Bit("正在登陆..."));
        break;
    case QFtp::Get:
        ui->label->setText(QString::fromLocal8Bit("正在下载..."));
        break;
    case QFtp::Close:
        ui->label->setText(QString::fromLocal8Bit("正在关闭连接..."));
        break;
    }
}

void MainWindow::ftpCommandFinished(int, bool error)
{
    if(ftp->currentCommand()==QFtp::ConnectToHost)
    {
            if(error)
                ui->label->setText(QString::fromLocal8Bit("连接服务器出现错误:%1").arg(ftp->errorString()));
            else
                ui->label->setText(QString::fromLocal8Bit("连接到服务器成功"));
    }
    else if(ftp->currentCommand()==QFtp::Login)
    {
            if(error)
                ui->label->setText(QString::fromLocal8Bit("登录出现错误:%1").arg(ftp->errorString()));
            else
            {

                ui->label->setText(QString::fromLocal8Bit("登录成功"));
                ftp->list();
            }
    }
    else if(ftp->currentCommand()==QFtp::Get)
    {
            if(error)
                ui->label->setText(QString::fromLocal8Bit("下载出现错误:%1").arg(ftp->errorString()));
            else
            {
                ui->label->setText(QString::fromLocal8Bit("已经完成下载"));
                ftp->close();
            }
            ui->pushButtonDwonLoad->setEnabled(true);
    }
    else if(ftp->currentCommand()==QFtp::List)
    {
            if(isDirectory.isEmpty())
            {
                ui->treeWidgetFileList->addTopLevelItem(new QTreeWidgetItem(QStringList()<<tr("<empty>")));
                ui->treeWidgetFileList->setEnabled(false);
                ui->label->setText(QString::fromLocal8Bit("该目录为空"));
            }
    }
    else if(ftp->currentCommand()==QFtp::Close)
    {
            if(error)
                ui->label->setText(QString::fromLocal8Bit("关闭出现错误:%1").arg(ftp->errorString()));
            else
                ui->label->setText(QString::fromLocal8Bit("已经关闭连接"));
    }
}

void MainWindow::updateDataTransferProgress(qint64 readBytes, qint64 totalBytes)
{
    ui->progressBar->setMaximum(totalBytes);
    ui->progressBar->setValue(readBytes);
}

void MainWindow::addToList(const QUrlInfo &urlInfo)
{
    //注意因为服务器上使用UTF-8编码,这里要进行编码转换后再显示中文
    QString name=QString::fromLocal8Bit(urlInfo.name().toLatin1());
    QString owner=QString::fromLocal8Bit(urlInfo.owner().toLatin1());
    QString group=QString::fromLocal8Bit(urlInfo.group().toLatin1());
    QTreeWidgetItem* item=new QTreeWidgetItem;
    item->setText(0,name);
    item->setText(1,QString::number(urlInfo.size()));
    item->setText(2,owner);
    item->setText(3,group);
    item->setText(4,urlInfo.lastModified().toString("yyyy-MM-dd"));
    QPixmap pixmap(urlInfo.isDir()?"../myFtp/dir.png":"../myFtp/file.png");
    item->setIcon(0,pixmap);
    isDirectory[name]=urlInfo.isDir();
    ui->treeWidgetFileList->addTopLevelItem(item);
    if(!ui->treeWidgetFileList->currentItem())
    {
        ui->treeWidgetFileList->setCurrentItem(ui->treeWidgetFileList->topLevelItem(0));
        ui->treeWidgetFileList->setEnabled(true);
    }


}

void MainWindow::processItem(QTreeWidgetItem *item, int)
{
    //如果这个文件是目录则打开
    if(isDirectory[item->text(0)])
    {
        //目录名可能是中文,要使用ftp命令cd前需要先进行编码转换
        QString name=QLatin1String(item->text(0).toUtf8());
        ui->treeWidgetFileList->clear();
        isDirectory.clear();
        currentPath+="/"+name;
        ftp->cd(name);
        ftp->list();
        ui->pushButtonGoParent->setEnabled(true);
    }
}

void MainWindow::on_pushButtonConnect_clicked()
{
    ui->treeWidgetFileList->clear();
    currentPath.clear();
    isDirectory.clear();
    ui->progressBar->setValue(0);

    ftp=new QFtp(this);
    connect(ftp,&QFtp::commandStarted,this,&MainWindow::ftpCommandStarted);
    connect(ftp,&QFtp::commandFinished,this,&MainWindow::ftpCommandFinished);
    connect(ftp,&QFtp::listInfo,this,&MainWindow::addToList);
    connect(ftp,&QFtp::dataTransferProgress,this,&MainWindow::updateDataTransferProgress);

    QString ftpServer=ui->lineEditServer->text();
    QString userName=ui->lineEditUser->text();
    QString passWord=ui->lineEditPwd->text();
    ftp->connectToHost(ftpServer,21);
    ftp->login(userName,passWord);

}

void MainWindow::on_pushButtonGoParent_clicked()
{
    ui->treeWidgetFileList->clear();
    isDirectory.clear();
    currentPath=currentPath.left(currentPath.lastIndexOf('/'));
    if(currentPath.isEmpty())
    {
        ui->pushButtonGoParent->setEnabled(false);
        ftp->cd("/");
    }else
    {
        ftp->cd(currentPath);
    }
    ftp->list();
}

void MainWindow::on_pushButtonDwonLoad_clicked()
{
    QString fileName=ui->treeWidgetFileList->currentItem()->text(0);
    QString name=QLatin1String(fileName.toUtf8());
    file=new QFile(fileName);
    if(!file->open(QIODevice::WriteOnly))
    {
        delete file;
        return;
    }
    ui->pushButtonDwonLoad->setEnabled(false);
    ftp->get(name,file);
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

以上是关于我的QT Creator学习笔记(三十四)——网络编程之HTTP与FTP的主要内容,如果未能解决你的问题,请参考以下文章

我的QT Creator学习笔记(三十五)——网络编程之UDP与TCP

我的QT Creator学习笔记(三十五)——网络编程之UDP与TCP

我的QT Creator学习笔记(三十六)——进程和线程

我的QT Creator学习笔记(三十六)——进程和线程

Python学习笔记(三十四)—内置模块base64

Qt基础之三十四:QTcpSocket和QTcpServer源码分析