使用QNetworkAccessManager实现FTP下载器

代码如下

photodownloader.h文件

#ifndef PHOTODOWNLOADER_H
#define PHOTODOWNLOADER_H

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#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 viewImgBtnMap; //违章页面的查看按钮

private:
    static PhotoDownloader *self;

    bool isTaskActive;
    quint16 board_id;
    QListdownloaderList;
    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;downloaderIdsetProperty("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;downloaderIdsetProperty("isBusy",true);
        }
    }
    else{
        //如果有未开启的下载器则开启
        for(int i=0;iproperty("isBusy").toBool()){
                prepareDownload(i);
                downloaderList[i]->setProperty("isBusy",true);
                qDebug()<<"新开启"<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()<setProperty("isBusy",false);
        for(int i=0;iproperty("isBusy").toBool()) return;
        }
        qDebug()<<"所有下载器均空闲";
        isTaskActive = false;
    }
}

ftpdownloader.h文件

#ifndef FTPDOWNLOADER_H
#define FTPDOWNLOADER_H

#include 
#include 
#include 
#include "mydatabase.h"
#include 
#include 
#include 
#include 
#include 
#include 

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(&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();
    //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()<<"下载器:"<downloaderId<<" 总大小:"<viewImgBtnMap.contains(violationId) ){
            PhotoDownloader::Instance()->viewImgBtnMap.value(violationId)->setProgress(bytesReceived,bytesTotal);
        }
    }
}

你可能感兴趣的:(使用QNetworkAccessManager实现FTP下载器)