代码下载器HTTP客户端
要求:
实现从"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 选择
默认是打开的。只能加载文档,加载不了网页。关闭后自己处理就可以了,
- 布局
这里选择垂直布局->报存
四、头文件添加相关的类库和成员变量
- 添加头文件类库
//QT +=network
#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; //管理HTTP通信的请求和响应
QNetworkRequest request; //请求
QNetworkReply *reply; //响应
QUrl currentUrl; //记录当前URL地址
QByteArray buf; //接收缓冲区:接收服务器返回的影响数据
};
五、源文件添加相应的槽函数
- 头文件中按F4跳转到源文件
//向服务器发送请求
void MainWindow::sendRequest(){}
//处理登录认证的槽函数
void MainWindow::onAuthenticationRequired(QNetworkReply*,QAuthenticator*){}
//接收响应数据的槽函数
void MainWindow::onReadyRead(){}
//接收响应数据的槽函数
void MainWindow::onFinished(){}
六、代码编写
1. 构建函数创建完整对象
//创建管理通信的Manger对象
manager = new QNetworkAccessManager(this);
//初始化请求URL
request.setUrl(QUrl("http://code.tarena.com.cn/"));
//发送请求
sendRequest();
2. 编写发送请求sendRequest
//向服务器发送请求
void MainWindow::sendRequest(){
//通过manager对象,使用GET方法向服务器发送请求,返回用于接收响应数据的reply对象指针
reply = manager->get(request);
//如果请求的URL,对应服务器需要登录认证,发送认证信号
connect(manager,SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
this,SLOT(onAuthenticationRequired(QNetworkReply*,QAuthenticator*)));
//当服务器返回响应数据,reply将会发送readyRead信号,连接到接收响应的槽函数
connect(reply,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
//当接收响应数据完成,reply将会发送finished信号,连接接收响应数据结束的槽函数
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(); //退出登录窗口的事件循环,返回QDialog::Accepted accept(); } //取消按钮对应的槽函数 void LoginDialog::on_buttonBox_rejected() { //退出登录窗口的事件循环,返回QDialog::Accepted reject(); } //获取用户名 const QString& LoginDialog::getUsername(){ return m_username; } //获取密码 const QString& LoginDialog::getPassword(){ return m_password; }
- 回到主页面代码添加头文件和实现登录槽函数
//处理登录认证的槽函数 void MainWindow::onAuthenticationRequired(QNetworkReply*,QAuthenticator* a){ //创建登录认证的窗口 LoginDialog login(this); //显示登录子窗口并进入事件循环,点击上面OK或者Cancel都会退出事件循环,但是返回值不同 //如果点击Ok验证循环,点击Cancel不验证 if(login.exec() == QDialog::Accepted){ a->setUser(login.getUsername()); a->setPassword(login.getPassword()); } }
-
4. 接收响应数据的槽函数
//接收响应数据的槽函数
void MainWindow::onReadyRead(){
//接收响应数据并保存
buf +=reply->readAll();
//记录当前URL地址
currentUrl = reply->url();
}
5. 接收响应数据的槽函数
//接收响应数据的槽函数
void MainWindow::onFinished(){
//显示
ui->textBrowser->setText(buf);
//清空buf
buf.clear();
//销毁reply对象
reply->deleteLater(); //delete this;
}
七、运行测试
八、完善功能
1. MainWindow.h 头文件添加处理目录槽函数
//处理目录链接的槽函数,参数为点击链接url地址
void onAnchorclicked(const QUrl&url);
2.mainwindow.cpp修改构造函数
//当接收响应数据完成,reply将会发送finished信号,连接接收响应数据结束的槽函数
connect(reply,SIGNAL(finished()),this,SLOT(onFinished()));
3.添加相关槽函数
//处理目录链接的槽函数,参数为点击链接url地址
void MainWindow::onAnchorclicked(const QUrl & url){
qDebug()<<"当前URL:"<<currentUrl.toString();
qDebug() << "点击url:"<<url.toString();
QUrl newUrl; //要进入新的链接URL地址
//如果点击时不是“../”
if(url.toString() !="../"){
//newUrl = 当前URL+点击URL
newUrl = currentUrl.toString()+url.toString();
}else{//处理../
//如果当前在界面,点击../什么也不做
if(currentUrl.toString() == "http://code.tarena.com.cn/"){
return;
}
//如果不在首界面,点击“../”去掉最后一级链接路径
//当前URL:http://code.tarena.com.cn/CSDCode/csd2002
//点击 ../ :http://code.tarena.com.cn/CSDCode/
//查找倒数第二次出现 "/"位置
int pos = currentUrl.toString().lastIndexOf("/",-2);
//字符串截断,截取到pos所在位置
newUrl = currentUrl.toString().mid(0,pos+1);
}
//设置请求为新的url的
request.setUrl(newUrl);
//发送新的请求
sendRequest();
}
4.是否为文件做判断下载逻辑
- 回到源文件添加槽函数
public slots:
//向服务器发送请求
void sendRequest();
//处理登录认证的槽函数
void onAuthenticationRequired(QNetworkReply*,QAuthenticator*);
//接收响应数据的槽函数
void onReadyRead();
//接收响应数据的槽函数
void onFinished();
//处理目录链接的槽函数,参数为点击链接url地址
void onAnchorclicked(const QUrl&url);
//下载文件操作
void downloadFile(const QUrl& url);
//接收文件内容的槽函数
void receiveFile();
//打印当前下载进度槽函数,参数:已收到字节数/总字节数
void onDownloadProgress(qint64,qint64);
//接收文件内容结束执行的槽函数
void receiveFileFinished();
private:
Ui::MainWindow *ui;
QNetworkAccessManager *manager; //管理HTTP通信的请求和响应
QNetworkRequest request; //请求
QNetworkReply *reply; //响应
QUrl currentUrl; //记录当前URL地址
QByteArray buf; //接收缓冲区:接收服务器返回的影响数据
QFile* file; //保存要下载的文件
};
- 槽函数编写
//处理目录链接的槽函数,参数为点击链接url地址
void MainWindow::onAnchorclicked(const QUrl & url){
// qDebug()<<"当前URL:"<<currentUrl.toString();
// qDebug() << "点击url:"<<url.toString();
QUrl newUrl; //要进入新的链接URL地址
//如果点击时不是“../”
if(url.toString() !="../"){
//newUrl = 当前URL+点击URL
newUrl = currentUrl.toString()+url.toString();
}else{//处理../
//如果当前在界面,点击../什么也不做
if(currentUrl.toString() == "http://code.tarena.com.cn/"){
return;
}
//如果不在首界面,点击“../”去掉最后一级链接路径
//当前URL:http://code.tarena.com.cn/CSDCode/csd2002
//点击 ../ :http://code.tarena.com.cn/CSDCode/
//查找倒数第二次出现 "/"位置
int pos = currentUrl.toString().lastIndexOf("/",-2);
//字符串截断,截取到pos所在位置
newUrl = currentUrl.toString().mid(0,pos+1);
}
//判断newUrl是否为文件链接,如果是文件链接,则执行下载文件操作
//判断方式:如果点击url不是目录链接,就是要下载的文件链接
if(url.toString().lastIndexOf("/") ==-1){
//qDebug()<<"下载文件操作:"<<newUrl;
downloadFile(newUrl);
}else{
//设置请求为新的url的
request.setUrl(newUrl);
//发送新的请求
sendRequest();
}
}
//下载文件
void MainWindow::downloadFile(const QUrl& fileUrl){
//根据获取fileUrl获取文件名
QFileInfo fileInfo = fileUrl.path();
QString filename = fileInfo.fileName();
//在本地创建同名的文件
file = new QFile(filename,this);
//打开本地同名文件,如果不存在将会自动创建
file->open(QIODevice::WriteOnly);
//设置请求URL为下载文件的URL
request.setUrl(fileUrl);
//发送下载文件连接请求
reply = manager->get(request);
//当收到服务器返回的响应数据(文件内容),发送信号readRead(),连接到接收文件内容的槽函数
connect(reply,SIGNAL(readyRead()),this,SLOT(receiveFile()));
//伴随文件下载REPLY会发送下载进度信号downloadProgress,连接到打印当前下载进度槽函数
connect(reply,SIGNAL(downloadProgress(qint64,qint64)),this,SLOT(onDownloadProgress(qint64,qint64)));
//文件下载完成,发送信号finished,连接到接收文件内容结束执行的槽函数
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>
//QT +=network
#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();
//处理目录链接的槽函数,参数为点击链接url地址
void onAnchorclicked(const QUrl&url);
//下载文件操作
void downloadFile(const QUrl& fileUrl);
//接收文件内容的槽函数
void receiveFile();
//打印当前下载进度槽函数,参数:已收到字节数/总字节数
void onDownloadProgress(qint64,qint64);
//接收文件内容结束执行的槽函数
void receiveFileFinished();
private:
Ui::MainWindow *ui;
QNetworkAccessManager *manager; //管理HTTP通信的请求和响应
QNetworkRequest request; //请求
QNetworkReply *reply; //响应
QUrl currentUrl; //记录当前URL地址
QByteArray buf; //接收缓冲区:接收服务器返回的影响数据
QFile* file; //保存要下载的文件
};
#endif // MAINWINDOW_H
- 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);
//创建管理通信的Manger对象
manager = new QNetworkAccessManager(this);
//初始化请求URL
request.setUrl(QUrl("http://code.tarena.com.cn/"));
//发送请求
sendRequest();
//点击界面的链接时,发送信号anchorclicked,
connect(ui->textBrowser,SIGNAL(anchorClicked(QUrl)),this,SLOT(onAnchorclicked(QUrl)));
}
MainWindow::~MainWindow()
{
delete ui;
}
//向服务器发送请求
void MainWindow::sendRequest(){
//通过manager对象,使用GET方法向服务器发送请求,返回用于接收响应数据的reply对象指针
reply = manager->get(request);
//如果请求的URL,对应服务器需要登录认证,发送认证信号
connect(manager,SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
this,SLOT(onAuthenticationRequired(QNetworkReply*,QAuthenticator*)));
//当服务器返回响应数据,reply将会发送readyRead信号,连接到接收响应的槽函数
connect(reply,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
//当接收响应数据完成,reply将会发送finished信号,连接接收响应数据结束的槽函数
connect(reply,SIGNAL(finished()),this,SLOT(onFinished()));
}
//处理登录认证的槽函数
void MainWindow::onAuthenticationRequired(QNetworkReply*,QAuthenticator* a){
//创建登录认证的窗口
LoginDialog login(this);
//显示登录子窗口并进入事件循环,点击上面OK或者Cancel都会退出事件循环,但是返回值不同
//如果点击Ok验证循环,点击Cancel不验证
if(login.exec() == QDialog::Accepted){
a->setUser(login.getUsername());
a->setPassword(login.getPassword());
}
}
//接收响应数据的槽函数
void MainWindow::onReadyRead(){
//接收响应数据并保存
buf +=reply->readAll();
//记录当前URL地址
currentUrl = reply->url();
}
//接收响应数据的槽函数
void MainWindow::onFinished(){
//显示
ui->textBrowser->setText(buf);
//清空buf
buf.clear();
//销毁reply对象
reply->deleteLater(); //delete this;
}
//处理目录链接的槽函数,参数为点击链接url地址
void MainWindow::onAnchorclicked(const QUrl & url){
// qDebug()<<"当前URL:"<<currentUrl.toString();
// qDebug() << "点击url:"<<url.toString();
QUrl newUrl; //要进入新的链接URL地址
//如果点击时不是“../”
if(url.toString() !="../"){
//newUrl = 当前URL+点击URL
newUrl = currentUrl.toString()+url.toString();
}else{//处理../
//如果当前在界面,点击../什么也不做
if(currentUrl.toString() == "http://code.tarena.com.cn/"){
return;
}
//如果不在首界面,点击“../”去掉最后一级链接路径
//当前URL:http://code.tarena.com.cn/CSDCode/csd2002
//点击 ../ :http://code.tarena.com.cn/CSDCode/
//查找倒数第二次出现 "/"位置
int pos = currentUrl.toString().lastIndexOf("/",-2);
//字符串截断,截取到pos所在位置
newUrl = currentUrl.toString().mid(0,pos+1);
}
//判断newUrl是否为文件链接,如果是文件链接,则执行下载文件操作
//判断方式:如果点击url不是目录链接,就是要下载的文件链接
if(url.toString().lastIndexOf("/") ==-1){
//qDebug()<<"下载文件操作:"<<newUrl;
downloadFile(newUrl);
}else{
//设置请求为新的url的
request.setUrl(newUrl);
//发送新的请求
sendRequest();
}
}
//下载文件
void MainWindow::downloadFile(const QUrl& fileUrl){
//根据获取fileUrl获取文件名
QFileInfo fileInfo = fileUrl.path();
QString filename = fileInfo.fileName();
//在本地创建同名的文件
file = new QFile(filename,this);
//打开本地同名文件,如果不存在将会自动创建
file->open(QIODevice::WriteOnly);
//设置请求URL为下载文件的URL
request.setUrl(fileUrl);
//发送下载文件连接请求
reply = manager->get(request);
//当收到服务器返回的响应数据(文件内容),发送信号readRead(),连接到接收文件内容的槽函数
connect(reply,SIGNAL(readyRead()),this,SLOT(receiveFile()));
//伴随文件下载REPLY会发送下载进度信号downloadProgress,连接到打印当前下载进度槽函数
connect(reply,SIGNAL(downloadProgress(qint64,qint64)),this,SLOT(onDownloadProgress(qint64,qint64)));
//文件下载完成,发送信号finished,连接到接收文件内容结束执行的槽函数
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_H
- 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();
//退出登录窗口的事件循环,返回QDialog::Accepted
accept();
}
//取消按钮对应的槽函数
void LoginDialog::on_buttonBox_rejected()
{
//退出登录窗口的事件循环,返回QDialog::Accepted
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;//记录当前的Url地址
QByteArray buf;//保存接收的响应数据
};
#endif // MAINWINDOW_H
- 代码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对象
manager = new QNetworkAccessManager(this);
//初始化请求网址
request.setUrl(QUrl("http://code.tarena.com.cn/"));
//向服务器发送请求
sendRequest();
//点击界面的链接时,发送信号anchorClicked,
//参数表示点击链接的URL地址
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);
//如果服务器需要进行登录认证,manager会发送认证
//信号:authenticationRequired
connect(manager,SIGNAL(
authenticationRequired(
QNetworkReply*,QAuthenticator*)),
this,SLOT(onAuthenticationRequired(
QNetworkReply*,QAuthenticator*)));
//如果认证成功,响应数据到来,发送信号readyRead
connect(reply,SIGNAL(readyRead()),
this,SLOT(onReadyRead()));
//响应数据接收结束,发送信号finished
connect(reply,SIGNAL(finished()),
this,SLOT(onFinished()));
}
//处理登录认证的槽函数
void MainWindow::onAuthenticationRequired(
QNetworkReply*,QAuthenticator* authenticator)
{
//qDebug("onAuthenticationRequired");
//从登录认证子窗口中,获取用户名和密码在进行认证
LoginDialog login(this);
//显示登录窗口,并进入事件循环,点击上面Ok/Cancel
//时都会退出登录窗口,但是返回值不同.
//如果点击Ok按钮退出,返回QDialog::Accepted
if(login.exec() == QDialog::Accepted){
authenticator->setUser(login.getUsername());
authenticator->setPassword(login.getPassword());
}
}
//接收响应数据的槽函数
void MainWindow::onReadyRead()
{
//qDebug("onReadyRead");
//读取响应数据,并保存
buf += reply->readAll();
//保存当前URl地址
currentUrl = reply->url();
}
//接收响应数据完成时执行的槽函数
void MainWindow::onFinished()
{
//qDebug("onFinished");
//显示响应数据
ui->textBrowser->setText(buf);
//清空buf
buf.clear();
//销毁reply对象
reply->deleteLater();
//恢复界面
ui->textBrowser->setEnabled(true);
}
//处理目录链接的槽函数
void MainWindow::onAnchorClicked(
const QUrl &url){
//qDebug()<<"当前的URL:"<<currentUrl.toString();
//qDebug()<<"点击的URL:"<<url.toString();
QUrl newUrl;
//如果点击是不是"../",新的URL=当前URL+点击URL
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);
}
//判断点击URL如果不是目录则执行文件下载操作.
if(url.toString().lastIndexOf("/")==-1){
downloadFile(newUrl);
return;
}
//设置新的请求URL
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()));
//设置下载文件请求的URL
download->request.setUrl(fileUrl);
//发送获取下载文件的请求
download->reply = manager->get(download->request);
//响应数据到来,在子线程中完成下载操作
//download已经移动到子线程中,通过信号触发里面槽将在子线程中运行
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:
//Ok按钮对应的槽函数
void on_buttonBox_accepted();
//Cancel按钮对应的槽函数
void on_buttonBox_rejected();
public:
//获取用户名
const QString& getUsername();
//获取密码
const QString& getPassword();
private:
Ui::LoginDialog *ui;
QString username;//保存用户名
QString password;//保存密码
};
#endif // LOGINDIALOG_H
- 代码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;
}
//Ok按钮对应的槽函数
void LoginDialog::on_buttonBox_accepted()
{
username = ui->usernameEdit->text();
password = ui->passwordEdit->text();
accept();//退出,返回QDialog::Accepted
}
//Cancel按钮对应的槽函数
void LoginDialog::on_buttonBox_rejected()
{
reject();//退出,返回QDialog::Rejected
}
//获取用户名
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;//下载文件的URL地址
QFile* file;
//将MainWindow类声明为当前类的友元,友元类可以访问当前类的任何成员
friend class MainWindow;
};
#endif // Download_H
- 代码6:download.cpp
#include "download.h"
#include "mainwindow.h"
DownloadFile::DownloadFile(const QUrl& url):fileUrl(url){
//根据URL获取文件名
QFileInfo fileInfo =QFileInfo(fileUrl.path());
QString filename = fileInfo.fileName();
//设置要下载文件的路径
QDir dir = QDir::home();
//判断是否存在Download目录
if(dir.exists("Downloads")==false){
//如果该目录不存在则创建
if(dir.mkdir("Downloads") == false){
qDebug("创建目录失败");
return;
}
}
//指定下载文件放到主目录的Download下面
QString path = QDir::homePath() + "/Downloads/"+ filename;
//在本地创建同名的文件
file = new QFile(path);
//以写的方式打开文件
file->open(QIODevice::WriteOnly);
}
DownloadFile::~DownloadFile()
{
//qDebug() << "~Download";
}
//接收文件的槽函数
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();//发送信号,表示文件下载完成
}