在一个下载程序的开发中,我实现了一个CHttpProvider类用于提供http请求和接收下载数据。先通过GetFileInfo获取远程文件的数据。主线程在获取远程文件数据取,根据文件大小进行分段,然后启动多个下载线程对每段下载。每个线程都会有一个此类的新对象。
结果是主线程式不能正确的获取文件信息,QNetworkAccessManager的head函数后,没有finished(QNetworkReply *)返回。
我考虑,是不是有可能是线程的消息循环的问题,让我无法接受此信号。或者是其它问题,请求帮助!
#include <QHttp>
#include <QNetworkReply>
#include <QNetworkAccessManager>
//#include <QHttpRequestHeader>
//#include <QHttpResponseHeader>
#include <QByteArray>
#include <QWaitCondition>
#include <QMutex>
#include <QPointer>
#include "providerbase.h"
#include "mirror.h"
#include "httpdef.h"
/**
* \class
* \brief send http request and recive response for downloading.
*/
class CHttpProvider :
public QObject,
public CProviderBase
{
Q_OBJECT
private:
QPointer<QNetworkReply> _reply;
QPointer<QNetworkAccessManager> _manager;
QIODevice *_dest;
CRemoteFileInfo *_fileInfo;
QString _contentRange;
bool _isHeadReq;
QWaitCondition _condReq;
QMutex _mutexReq;
public:
CHttpProvider();
virtual bool getFileInfo(CMirror *mirror, CRemoteFileInfo *info);
virtual void setProviderRange(int start, int end, int length);
virtual bool startTrans(CMirror *mirror, QIODevice *dest);
virtual bool stopTrans();
virtual CProviderBase *createNew();
virtual ~CHttpProvider();
private:
bool headRequest(CMirror *mirror = 0);
bool downloadRequest(CMirror *mirror = 0, QIODevice *dest = 0);
public slots:
void onFinished();
void onError(QNetworkReply::NetworkError code);
void onDownloadProgress(qint64 current, qint64 total);
void onReadyRead();
void onReplyFinished(QNetworkReply *reply);
};
#include <QMessageBox>
CHttpProvider::CHttpProvider()
{
_manager = new QNetworkAccessManager();
//used to remark head request.
_isHeadReq = false;
connect(_manager, SIGNAL(finished(QNetworkReply *)),
this, SLOT(onReplyFinished(QNetworkReply *)));
}
CHttpProvider::~CHttpProvider()
{
if (!_reply.isNull())
_reply->close();
if (!_reply.isNull())
_reply->abort();
if (!_reply.isNull())
_reply->disconnect();
if (!_reply.isNull())
_reply->deleteLater();
if (!_manager.isNull())
_manager->deleteLater();
}
bool CHttpProvider::headRequest(CMirror *mirror)
{
bool ret = false;
_manager->head(QNetworkRequest(*(mirror->getUrl())));
_isHeadReq = true;
_mutexReq.lock();
if (_condReq.wait(&_mutexReq, HTTP_REQUEST_TIMEOUT))
ret = true;
else
ret = false;
_mutexReq.unlock();
return ret;
}
bool CHttpProvider::downloadRequest(CMirror *mirror, QIODevice *dest)
{
bool ret = false;
this->_dest = dest;
if (_dest->open(QIODevice::QIODevice::Append))
{
QNetworkRequest getR(*(mirror->getUrl()));
getR.setRawHeader(QByteArray("Range"), _contentRange.toUtf8());
_manager->get(getR);
_isHeadReq = false;
_mutexReq.lock();
if (_condReq.wait(&_mutexReq, 20000))
ret = true;
else
ret = false;
_mutexReq.unlock();
}
else
{
#ifdef _DEBUG
CTrace::TraceQ.trace("dest device is failed to open");
#endif
}
return ret;
}
/**
* \fn
* \brief get remote file information, make a head request and wait result.
* - inherated from CProviderBase.
*/
bool CHttpProvider::getFileInfo(CMirror *mirror, CRemoteFileInfo *info)
{
_fileInfo = info;
#ifdef _DEBUG
CTrace::TraceQ.trace("In GetFileInfo function of http provider to remote %s", qPrintable(mirror->getUrl()->toString()));
#endif
if (headRequest(mirror))
{
_fileInfo->FileSize = _reply->header(QNetworkRequest::ContentLengthHeader).toInt();
_fileInfo->MimeType = _reply->header(QNetworkRequest::ContentTypeHeader).toString();
_fileInfo->AcceptRanges = true;
#ifdef _DEBUG
CTrace::TraceQ.trace("FileInfo as follows: FileSize %d, MimeType %s, AcceptRanges %s",
_fileInfo->FileSize, qPrintable(_fileInfo->MimeType), _fileInfo->AcceptRanges ? "true" : "false");
#endif
onFinished();
return true;
}
return false;
}
bool CHttpProvider::startTrans(CMirror *mirror, QIODevice *dest)
{
// _getId = 0;
#ifdef _DEBUG
CTrace::TraceQ.trace("In StartTrans function of http provider to remote %s", qPrintable(mirror->getUrl()->toString()));
#endif
return downloadRequest(mirror, dest);
}
bool CHttpProvider::stopTrans()
{
_reply->close();
return true;
}
void CHttpProvider::setProviderRange(int start, int end, int length)
{
_contentRange = QString(tr("%1=%2-%3"))
.arg(tr("bytes"))
.arg(start)
.arg(end);
#ifdef _DEBUG
CTrace::TraceQ.trace("Current range is set to %s", qPrintable(_contentRange));
#endif
}
CProviderBase *CHttpProvider::createNew()
{
CHttpProvider *provider = new CHttpProvider();
return provider;
}
void CHttpProvider::onReplyFinished(QNetworkReply *reply)
{
this->_reply = reply;
if (!_isHeadReq)
{
connect(_reply, SIGNAL(downloadProgress(qint64, qint64)),
this, SLOT(onDownloadProgress(qint64, qint64)));
connect(_reply, SIGNAL(finished()),
this, SLOT(onFinished()), Qt::DirectConnection);
connect(_reply, SIGNAL(error(QNetworkReply::NetworkError )),
this, SLOT(onError(QNetworkReply::NetworkError)));
connect(_reply, SIGNAL(readyRead()),
this, SLOT(onReadyRead()));
}
_condReq.wakeAll();
}
void CHttpProvider::onFinished()
{
if (!_reply.isNull())
{
_reply->abort();
_reply->disconnect();
_reply->deleteLater();
}
if (_dest != NULL)
{
_dest->close();
}
#ifdef _DEBUG
if (_isHeadReq)
CTrace::TraceQ.trace("HEAD request finished!");
else
CTrace::TraceQ.trace("GET request finished!");
#endif
if (!_isHeadReq && _input != NULL)
{
_input->onReadComplete(false);
}
}
void CHttpProvider::onDownloadProgress(qint64 current, qint64 total)
{
#ifdef _DEBUG
CTrace::TraceQ.trace("downloading progress, current %d total %d", current, total);
#endif
}
void CHttpProvider::onError(QNetworkReply::NetworkError code)
{
#ifdef _DEBUG
if (_isHeadReq)
CTrace::TraceQ.trace("HEAD request error %d", code);
else
CTrace::TraceQ.trace("GET request error %d", code);
#endif
_input->onReadComplete(true);
}
void CHttpProvider::onReadyRead()
{
QByteArray buff = _reply->readAll();
int size = buff.length();
_dest->write(buff);
#ifdef _DEBUG
CTrace::TraceQ.trace("Another %d is downloaded to dest file", size);
#endif
}