要求: 实现从"http://code.tarena.com.cn/"下载代码 点击可以直接下载 QT版本:5.15
一、使用Qt Creater创建项目
工程名:HttpClient 类名:MainWindow(默认) 选择qmake构建 基类选择QMainWindow 创建完成后如下
二、工程文件.pro添加相应模块
添加网络模块:QT+=network
三、编写UI界面
要使用的控件有:Text Browser
- 双击mainwindow.ui打开
- 添加Text Browser控件
- 更改程序窗口名为:代码下载器
- 控件TextBrowser去掉openlinks 选择
默认是打开的。只能加载文档,加载不了网页。关闭后自己处理就可以了, - 布局
这里选择垂直布局->报存
四、头文件添加相关的类库和成员变量
- 添加头文件类库
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QAuthenticator>
#include <QFile>
#include <QFileInfo>
#include <QDebug>
- 添加成员变量和槽函数
public slots:
void sendRequest();
void onAuthenticationRequired(QNetworkReply*,QAuthenticator*);
void onReadyRead();
void onFinished();
private:
Ui::MainWindow *ui;
QNetworkAccessManager *manager;
QNetworkRequest request;
QNetworkReply *reply;
QUrl currentUrl;
QByteArray buf;
};
五、源文件添加相应的槽函数
- 头文件中按F4跳转到源文件
void MainWindow::sendRequest(){}
void MainWindow::onAuthenticationRequired(QNetworkReply*,QAuthenticator*){}
void MainWindow::onReadyRead(){}
void MainWindow::onFinished(){}
六、代码编写
1. 构建函数创建完整对象
manager = new QNetworkAccessManager(this);
request.setUrl(QUrl("http://code.tarena.com.cn/"));
sendRequest();
2. 编写发送请求sendRequest
void MainWindow::sendRequest(){
reply = manager->get(request);
connect(manager,SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
this,SLOT(onAuthenticationRequired(QNetworkReply*,QAuthenticator*)));
connect(reply,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
connect(reply,SIGNAL(finished()),this,SLOT(onFinished()));
}
3. 处理登录认证的槽函数
-
创建登录认证的窗口
-
选择没有任何按钮的对话框
-
创建完成后多了一个UI文件:
-
编辑logindialog.ui页面设置 需要用到三个label、二个lineEdit、一个Button Box控件
-
把输入框更改为明文输入 -
修改LineEdit对象名
-
确认和取消按钮转到槽函数自动生成
-
按F4转到logindialog头文件,添加成员变量 public:
const QString& getUsername();
const QString& getPassword();
private:
Ui::LoginDialog *ui;
QString m_username;
QString m_password;
- F4切换到源文件,添加对应槽函数
void LoginDialog::on_buttonBox_accepted()
{
m_username = ui->usernameEdit->text();
m_password = ui->passwdEdit->text();
accept();
}
void LoginDialog::on_buttonBox_rejected()
{
reject();
}
const QString& LoginDialog::getUsername(){
return m_username;
}
const QString& LoginDialog::getPassword(){
return m_password;
}
- 回到主页面代码添加头文件和实现登录槽函数
void MainWindow::onAuthenticationRequired(QNetworkReply*,QAuthenticator* a){
LoginDialog login(this);
if(login.exec() == QDialog::Accepted){
a->setUser(login.getUsername());
a->setPassword(login.getPassword());
}
}
4. 接收响应数据的槽函数
void MainWindow::onReadyRead(){
buf +=reply->readAll();
currentUrl = reply->url();
}
5. 接收响应数据的槽函数
void MainWindow::onFinished(){
ui->textBrowser->setText(buf);
buf.clear();
reply->deleteLater();
}
七、运行测试
八、完善功能
1. MainWindow.h 头文件添加处理目录槽函数
void onAnchorclicked(const QUrl&url);
2.mainwindow.cpp修改构造函数
connect(reply,SIGNAL(finished()),this,SLOT(onFinished()));
3.添加相关槽函数
void MainWindow::onAnchorclicked(const QUrl & url){
qDebug()<<"当前URL:"<<currentUrl.toString();
qDebug() << "点击url:"<<url.toString();
QUrl newUrl;
if(url.toString() !="../"){
newUrl = currentUrl.toString()+url.toString();
}else{
if(currentUrl.toString() == "http://code.tarena.com.cn/"){
return;
}
int pos = currentUrl.toString().lastIndexOf("/",-2);
newUrl = currentUrl.toString().mid(0,pos+1);
}
request.setUrl(newUrl);
sendRequest();
}
4.是否为文件做判断下载逻辑
- 回到源文件添加槽函数
public slots:
void sendRequest();
void onAuthenticationRequired(QNetworkReply*,QAuthenticator*);
void onReadyRead();
void onFinished();
void onAnchorclicked(const QUrl&url);
void downloadFile(const QUrl& url);
void receiveFile();
void onDownloadProgress(qint64,qint64);
void receiveFileFinished();
private:
Ui::MainWindow *ui;
QNetworkAccessManager *manager;
QNetworkRequest request;
QNetworkReply *reply;
QUrl currentUrl;
QByteArray buf;
QFile* file;
};
- 槽函数编写
void MainWindow::onAnchorclicked(const QUrl & url){
QUrl newUrl;
if(url.toString() !="../"){
newUrl = currentUrl.toString()+url.toString();
}else{
if(currentUrl.toString() == "http://code.tarena.com.cn/"){
return;
}
int pos = currentUrl.toString().lastIndexOf("/",-2);
newUrl = currentUrl.toString().mid(0,pos+1);
}
if(url.toString().lastIndexOf("/") ==-1){
downloadFile(newUrl);
}else{
request.setUrl(newUrl);
sendRequest();
}
}
void MainWindow::downloadFile(const QUrl& fileUrl){
QFileInfo fileInfo = fileUrl.path();
QString filename = fileInfo.fileName();
file = new QFile(filename,this);
file->open(QIODevice::WriteOnly);
request.setUrl(fileUrl);
reply = manager->get(request);
connect(reply,SIGNAL(readyRead()),this,SLOT(receiveFile()));
connect(reply,SIGNAL(downloadProgress(qint64,qint64)),this,SLOT(onDownloadProgress(qint64,qint64)));
connect(reply,SIGNAL(finished()),this,SLOT(receiveFileFinished()));
}
void MainWindow::receiveFile(){
file->write(reply->readAll());
}
void MainWindow::onDownloadProgress(qint64 readBytes,qint64 totalBytes){
qint64 progress = readBytes*100/totalBytes;
qDebug()<<file->fileName()<<"下载:"<<progress<<"%...";
}
void MainWindow::receiveFileFinished(){
qDebug()<<file->fileName()<<":文件下载完成!";
file->flush();
file->close();
reply->deleteLater();
}
5.测试
6.完整代码
- mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QAuthenticator>
#include <QFile>
#include <QFileInfo>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
public slots:
void sendRequest();
void onAuthenticationRequired(QNetworkReply*,QAuthenticator*);
void onReadyRead();
void onFinished();
void onAnchorclicked(const QUrl&url);
void downloadFile(const QUrl& fileUrl);
void receiveFile();
void onDownloadProgress(qint64,qint64);
void receiveFileFinished();
private:
Ui::MainWindow *ui;
QNetworkAccessManager *manager;
QNetworkRequest request;
QNetworkReply *reply;
QUrl currentUrl;
QByteArray buf;
QFile* file;
};
#endif
- mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "logindialog.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
manager = new QNetworkAccessManager(this);
request.setUrl(QUrl("http://code.tarena.com.cn/"));
sendRequest();
connect(ui->textBrowser,SIGNAL(anchorClicked(QUrl)),this,SLOT(onAnchorclicked(QUrl)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::sendRequest(){
reply = manager->get(request);
connect(manager,SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
this,SLOT(onAuthenticationRequired(QNetworkReply*,QAuthenticator*)));
connect(reply,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
connect(reply,SIGNAL(finished()),this,SLOT(onFinished()));
}
void MainWindow::onAuthenticationRequired(QNetworkReply*,QAuthenticator* a){
LoginDialog login(this);
if(login.exec() == QDialog::Accepted){
a->setUser(login.getUsername());
a->setPassword(login.getPassword());
}
}
void MainWindow::onReadyRead(){
buf +=reply->readAll();
currentUrl = reply->url();
}
void MainWindow::onFinished(){
ui->textBrowser->setText(buf);
buf.clear();
reply->deleteLater();
}
void MainWindow::onAnchorclicked(const QUrl & url){
QUrl newUrl;
if(url.toString() !="../"){
newUrl = currentUrl.toString()+url.toString();
}else{
if(currentUrl.toString() == "http://code.tarena.com.cn/"){
return;
}
int pos = currentUrl.toString().lastIndexOf("/",-2);
newUrl = currentUrl.toString().mid(0,pos+1);
}
if(url.toString().lastIndexOf("/") ==-1){
downloadFile(newUrl);
}else{
request.setUrl(newUrl);
sendRequest();
}
}
void MainWindow::downloadFile(const QUrl& fileUrl){
QFileInfo fileInfo = fileUrl.path();
QString filename = fileInfo.fileName();
file = new QFile(filename,this);
file->open(QIODevice::WriteOnly);
request.setUrl(fileUrl);
reply = manager->get(request);
connect(reply,SIGNAL(readyRead()),this,SLOT(receiveFile()));
connect(reply,SIGNAL(downloadProgress(qint64,qint64)),this,SLOT(onDownloadProgress(qint64,qint64)));
connect(reply,SIGNAL(finished()),this,SLOT(receiveFileFinished()));
}
void MainWindow::receiveFile(){
file->write(reply->readAll());
}
void MainWindow::onDownloadProgress(qint64 readBytes,qint64 totalBytes){
qint64 progress = readBytes*100/totalBytes;
qDebug()<<file->fileName()<<"下载:"<<progress<<"%...";
}
void MainWindow::receiveFileFinished(){
qDebug()<<file->fileName()<<":文件下载完成!";
file->flush();
file->close();
reply->deleteLater();
}
- logindialog.h
#ifndef LOGINDIALOG_H
#define LOGINDIALOG_H
#include <QDialog>
namespace Ui {
class LoginDialog;
}
class LoginDialog : public QDialog
{
Q_OBJECT
public:
explicit LoginDialog(QWidget *parent = nullptr);
~LoginDialog();
private slots:
void on_buttonBox_accepted();
void on_buttonBox_rejected();
public:
const QString& getUsername();
const QString& getPassword();
private:
Ui::LoginDialog *ui;
QString m_username;
QString m_password;
};
#endif
- logindialog.cpp
#include "logindialog.h"
#include "ui_logindialog.h"
LoginDialog::LoginDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::LoginDialog)
{
ui->setupUi(this);
}
LoginDialog::~LoginDialog()
{
delete ui;
}
void LoginDialog::on_buttonBox_accepted()
{
m_username = ui->usernameEdit->text();
m_password = ui->passwdEdit->text();
accept();
}
void LoginDialog::on_buttonBox_rejected()
{
reject();
}
const QString& LoginDialog::getUsername(){
return m_username;
}
const QString& LoginDialog::getPassword(){
return m_password;
}
- main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
九、版本2使用多线程
目前不足:一次只能下载一个文件,如果中途点了其他的就无法继续下载 扩展:
- 使用多线程,将下载文件操作放到子线程中,实现多个文件同时下载
- 将要下载的文件,指定保存在用户的Downloads目录下
完整代码
参考代码: mainwindow.h、 mainwindow.cpp、 登录验证:logindialog.h、logindialog.cpp 下载模块:download.h 、download.cpp
- 代码1:mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QAuthenticator>
#include <QFile>
#include <QFileInfo>
#include <QDebug>
#include <download.h>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void sendRequest();
private slots:
void onAuthenticationRequired(
QNetworkReply*,QAuthenticator*);
void onReadyRead();
void onFinished();
void onAnchorClicked(const QUrl& url);
private:
void downloadFile(const QUrl& fileUrl);
private:
Ui::MainWindow *ui;
public:
QNetworkAccessManager* manager;
private:
QNetworkRequest request;
QNetworkReply* reply;
QUrl currentUrl;
QByteArray buf;
};
#endif
- 代码2:mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "logindialog.h"
#include "download.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
manager = new QNetworkAccessManager(this);
request.setUrl(QUrl("http://code.tarena.com.cn/"));
sendRequest();
connect(ui->textBrowser,SIGNAL(anchorClicked(QUrl)),
this,SLOT(onAnchorClicked(QUrl)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::sendRequest()
{
ui->textBrowser->setEnabled(false);
reply = manager->get(request);
connect(manager,SIGNAL(
authenticationRequired(
QNetworkReply*,QAuthenticator*)),
this,SLOT(onAuthenticationRequired(
QNetworkReply*,QAuthenticator*)));
connect(reply,SIGNAL(readyRead()),
this,SLOT(onReadyRead()));
connect(reply,SIGNAL(finished()),
this,SLOT(onFinished()));
}
void MainWindow::onAuthenticationRequired(
QNetworkReply*,QAuthenticator* authenticator)
{
LoginDialog login(this);
if(login.exec() == QDialog::Accepted){
authenticator->setUser(login.getUsername());
authenticator->setPassword(login.getPassword());
}
}
void MainWindow::onReadyRead()
{
buf += reply->readAll();
currentUrl = reply->url();
}
void MainWindow::onFinished()
{
ui->textBrowser->setText(buf);
buf.clear();
reply->deleteLater();
ui->textBrowser->setEnabled(true);
}
void MainWindow::onAnchorClicked(
const QUrl &url){
QUrl newUrl;
if(url.toString() != "../"){
newUrl = currentUrl.toString() +
url.toString();
}
else{
if(currentUrl.toString() ==
"http://code.tarena.com.cn/"){
return;
}
int pos = currentUrl.toString(
).lastIndexOf("/",-2);
newUrl =
currentUrl.toString().mid(0,pos+1);
}
if(url.toString().lastIndexOf("/")==-1){
downloadFile(newUrl);
return;
}
request.setUrl(newUrl);
sendRequest();
}
void MainWindow::downloadFile(const QUrl& fileUrl)
{
QThread *thread = new QThread;
DownloadFile* download = new DownloadFile(fileUrl);
download->moveToThread(thread);
connect(download,SIGNAL(downloadFinished()),
thread,SLOT(quit()));
connect(thread,SIGNAL(finished()),
download,SLOT(deleteLater()));
connect(thread,SIGNAL(finished()),
thread,SLOT(deleteLater()));
download->request.setUrl(fileUrl);
download->reply = manager->get(download->request);
connect(download->reply,SIGNAL(readyRead()),
download,SLOT(ReceiveFile()));
connect(download->reply,SIGNAL(finished()),
download,SLOT(ReceiveFileFinished()));
connect(download->reply,SIGNAL(downloadProgress(qint64,qint64)),
download,SLOT(onDownloadProgress(qint64,qint64)));
thread->start();
}
- 代码3:logindialog.h
#ifndef LOGINDIALOG_H
#define LOGINDIALOG_H
#include <QDialog>
namespace Ui {
class LoginDialog;
}
class LoginDialog : public QDialog
{
Q_OBJECT
public:
explicit LoginDialog(QWidget *parent = 0);
~LoginDialog();
private slots:
void on_buttonBox_accepted();
void on_buttonBox_rejected();
public:
const QString& getUsername();
const QString& getPassword();
private:
Ui::LoginDialog *ui;
QString username;
QString password;
};
#endif
- 代码4:logindialog.cpp
#include "logindialog.h"
#include "ui_logindialog.h"
LoginDialog::LoginDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::LoginDialog)
{
ui->setupUi(this);
}
LoginDialog::~LoginDialog()
{
delete ui;
}
void LoginDialog::on_buttonBox_accepted()
{
username = ui->usernameEdit->text();
password = ui->passwordEdit->text();
accept();
}
void LoginDialog::on_buttonBox_rejected()
{
reject();
}
const QString& LoginDialog::getUsername()
{
return username;
}
const QString& LoginDialog::getPassword()
{
return password;
}
- 代码5:download.h
#ifndef Download_H
#define Download_H
#include <QThread>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <mainwindow.h>
class DownloadFile : public QObject
{
Q_OBJECT
public:
DownloadFile(const QUrl& url);
~DownloadFile();
private slots:
void ReceiveFile();
void onDownloadProgress(qint64,qint64);
void ReceiveFileFinished();
signals:
void downloadFinished();
private:
QNetworkRequest request;
QNetworkReply* reply;
QUrl fileUrl;
QFile* file;
friend class MainWindow;
};
#endif
- 代码6:download.cpp
#include "download.h"
#include "mainwindow.h"
DownloadFile::DownloadFile(const QUrl& url):fileUrl(url){
QFileInfo fileInfo =QFileInfo(fileUrl.path());
QString filename = fileInfo.fileName();
QDir dir = QDir::home();
if(dir.exists("Downloads")==false){
if(dir.mkdir("Downloads") == false){
qDebug("创建目录失败");
return;
}
}
QString path = QDir::homePath() + "/Downloads/"+ filename;
file = new QFile(path);
file->open(QIODevice::WriteOnly);
}
DownloadFile::~DownloadFile()
{
}
void DownloadFile::ReceiveFile()
{
if(reply->bytesAvailable()){
file->write(reply->readAll());
}
}
void DownloadFile::onDownloadProgress(
qint64 readBytes,qint64 totalBytes)
{
qint64 progress=readBytes*100/totalBytes;
qDebug() << file->fileName() << ":" << progress << "%....";
}
void DownloadFile::ReceiveFileFinished()
{
qDebug() << file->fileName() << "文件下载完成";
file->flush();
file->close();
delete file;
reply->deleteLater();
emit downloadFinished();
}
|