使用QNetworkAccessManager实现FTP下载器

代码如下

photodownloader.h文件

#ifndef PHOTODOWNLOADER_H
#define PHOTODOWNLOADER_H

#include <QObject>
#include <QTimer>
#include <QThread>
#include <QList>
#include <QUrl>
#include <QDir>
#include <QMutex>
#include "communicate.h"
#include "mydatabase.h"
#include "ftpdownloader.h"
#include "withintablepushbutton.h"

/**
violation表imgexist字段含义:0-未下载 1-已下载 2-下载失败 3-正在下载
*/

class PhotoDownloader : public QObject
{
    Q_OBJECT
public:
    //单例模式
    static PhotoDownloader *Instance();
    //构造函数
    explicit PhotoDownloader(QObject *parent = Q_NULLPTR);

    void initTask();

private:
    //初始化imgexist字段(如果存在3则置为0)
    void initImgExistField();
    void prepareDownload(int downloaderId);

public:
    QMap<int,WithinTablePushButton *> viewImgBtnMap; //违章页面的查看按钮

private:
    static PhotoDownloader *self;

    bool isTaskActive;
    quint16 board_id;
    QList<FtpDownloader *>downloaderList;
    int maxDownloaderNum; //下载器最大数量
};

#endif // PHOTODOWNLOADER_H

photodownloader.cpp文件

#pragma execution_character_set("utf-8")
#include "photodownloader.h"

//单例模式
PhotoDownloader *PhotoDownloader::self = nullptr;
PhotoDownloader *PhotoDownloader::Instance()
{
    if (!self) {
        QMutex mutex;
        QMutexLocker locker(&mutex);
        if (!self) {
            self = new PhotoDownloader;
        }
    }
    return self;
}

//构造函数
PhotoDownloader::PhotoDownloader(QObject *parent):QObject(parent),isTaskActive(false)
  ,maxDownloaderNum(3)
{
    initImgExistField();

    //创建下载器
    for(int downloaderId=0;downloaderId<maxDownloaderNum;downloaderId++){
        downloaderList.append(new FtpDownloader(downloaderId));
        connect(downloaderList.at(downloaderId),&FtpDownloader::downloadFinish,this,&PhotoDownloader::prepareDownload);
        downloaderList[downloaderId]->setProperty("isBusy",false);
    }

    //设备连接时自动调用initTask
    connect(Communicate::Instance(),&Communicate::hasConnected,[=]{
        this->board_id = Communicate::Instance()->getBoardId();
        initTask();
    });
}

//初始化imgexist字段(如果存在3则置为0)
void PhotoDownloader::initImgExistField()
{
    MyDatabase::Instance()->restoreViolationImgexistField();
}

void PhotoDownloader::initTask()
{
    if(!isTaskActive){
        isTaskActive = true;
        //获取未下载的图片数
        int imgNum = MyDatabase::Instance()->getUndownloadImgNum(this->board_id);
        if(imgNum<=0){
            isTaskActive = false;
            return;
        }
        //准备下载
        for(int downloaderId=0;downloaderId<qMin(imgNum,maxDownloaderNum);downloaderId++){
            prepareDownload(downloaderId);
            downloaderList[downloaderId]->setProperty("isBusy",true);
        }
    }
    else{
        //如果有未开启的下载器则开启
        for(int i=0;i<maxDownloaderNum;i++){
            if(!downloaderList[i]->property("isBusy").toBool()){
                prepareDownload(i);
                downloaderList[i]->setProperty("isBusy",true);
                qDebug()<<"新开启"<<i<<"号下载器";
                break;
            }
        }
    }

}

void PhotoDownloader::prepareDownload(int downloaderId)
{
    int violationId;
    QString imgName;
    if( MyDatabase::Instance()->getEarliestImgName(this->board_id,violationId,imgName) ){
        QString url = "ftp://speedtest.tele2.net/" + imgName;
        QString dir = QDir::currentPath() + "/violationPhoto";
        MyDatabase::Instance()->updateImgexistById(violationId,3);//将imgexist改为3(正在下载)
        downloaderList[downloaderId]->startDownload(violationId,url,dir);
    }
    else{
        qDebug()<<QString("下载器%1 发现 无未下载图片").arg(downloaderId);
        downloaderList[downloaderId]->setProperty("isBusy",false);
        for(int i=0;i<maxDownloaderNum;i++){
            if(downloaderList[i]->property("isBusy").toBool()) return;
        }
        qDebug()<<"所有下载器均空闲";
        isTaskActive = false;
    }
}

ftpdownloader.h文件

#ifndef FTPDOWNLOADER_H
#define FTPDOWNLOADER_H

#include <QObject>
#include <QDir>
#include <QUrl>
#include "mydatabase.h"
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QTimer>
#include <QDebug>
#include <QMetaEnum>
#include <QThread>

class FtpDownloader : public QObject
{
    Q_OBJECT
public:
    explicit FtpDownloader(int downloaderId,QObject *parent = Q_NULLPTR);
    void startDownload(int violationId, QString url, QString dir);

private:
    //检查Url正确性
    bool checkUrl();
    //检查保存目录正确性
    bool checkSaveDir();
    //创建下载文件
    bool createDownloadFile();

signals:
    void downloadFinish(int downloaderId);//下载结束信号

private slots:
    void timeOut();
    void slotReadyRead();
    void readReplyError(QNetworkReply::NetworkError error);
    void downloadFinishReply(QNetworkReply* reply);
    void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);


private:
    int downloaderId;//当前下载器的编号
    int violationId;
    QUrl url;
    QDir dir;
    QFile *file;
    QTimer *timer;
    qint64 fileDownloadSize;
    qint64 lastDownloadSize;
    QNetworkReply *downloadReply;
    QNetworkAccessManager *downloadManager;
};

#endif // FTPDOWNLOADER_H

ftpdownloader.cpp文件

#pragma execution_character_set("utf-8")
#include "ftpdownloader.h"
#include "photodownloader.h"

FtpDownloader::FtpDownloader(int downloaderId,QObject *parent):QObject(parent)
{
    this->downloaderId = downloaderId;

    downloadManager = new QNetworkAccessManager(this);
    connect(downloadManager,&QNetworkAccessManager::finished,this,&FtpDownloader::downloadFinishReply);

    timer = new QTimer(this);
    connect(timer,&QTimer::timeout,this,&FtpDownloader::timeOut);
}

//检查Url正确性
bool FtpDownloader::checkUrl()
{
    if(!url.isValid()) return false;
    if(url.scheme() != "ftp") return false;
    if(url.path().isEmpty() || url.path()=="/") return false;
    return true;
}

//检查保存目录正确性
bool FtpDownloader::checkSaveDir()
{
    //如果目录不存在就创建目录
    if(!dir.exists())
    {
        if(!dir.mkpath(dir.absolutePath())) return false;
    }
    return true;
}

//创建下载文件
bool FtpDownloader::createDownloadFile()
{
    //从url中截取出文件名
    QString localFileName = QFileInfo(url.path()).fileName();
    localFileName = QString("%1-下载器%2-%3").arg(this->violationId).arg(this->downloaderId).arg(localFileName);//(临时这样命名文件)
    file = new QFile();
    file->setFileName(dir.absoluteFilePath(localFileName));
    if(!file->open(QIODevice::WriteOnly)) return false;
    return true;
}

void FtpDownloader::startDownload(int violationId, QString url, QString dir)
{
    this->violationId = violationId;
    this->url = QUrl(url);
    this->dir = QDir(dir);

    if( !checkUrl() || !checkSaveDir() || !createDownloadFile() ){
        MyDatabase::Instance()->updateImgexistById(violationId,2);//将imgexist改为2(下载失败)
        if( PhotoDownloader::Instance()->viewImgBtnMap.contains(violationId) ){   //将按钮文字改为“不存在”
            PhotoDownloader::Instance()->viewImgBtnMap.value(violationId)->setText("不存在");
        }
        emit downloadFinish(this->downloaderId);
        return;
    }

    fileDownloadSize = 0;
    lastDownloadSize = 0;

    downloadReply = downloadManager->get(QNetworkRequest(url));
    connect(downloadReply,&QNetworkReply::readyRead,this,&FtpDownloader::slotReadyRead);
    connect(downloadReply,&QNetworkReply::downloadProgress,this,&FtpDownloader::downloadProgress);
    connect(downloadReply,static_cast<void(QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error),this,&FtpDownloader::readReplyError);

    if(!timer->isActive()) timer->start(30 * 1000);//启动超时检查定时器,每30秒查询下载情况
}

void FtpDownloader::timeOut()
{
    if(lastDownloadSize != fileDownloadSize){
        lastDownloadSize = fileDownloadSize;//更新lastDownloadSize
    }
    else{
        //如果30秒之前下载了的文件大小等于下载下载了的文件大小,就主动发出超时error信号
        emit downloadReply->error(QNetworkReply::TimeoutError);
    }
}

void FtpDownloader::slotReadyRead()
{
    file->write(downloadReply->readAll());
    fileDownloadSize = file->size();//更新下载字节数
}

void FtpDownloader::readReplyError(QNetworkReply::NetworkError error)
{
    //打印错误信息
    QMetaEnum metaEnum = QMetaEnum::fromType<QNetworkReply::NetworkError>();
    //PS:字符串转换为枚举值
    //Qt::Alignment alignment = (Qt::Alignment)metaEnum.keyToValue("Qt::AlignLeft");
    //alignment = (Qt::Alignment)metaEnum.keysToValue("Qt::AlignLeft | Qt::AlignVCenter");
    //枚举值转换为字符串
    const char *errStr = metaEnum.valueToKey(error);
    qDebug()<<"文件下载error: " + QString(errStr);

    file->close();
    file->deleteLater();
    file = Q_NULLPTR;
    downloadReply->deleteLater();
    downloadReply = Q_NULLPTR;

    startDownload(violationId,url.toString(),dir.absolutePath()); //重新尝试下载文件
}

void FtpDownloader::downloadFinishReply(QNetworkReply *reply)
{
    if(timer->isActive()) timer->stop(); //停止超时计时器

    file->waitForBytesWritten(5 * 1000); //等待文件写入结束(5秒钟)

    if(file->size()==0){
//        MyDatabase::Instance()->updateImgexistById(violationId,2);
    }
    else{
        MyDatabase::Instance()->updateImgexistById(violationId,1);
    }

    file->close();
    file->deleteLater();
    file = Q_NULLPTR;
    reply->deleteLater();
    reply = Q_NULLPTR;

    emit downloadFinish(this->downloaderId);
}

void FtpDownloader::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
    if(bytesTotal!=0)
    {
        qDebug()<<"下载器:"<<this->downloaderId<<" 总大小:"<<bytesTotal/1024<<"kb 已下载:"<<bytesReceived/1024<<"kb";
        if( PhotoDownloader::Instance()->viewImgBtnMap.contains(violationId) ){
            PhotoDownloader::Instance()->viewImgBtnMap.value(violationId)->setProgress(bytesReceived,bytesTotal);
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,064评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,606评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,011评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,550评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,465评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,919评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,428评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,075评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,208评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,185评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,191评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,914评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,482评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,585评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,825评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,194评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,703评论 2 339

推荐阅读更多精彩内容