ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

http 下载类封装

2021-09-07 12:00:10  阅读:94  来源: 互联网

标签:task 封装 url void QString http size 下载 const


/************************************************************************
*时间:2021/06/29
*类介绍:http 下载类        
************************************************************************/
#ifndef DOWNLOADER_H
#define DOWNLOADER_H

#include <string>
#include <queue>

#include <QFile>
#include <QMutex>
#include <QNetworkReply>
#include <QThread>

/** \class DataReceiver
 *
 *   数据接收者抽象基类
 */
class DataReceiver {

public:

    virtual ~DataReceiver();

    enum DataMode {

        DM_MEMORY = 0,		///< 下载到内存
        DM_FILE,		///< 下载到文件
    };

public:

    virtual const std::string &getLocalDataPath(void) const = 0;
    virtual void receiveData(QByteArray data) = 0;
    void setMode(DataMode mode);
    DataMode mode(void) const;

protected:

    DataMode _dataMode = DM_FILE;
};

/** class NetFile
 *
 *  远程文件对象
 */
class NetFile : public DataReceiver {

public:

	NetFile();
	NetFile(const QString &name, const QString path="", DataMode mode = DM_FILE);
	virtual ~NetFile();

	const std::string &getLocalDataPath(void) const override;
	void receiveData(QByteArray data);
	const QByteArray getData(void);
	QString getName(void);
	QString getPath(void);;
	void close(void);

protected:
	QByteArray _data;
	QFile _file;
	QString _name;
	QString _path;
};


/** class DataDownloadWorker
 *
 *   下载器工作线程
 */
class DataDownloadWorker : public QThread {

    Q_OBJECT

public:

    typedef struct {
        std::string _url;
        DataReceiver *_receiver;
        bool _ignore;
    } Task;

public:

    DataDownloadWorker();
    ~DataDownloadWorker();

public:
    /** fn Download
     *
     *    从指定URL下载文件,将数据存入DataReceiver对象
     *
     *  param url   待下载文件的远程路径
     *  param receiver  一个用于接收数据的DataReceiver对象
     */
    void DownLoadTo(const std::string &url, DataReceiver *receiver);

    bool isIgnorable(const std::string &url);

    /** fn removeTask
     *
     *    从下载队列删除任务
     *
     *  param url 下载队列中的任务远程URL
     *
     *  remark 如果数据已经全部下载成功,此函数不会删除已经下载的数据
     */
    void removeTask(const std::string &url);

    qint64 currentJobCount(void) const;

signals:

    void downloadingProgress(int progress);
    void NetFileDownLoaded(NetFile *);
    void startJob(QString);
    void networkError(QString msg, QString file);

public slots:

    void replyFinished(QNetworkReply *reply);
    void onTransferring(qint64, qint64);
    void onStartJob(QString);

private:

    void run() override;

private:

    QNetworkAccessManager *_net = nullptr;

    std::map<std::string, DataReceiver *> _task_paths;
    std::queue<Task> _task_queue;
    bool _running = false;
    QMutex _stl_mutex, _downloadingMutex;

};

#endif // DOWNLOADER_H
#include <QDir>
#include <iostream>
#include <QTextCodec>
#include "DownLoader.h"

using namespace std;

DataReceiver::~DataReceiver() {

}

void DataReceiver::setMode(DataMode mode) {

    this->_dataMode = mode;
}

DataReceiver::DataMode DataReceiver::mode() const {

    return _dataMode;
}

NetFile::NetFile(const QString &name, const QString path/* ="" */, DataMode mode /* = DM_FILE */) :
	_file(path +"/"+ name)
{
    _name = name;
    _dataMode = mode;
	_path = path;
		
    QDir outDir(path);
    if ( _dataMode == DM_FILE && !outDir.exists() )
        outDir.mkpath(path);

    if ( _dataMode == DM_FILE )
        _file.open(QIODevice::ReadWrite);
}

NetFile::NetFile() {

    _dataMode = DM_MEMORY;
}

NetFile::~NetFile() {

    if ( _file.isOpen() )
        _file.close();
}

const QByteArray NetFile::getData() {

    if ( _dataMode == DM_FILE && _data.isEmpty() )
        _data = _file.readAll();

    return _data;
}

QString NetFile::getName() {

    return _file.fileName();
}


QString NetFile::getPath(void)
{
	return _path;
}

void NetFile::close() {

    if ( _dataMode == DM_FILE && _file.isOpen() )
        _file.close();
}

const std::string &NetFile::getLocalDataPath(void) const {

    static string result;
    result.clear();

    if ( _dataMode == DM_FILE )
        result = _file.fileName().toStdString();

    return result;
}


void NetFile::receiveData(QByteArray data)
{
	if (_dataMode == DM_FILE)
	{
		_file.atEnd();
		_file.write(data);
	}
}

DataDownloadWorker::DataDownloadWorker() {

#ifdef _DEBUG
    this->setObjectName("_file_downloader");
#endif
    _net = new QNetworkAccessManager;

    connect(_net, SIGNAL(finished(QNetworkReply*)),
            this, SLOT(replyFinished(QNetworkReply*)));

    connect(this, SIGNAL(startJob(QString)),
            this, SLOT(onStartJob(QString)));

    _running = true;
    this->start();
}

DataDownloadWorker::~DataDownloadWorker() {

    _running = false;
    quit();
    wait();
    if ( _net )
        delete _net, _net = nullptr;
}

qint64 DataDownloadWorker::currentJobCount() const {

    return _task_paths.size();
}

void DataDownloadWorker::removeTask(const string &url) {

    _stl_mutex.lock();

    if ( _task_paths.find(url) != _task_paths.end() ) {

        queue<Task> tmp;

        while ( !_task_queue.empty() ) {

            if ( _task_queue.front()._url != url )
                tmp.push(_task_queue.front());

            _task_queue.pop();
        }

        while ( !tmp.empty() ) {

            _task_queue.push(tmp.front());
            tmp.pop();
        }

        _task_paths.erase(url);
    }

    _stl_mutex.unlock();
}

bool DataDownloadWorker::isIgnorable(const string &url) {

    bool result = false;
    size_t size = url.size();
    if ( size > 1 ) {

        const char *str = url.c_str();
        if (str[size-1] == 'a' && str[size-2] == '/' )
            result = true;
    }
    return result;
}

void DataDownloadWorker::DownLoadTo(const string &url, DataReceiver *recv) {

    _stl_mutex.lock();
    if ( _task_paths.find(url) == _task_paths.end() ) {
        Task task = { url, recv, false };
        _task_paths.insert(std::pair<string, DataReceiver *>(url, recv));
        _task_queue.push(task);
    }
    _stl_mutex.unlock();
}

void DataDownloadWorker::onStartJob(QString path) {

    auto reply = _net->get( QNetworkRequest( QUrl(path) ) );
    connect(reply, SIGNAL(downloadProgress(qint64,qint64)),
            this, SLOT(onTransferring(qint64,qint64)));
}

void DataDownloadWorker::onTransferring(qint64 size_read, qint64 total_size) {

    int percent = total_size ? size_read * 100 / total_size : 0;
    emit downloadingProgress(percent);
}

void DataDownloadWorker::run() {

    while ( _running ) {

        if ( _stl_mutex.tryLock() ) {

            if ( !_task_queue.empty() ) {

                if ( _downloadingMutex.tryLock() ) {

                    if ( _task_queue.front()._ignore )
                        _task_queue.pop();

                    emit startJob(_task_queue.front()._url.c_str());
                }
            }
            _stl_mutex.unlock();
        }

        msleep(1);
    }
}

void DataDownloadWorker::replyFinished(QNetworkReply *reply) {

    QNetworkRequest request = reply->request();
    string url = request.url().toString().toStdString();

    bool noError = false;

    QNetworkReply::NetworkError error = reply->error();

    switch ( error ) {

        case QNetworkReply::ContentNotFoundError:
            if ( isIgnorable(url) )
                removeTask(url);
            else
                emit networkError(tr("Can't find remote file!"), url.c_str());
            break;

        case QNetworkReply::ConnectionRefusedError:
            emit networkError(tr("Connection Refused!"), url.c_str());
            break;

        case QNetworkReply::TimeoutError:
            emit networkError(tr("Timeout!"), url.c_str());
            break;

        case QNetworkReply::NoError:
            noError = true;
            break;

        case QNetworkReply::HostNotFoundError:
            emit networkError(tr("Host not found!"), url.c_str());
            break;

        default:
            emit networkError(tr("Unknown Error!"), url.c_str());
            break;
    }

    if ( noError && _task_paths.find(url) != _task_paths.end() ) {

        DataReceiver *receiver = nullptr;
        if ( _task_paths.find(url) != _task_paths.end() )
            receiver = _task_paths.at(url);
        QByteArray transferred;

        if ( reply->hasRawHeader(QString("Content-Length").toUtf8() ) ) {

            int total_size = atoi( reply->rawHeader("Content-Length") );
            int size = total_size, size_read = 0;

            const int buffer_size = 10240;
            static char buffer[buffer_size];
            memset(buffer, 0, buffer_size);

            qint64 bytes_read = 0;

            do {

                bytes_read = reply->read( buffer, min(buffer_size, size) );
                transferred.append(buffer, bytes_read);
                size -= bytes_read;

                size_read += bytes_read;

            } while( size > 0 );


        } else if ( reply->isFinished() ) {

            QTextCodec *codec = QTextCodec::codecForName("utf8");
            QString context = codec->toUnicode( reply->readAll() );
            transferred.clear();
            transferred.append(context);
        }

        if (receiver) {

            receiver->receiveData(transferred);

            NetFile *net_file = dynamic_cast<NetFile *>(receiver);
            if ( net_file ) {

                _stl_mutex.lock();
                _task_paths.erase(url);
                _task_queue.pop();
                _stl_mutex.unlock();

                emit NetFileDownLoaded(net_file);
            }
        }
    }

    disconnect( reply, SIGNAL(downloadProgress(qint64, qint64)),
                this, SLOT(onTransferring(qint64, qint64)) );
    reply->deleteLater();

    _downloadingMutex.unlock();
}

标签:task,封装,url,void,QString,http,size,下载,const
来源: https://blog.csdn.net/wu110112/article/details/120155249

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有