QT:多线程下载文件

这里的线程是指下载的通道(和操作系统中的线程不一样),一个线程就是一个文件的下载通道,多线程也就是同时开起好几个下载通道.当服务器提供下载服务时,使用下载者是共享带宽的,在优先级相同的情况下,总服务器会对总下载线程进行平均分配。不难理解,如果你线程多的话,那下载的越快。现流行的下载软件都支持多线程。


思路:
1:用阻塞的方式获取目标地址的HTTP头部,得到目标文件的大小。
2:算出每段文件的开始点,结尾点,并分别向目标地址发出请求。
3:每次目标地址有数据返回,都将得到的数据写入文件。

4:等待各段文件下载结果。


运行截图:



源代码:

#include <QtCore>  
#include <QtNetwork>  

//多线程下载的线程数  
const int PointCount = 5;  
//目标文件的地址(千千静听的下载地址,我用来做实验的)  
const QString strUrl = "http://ttplayer.qianqian.com/otherdown/alladin/ttpsetup_5713.exe";  

//用于下载文件(或文件的一部分)  
class Download : public QObject  
{  
	Q_OBJECT  
private:  
	QNetworkAccessManager m_Qnam;  
	QNetworkReply *m_Reply;  
	QFile *m_File;  

	const int m_Index;  
	qint64 m_HaveDoneBytes;  
	qint64 m_StartPoint;  
	qint64 m_EndPoint;  

public:  
	Download(int index, QObject *parent = 0);  
	void StartDownload(const QUrl &url, QFile *file,   
		qint64 startPoint=0, qint64 endPoint=-1);   
signals:  
	void DownloadFinished();  

	public slots:  
		void FinishedSlot();  
		void HttpReadyRead();  
};  

//用于管理文件的下载  
class DownloadControl : public QObject  
{  
	Q_OBJECT  
private:  
	int m_DownloadCount;  
	int m_FinishedNum;  
	int m_FileSize;  
	QUrl m_Url;  
	QFile *m_File;  
public:  
	DownloadControl(QObject *parent = 0);  
	void StartFileDownload(const QString &url, int count);  
	qint64 GetFileSize(QUrl url);  
signals:  
	void FileDownloadFinished();  
	private slots:  
		void SubPartFinished();  
};  

Download::Download(int index, QObject *parent)  
: QObject(parent), m_Index(index)  
{  
	m_HaveDoneBytes = 0;  
	m_StartPoint = 0;  
	m_EndPoint = 0;  
	m_File = NULL;  
}  

void Download::StartDownload(const QUrl &url,   
							 QFile *file,  
							 qint64 startPoint/* =0 */,  
							 qint64 endPoint/* =-1 */)  
{  
	if( NULL == file )  
		return;  

	m_HaveDoneBytes = 0;  
	m_StartPoint = startPoint;  
	m_EndPoint = endPoint;  
	m_File = file;  

	//根据HTTP协议,写入RANGE头部,说明请求文件的范围  
	QNetworkRequest qheader;  
	qheader.setUrl(url);  
	QString range;  
	range.sprintf("Bytes=%lld-%lld", m_StartPoint, m_EndPoint);  
	qheader.setRawHeader("Range", range.toAscii());  

	//开始下载  
	qDebug() << "Part " << m_Index << " start download";  
	m_Reply = m_Qnam.get(QNetworkRequest(qheader));  
	connect(m_Reply, SIGNAL(finished()),  
		this, SLOT(FinishedSlot()));  
	connect(m_Reply, SIGNAL(readyRead()),  
		this, SLOT(HttpReadyRead()));  
}  

//下载结束  
void Download::FinishedSlot()  
{  
	m_File->flush();  
	m_Reply->deleteLater();  
	m_Reply = 0;  
	m_File = 0;  
	qDebug() << "Part " << m_Index << " download finished";  
	emit DownloadFinished();  
}  

void Download::HttpReadyRead()  
{  
	if ( !m_File )  
		return;  

	//将读到的信息写入文件  
	QByteArray buffer = m_Reply->readAll();  
	m_File->seek( m_StartPoint + m_HaveDoneBytes );  
	m_File->write(buffer);  
	m_HaveDoneBytes += buffer.size();  
}  

//用阻塞的方式获取下载文件的长度  
qint64 DownloadControl::GetFileSize(QUrl url)  
{  
	QNetworkAccessManager manager;  
	qDebug() << "Getting the file size...";  
	QEventLoop loop;  
	//发出请求,获取目标地址的头部信息  
	QNetworkReply *reply = manager.head(QNetworkRequest(url));  
	QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()), Qt::DirectConnection);  
	loop.exec();  
	QVariant var = reply->header(QNetworkRequest::ContentLengthHeader);  
	reply->deleteLater(); 
	qint64 size = var.toLongLong();  
	qDebug() << "The file size is: " << size;  
	return size;  
}  

DownloadControl::DownloadControl(QObject *parent)  
: QObject(parent)  
{  
	m_DownloadCount = 0;  
	m_FinishedNum = 0;  
	m_FileSize = 0;  
	m_File = new QFile;  
}  

void DownloadControl::StartFileDownload(const QString &url, int count)  
{  
	m_DownloadCount = count;  
	m_FinishedNum = 0;  
	m_Url = QUrl(url);  
	m_FileSize = GetFileSize(m_Url);  
	//先获得文件的名字  
	QFileInfo fileInfo(m_Url.path());  
	QString fileName = fileInfo.fileName();  
	if (fileName.isEmpty())  
		fileName = "index.html";  

	m_File->setFileName(fileName);  
	//打开文件  
	m_File->open(QIODevice::WriteOnly);  
	Download *tempDownload;  

	//将文件分成PointCount段,用异步的方式下载  
	qDebug() << "Start download file from " << strUrl;  
	for(int i=0; i<m_DownloadCount; i++)  
	{  
		//先算出每段的开头和结尾(HTTP协议所需要的信息)  
		int start = m_FileSize * i / m_DownloadCount;  
		int end = m_FileSize * (i+1) / m_DownloadCount;  
		if( i != 0 )  
			start--;  

		//分段下载该文件  
		tempDownload = new Download(i+1, this);  
		connect(tempDownload, SIGNAL(DownloadFinished()),   
			this, SLOT(SubPartFinished()));  
		connect(tempDownload, SIGNAL(DownloadFinished()),   
			tempDownload, SLOT(deleteLater()));  
		tempDownload->StartDownload(m_Url, m_File, start, end);  
	}  
}  

void DownloadControl::SubPartFinished()  
{  
	m_FinishedNum++;  
	//如果完成数等于文件段数,则说明文件下载完毕,关闭文件,发生信号  
	if( m_FinishedNum == m_DownloadCount )  
	{  
		m_File->close();  
		emit FileDownloadFinished();  
		qDebug() << "Download finished";  
	}  
}  

#include "main.moc"  

int main(int argc, char **argv)  
{  
	QCoreApplication app(argc, argv);  
	//用阻塞的方式下载文件,完成后退出  
	DownloadControl *control = new DownloadControl;  
	QEventLoop loop;  
	QObject::connect(control, SIGNAL(FileDownloadFinished()),   
		&loop, SLOT(quit()));  
	control->StartFileDownload(strUrl, PointCount);  
	loop.exec();  
	control->deleteLater();
	return 0;  
}  


  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 19
    评论
Qt HTTP库是Qt框架中的一个模块,用于处理HTTP和HTTPS请求,常用于网络通信和数据下载。要在Qt中使用它进行多线程下载三个PDF文件,你可以按照以下步骤操作: 1. **设置环境**: 首先,确保你已经在项目中包含了Qt的网络模块(QtNetwork)。 2. **创建下载任务**: 对每个PDF文件,定义一个单独的下载任务类,继承自`QThread`。这个类通常会包含一个私有成员变量保存URL,以及开始下载的方法。 ```cpp class DownloadTask : public QThread { QUrl url; QTemporaryFile file; public: DownloadTask(const QUrl& url, QObject* parent = nullptr) : QThread(parent), url(url) {} void run() override; }; ``` 3. **开始下载**: 在主线程中,创建并启动这些下载任务。每个任务的`run()`方法会在子线程中执行,避免阻塞主线程。 ```cpp DownloadTask* task1 = new DownloadTask(QUrl("PDF1_URL"), this); task1->start(); // 同样为其他两个PDF文件创建和启动任务 DownloadTask* task2 = new DownloadTask(QUrl("PDF2_URL"), this); task2->start(); // ... (重复类似代码为PDF3) connect(task1, &DownloadTask::finished, []() { qDebug() << "PDF1 downloaded"; }); // 连接其他任务的finished信号 ``` 4. **处理下载完成**: 使用`finished`信号连接到一个槽函数,当下载任务完成后,你可以检查文件是否成功下载,并进行后续操作,如保存到本地或显示进度。 5. **错误处理**: 为了增强健壮性,记得处理可能发生的网络错误、文件写入错误等。 6. **下载管理**: 如果你想控制下载顺序或者有并发限制,可以考虑使用`QSemaphore`或者其他并发工具。 7. **下载进度更新**: 如果需要实时更新下载进度,可以在`run()`方法中添加进度更新的逻辑,然后使用`emit progressChanged(int percentage)`来发送进度更新信号。
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值