Qt实现 Http网络在线下载程序(支持断点续传功能)

目录

一、gitHub地址

二、功能

三、目前存在的问题

四、界面效果

五、主要代码


开发过程中遇到了一些坑,花了半天时间搞。出现了很多理论上不应该出现的bug。

做这个的目的,主要是为了巩固一下对Qt网络部分接口的使用,以及http协议的记忆。

思路主要是,解析URL——>获取响应头——>获取响应状态码——>请求下载——>回读当前文件字节数进行断点续传。

需要注意,访问https前,需要添加libcrypto-1_1.dll与libssl-1_1.dll到Qt\5.14.0\mingw73_32\bin或者exe目录下。

以上DLL下载地址:http://slproweb.com/products/Win32OpenSSL.html

需要注意一下32位和64位,应下载与自己Qt编译器位数一致的版本。下载好后直接安装,然后到安装目录拷贝即可。

一、gitHub地址

https://github.com/KindMans/HttpDownLoad

 

二、功能

支持输入url网址进行在线下载,具备断点续传功能。

后期扩展:结合qml树列表与多线程,模拟出迅雷的下载效果。

 

三、目前存在的问题

QEventLoop *loop = new QEventLoop;
connect(m_netWorkManager, SIGNAL(finished(QNetworkReply*)), loop, SLOT(quit()));
m_reply = m_netWorkManager->get(request);
loop->exec();

通过以上get访问到需要跳转的url时,速度明显快于不需要跳转的。具体原因还不清楚。

 

四、界面效果

粗略的绘制了一下界面,比较丑,主要以实现效果为主。

 

五、主要代码

启动下载:

void Http::startDownLoad(const QString &url)
{
    m_url = url;
    if(m_url.isEmpty()) return;

    if(!m_IsDownloading)
    {
        //获取请求头
        QNetworkRequest request;
        QUrl url = QUrl(m_url);
        request.setUrl(url);
        m_fileName = url.fileName();
        qDebug()<<"fileName = "<<m_fileName;
        m_reply = m_netWorkManager->head(request);
        m_state = requestHead;
        getCurrentFileSize();
        connect(m_reply,SIGNAL(finished()),this,SLOT(onfinishedRequest()));
    }
}

通过setUrl来装载我们请求的地址,随后获取请求头,请求结束后,进入槽函数做处理。

暂停下载:

void Http::stopDownLoad()
{
    if(m_reply == nullptr) return;

    disconnect(m_reply,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
    disconnect(m_reply,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(onError(QNetworkReply::NetworkError)));
    disconnect(m_reply,SIGNAL(finished()),this,SLOT(onfinishedRequest()));

    m_reply->abort();
    m_reply->deleteLater();

    m_file.close();
    getCurrentFileSize();

    m_IsDownloading = false;
}

暂停下载主要是断开信号槽的连接,然后释放资源和关闭文件操作符。

文件大小回读:

void Http::getCurrentFileSize()
{
    QFileInfo fileInfo(m_fileName);
    if(fileInfo.exists())
    {
        m_currentLoadedBytes = fileInfo.size();
    }
    else
    {
        m_currentLoadedBytes = 0;
    }
}

通过QFileInfo类提供的size()接口,获取文件当前的字节大小。

槽函数onfinishedRequest():

void Http::onfinishedRequest()
{
    if(m_reply==nullptr) return;

    if(m_state == requestHead)
    {       
        m_fileSize = m_reply->rawHeader("Content-Length").toInt();
        qDebug()<<"m_fileSize = "<<m_fileSize;
        if(m_currentLoadedBytes == m_fileSize)
        {
            qDebug()<<"文件已经存在!";
            return;
        }

        QEventLoop *loop = new QEventLoop;
        connect(m_netWorkManager, SIGNAL(finished(QNetworkReply*)), loop, SLOT(quit()));
        QNetworkRequest request;
        request.setUrl(m_url);
        m_reply = m_netWorkManager->get(request);
        loop->exec();

        //获取状态码
        m_statusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
        qDebug()<<"statusCode="<<m_statusCode;
        m_state = requestBody;
    }

    QNetworkRequest request;
    if(m_statusCode==200)
    {
        request.setUrl(m_url);
    }
    else if(m_statusCode == 302)    //存在转调url
    {
        //获取实际下载地址
        QUrl realUrl = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
        request.setUrl(realUrl);
    }
    else
    {
        return;
    }

    QString downLoadSize = QString::number(m_fileSize);
    QString selectSize = QString("bytes=%1-%2").arg(m_currentLoadedBytes).arg(downLoadSize);
    request.setRawHeader("Range",selectSize.toLatin1());
    m_reply = m_netWorkManager->get(request);

    connect(m_reply,SIGNAL(finished()),this,SLOT(onfinishedRequest()));
    connect(m_reply,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
    connect(m_reply,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(onError(QNetworkReply::NetworkError)));
}

这里需要注意的是做了一个转调功能,因为有些资源的实际下载地址并不是当前所给的Url。当返回302的时候,说明需要获取背后正确的下载地址。

槽函数onReadyRead():

void Http::onReadyRead()
{
    if(m_reply==nullptr) return;

    if(!m_file.isOpen())
    {
        m_file.setFileName(m_fileName);
        m_file.open(QIODevice::WriteOnly|QIODevice::Append);
    }

    m_file.write(m_reply->readAll());

    m_downLoadedBytes =m_file.size();

    emit fileDownloadProgress(m_downLoadedBytes, m_fileSize);
    if(m_file.size() == m_fileSize)
    {
        qDebug()<<"download finished!";
        stopDownLoad();
    }
}

将接收到的数据,写入文件中,并且将当前下载字节数通过信号槽发送出去,提供给进度条对话框使用。

当下载的字节总大小等于请求时服务器返回过来的文件大小时,就认为下载结束。

当然,更严谨的做法是再计算文件的MD5码,与服务器回传的MD5码进行比较,以确保下载的是正确的文件。

Qt是一个跨平台的C++库,可以用来实现各种类型的应用程序,包括网络应用程序。要实现断点续传服务器,首先需要使用Qt网络模块来创建一个服务器程序,然后在服务器程序实现断点续传的逻辑。 首先,我们需要创建一个QTcpServer对象来监听客户端的连接请求。当有客户端连接上来时,我们可以创建一个QTcpSocket对象来处理和该客户端的通信。 在断点续传服务器中,我们需要实现一个文件传输的功能。当客户端发送请求下载一个文件时,服务器首先需要检查这个文件是否支持断点续传。如果支持断点续传,服务器需要告知客户端支持的范围,并发送文件的部分内容。如果客户端在传输中断开连接,那么服务器需要记录传输的断点,以便客户端下次连接可以继续传输。 在Qt中,我们可以使用QFile和QIODevice来实现文件传输的逻辑。通过QFile可以读取文件内容,而QIODevice可以作为网络数据的传输通道。当服务器接收到客户端的数据时,可以使用QFile来保存文件内容,并且可以通过QIODevice来发送文件内容给客户端。 当客户端下载文件时,我们还可以使用QNetworkReply类来实现断点续传的过程。通过QNetworkReply可以获取服务器的响应信息,并且可以设置客户端的请求范围。 通过Qt网络模块和文件操作类,可以比较方便地实现断点续传服务器。当然,在实际开发中,还需要考虑到数据安全、断点记录和恢复等方面的问题。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值