我的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