zoukankan      html  css  js  c++  java
  • 使用QNetworkAccessManager实现Qt的FTP下载服务

     从Qt5开始,官方推荐使用QNetworkAccessManager进行Ftp和http的上传和下载操作;Qt4中使用的QtFtp模块即作为独立模块,需要自己从github上进行下载编译后使用(官方地址:https://github.com/qt/qtftp)。

    官方的QtFtp最后一次更新为2014年,根据搜索的资料,其尚存在若干bug。不过有人对此代码在Github上进行维护和更新,如果需要使用的话,可以搜索一下。

     

    QNetworkAccessManager的相关API比较丰富,但是相应也比较低级。如果需要对Ftp进行较为复杂的操作,在缺少资料的基础上就会很麻烦,需要较好的功底。

    因为个人对Ftp的操作仅限于下载或者上传,因此使用`QNetworkAccessManager`即可满足要求。此处仅对下载进行示范,上传基本一致。

     1 #ifndef FTPGETWINDOW_H
     2 #define FTPGETWINDOW_H
     3 
     4 #include <QWidget>
     5 #include <QUrl>
     6 #include <QDir>
     7 #include <QNetworkReply>
     8 
     9 class QFile;
    10 class QLabel;
    11 class QLineEdit;
    12 class QTextEdit;
    13 class QPushButton;
    14 class QProgressBar;
    15 class QGridLayout;
    16 
    17 class QTimer;
    18 class QNetworkAccessManager;
    19 
    20 class FtpgetWindow : public QWidget
    21 {
    22     Q_OBJECT
    23 
    24 public:
    25     FtpgetWindow(QWidget *parent = 0);
    26     ~FtpgetWindow();
    27 
    28 private slots:
    29     void timeOut();
    30     void updateSelectSaveDir();
    31     void updateTaskRunningState();
    32     void slotReadyRead();
    33     void readReplyError(QNetworkReply::NetworkError error);
    34     void downloadFinishReply(QNetworkReply* reply);
    35     void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
    36 
    37 private:
    38     bool checkUrl();
    39     bool checkSaveDir();
    40     bool createDownloadFile();
    41     void startDownloadFile();
    42 
    43 private:
    44     qint64 fileDownloadSize;
    45     qint64 lastDownloadSize;
    46     QUrl url;
    47     QDir saveDir;
    48     QFile *file;
    49     QTimer *timer;
    50     QNetworkReply *downloadReply;
    51     QNetworkAccessManager *downloadManager;
    52 
    53     QLabel *urlLabel;
    54     QLabel *dirLoactionLabel;
    55     QLabel *downlaodInfoLabel;
    56     QLabel *runningTipLabel;
    57     QLineEdit *urlTextEdit;
    58     QLineEdit *dirTextEdit;
    59     QTextEdit *downloadInfoTextEdit;
    60     QPushButton *runningTaskButton;
    61     QPushButton *dirLocationButton;
    62     QProgressBar *progressBar;
    63     QGridLayout *mainLayout;
    64 };
    65 
    66 #endif // FTPGETWINDOW_H

     头文件无需赘述。

      1 #include "ftpgetwindow.h"
      2 
      3 #include <QLabel>
      4 #include <QLineEdit>
      5 #include <QTextEdit>
      6 #include <QPushButton>
      7 #include <QProgressBar>
      8 #include <QGridLayout>
      9 #include <QFileDialog>
     10 
     11 #include <QUrl>
     12 #include <QDir>
     13 #include <QFile>
     14 #include <QTimer>
     15 #include <QFileInfo>
     16 #include <QMetaEnum>
     17 #include <QNetworkAccessManager>
     18 
     19 FtpgetWindow::FtpgetWindow(QWidget *parent)
     20     : QWidget(parent),
     21       fileDownloadSize(0),
     22       lastDownloadSize(0),
     23       file(Q_NULLPTR)
     24 {
     25     downloadManager = new QNetworkAccessManager(this);
     26     connect(downloadManager, SIGNAL(finished(QNetworkReply*)),SLOT(downloadFinishReply(QNetworkReply*)));
     27 
     28     //初始化超时检查定时器,30秒查询一次
     29     timer = new QTimer;
     30     connect(timer, SIGNAL(timeout()), SLOT(timeOut()));
     31 
     32     urlLabel = new QLabel;
     33     urlLabel->setText(tr("Url:"));
     34 
     35     urlTextEdit = new QLineEdit;
     36     urlLabel->setBuddy(urlTextEdit);
     37 
     38     runningTaskButton = new QPushButton;
     39     runningTaskButton->setText("Run");
     40     connect(runningTaskButton, SIGNAL(clicked(bool)), SLOT(updateTaskRunningState()));
     41 
     42     dirLoactionLabel = new QLabel;
     43     dirLoactionLabel->setText(tr("Save Dir:"));
     44 
     45     dirTextEdit = new QLineEdit;
     46     dirTextEdit->setReadOnly(true);
     47     dirLoactionLabel->setBuddy(dirTextEdit);
     48 
     49     dirLocationButton = new QPushButton;
     50     dirLocationButton->setText("Select Save Dir");
     51     connect(dirLocationButton, SIGNAL(clicked(bool)), SLOT(updateSelectSaveDir()));
     52 
     53     runningTipLabel = new QLabel;
     54     runningTipLabel->setText(tr("Runing task:"));
     55 
     56     progressBar = new QProgressBar;
     57     runningTipLabel->setBuddy(progressBar);
     58 
     59     downlaodInfoLabel = new QLabel;
     60     downlaodInfoLabel->setText(tr("Download Info:"));
     61 
     62     downloadInfoTextEdit = new QTextEdit;
     63     downloadInfoTextEdit->setReadOnly(true);
     64     downlaodInfoLabel->setBuddy(downloadInfoTextEdit);
     65 
     66     mainLayout = new QGridLayout;
     67     mainLayout->setColumnStretch(0, 1);
     68     mainLayout->setColumnStretch(1, 3);
     69     mainLayout->setColumnStretch(2, 1);
     70     mainLayout->setMargin(15);
     71     mainLayout->setColumnMinimumWidth(2, 15);
     72 
     73     mainLayout->addWidget(urlLabel, 0, 0);
     74     mainLayout->addWidget(urlTextEdit, 0, 1);
     75     mainLayout->addWidget(runningTaskButton, 0, 2);
     76     mainLayout->addWidget(dirLoactionLabel, 1, 0);
     77     mainLayout->addWidget(dirTextEdit, 1, 1);
     78     mainLayout->addWidget(dirLocationButton, 1, 2);
     79     mainLayout->addWidget(runningTipLabel, 2, 0, 1, 1);
     80     mainLayout->addWidget(progressBar, 2, 1, 1, 1);
     81     mainLayout->addWidget(downlaodInfoLabel, 3, 0, 1, 1);
     82     mainLayout->addWidget(downloadInfoTextEdit, 4, 0, 3, 3);
     83     setLayout(mainLayout);
     84 
     85     setFixedWidth(800);
     86     setWindowTitle(tr("FpGet Window"));
     87 }
     88 
     89 FtpgetWindow::~FtpgetWindow()
     90 {
     91     if(file != Q_NULLPTR)
     92     {
     93         file->deleteLater();
     94         file = Q_NULLPTR;
     95     }
     96     //downloadManager的父对象是窗体,会自动进行析构
     97 }
     98 
     99 /**
    100  * @brief 进行下载超时判断,错误则发送超时信号
    101  */
    102 void FtpgetWindow::timeOut()
    103 {
    104     if(lastDownloadSize != fileDownloadSize)
    105         lastDownloadSize = fileDownloadSize;
    106     else
    107         emit downloadReply->error(QNetworkReply::TimeoutError); //下载超时,发送超时错误信号
    108 }
    109 
    110 /**
    111  * @brief 检查Url地址合法性
    112  * @return
    113  */
    114 bool FtpgetWindow::checkUrl()
    115 {
    116     url = QUrl(urlTextEdit->text());
    117     if(!url.isValid())
    118     {
    119         downloadInfoTextEdit->append("Error: Invalid URL");
    120         return false;
    121     }
    122 
    123     if(url.scheme() != "ftp")
    124     {
    125         downloadInfoTextEdit->append("Error: URL must start with 'ftp:'");
    126         return false;
    127     }
    128 
    129     if (url.path().isEmpty()) {
    130         downloadInfoTextEdit->append("Error: URL has no path");
    131         return false;
    132     }
    133     return true;
    134 }
    135 
    136 /**
    137  * @brief 检查文件下载地址
    138  * @return
    139  */
    140 bool FtpgetWindow::checkSaveDir()
    141 {
    142     QString dir = dirTextEdit->text();
    143     if(dir.isEmpty())
    144         dir = QDir::currentPath() + "/Download/";
    145     saveDir = QDir(dir);
    146 
    147     if(!saveDir.exists())
    148     {
    149         auto ok = saveDir.mkdir(dir);
    150         if(!ok) return false;
    151     }
    152     return true;
    153 }
    154 
    155 bool FtpgetWindow::createDownloadFile()
    156 {
    157     auto localFileName = QFileInfo(url.path()).fileName();
    158     if (localFileName.isEmpty())
    159         localFileName = "ftpget.out";
    160 
    161     file = new QFile;
    162     file->setFileName(saveDir.absoluteFilePath(localFileName));
    163     if(!file->open(QIODevice::WriteOnly))
    164     {
    165         auto info = "Error: Cannot write file " + file->fileName()
    166                 + ": " + file->errorString();
    167         downloadInfoTextEdit->append(info);
    168         return false;
    169     }
    170     return true;
    171 }
    172 
    173 /**
    174  * @brief 开始下载文件操作
    175  */
    176 void FtpgetWindow::startDownloadFile()
    177 {
    178     if(!createDownloadFile()) return;
    179 
    180     if(timer->isActive())
    181         timer->stop();
    182     fileDownloadSize = lastDownloadSize = 0; //重新设置定时器以及相关变量
    183 
    184     downloadInfoTextEdit->append("Download file: " + url.fileName());
    185 
    186     downloadReply = downloadManager->get(QNetworkRequest(url));
    187 
    188     //分块获取文件信息,并写入文件中
    189     connect(downloadReply, SIGNAL(readyRead()), SLOT(slotReadyRead()));
    190 
    191     //获取下载进度信息
    192     connect(downloadReply, SIGNAL(downloadProgress(qint64,qint64)),
    193             SLOT(downloadProgress(qint64,qint64)));
    194 
    195     //下载过程出错,进行报错处理(超时处理也是丢出超时信号,交由此槽函数进行处理)
    196     connect(downloadReply, SIGNAL(error(QNetworkReply::NetworkError)),
    197             SLOT(readReplyError(QNetworkReply::NetworkError)));
    198 
    199     timer->start(30 * 1000); //启动超时检查定时器,每30秒查询下载情况
    200 }
    201 
    202 void FtpgetWindow::updateSelectSaveDir()
    203 {
    204     dirTextEdit->setText("");
    205     QString dir = QFileDialog::getExistingDirectory(this, tr("Open Directory"),
    206                                                     "C://",
    207                                                     QFileDialog::ShowDirsOnly
    208                                                     | QFileDialog::DontResolveSymlinks);
    209     if(!dir.isEmpty())
    210         dirTextEdit->setText(dir);
    211 }
    212 
    213 void FtpgetWindow::updateTaskRunningState()
    214 {
    215     if(!checkUrl() || !checkSaveDir())
    216         return;
    217 
    218     downloadInfoTextEdit->clear(); //清空信息栏
    219 
    220     runningTaskButton->setEnabled(false);
    221     dirLocationButton->setEnabled(false);
    222     startDownloadFile();
    223 }
    224 
    225 /**
    226  * @brief 文件下载完成的清尾操作
    227  * @param reply
    228  */
    229 void FtpgetWindow::downloadFinishReply(QNetworkReply *reply)
    230 {
    231     file->waitForBytesWritten(5 * 1000); //等待文件写入结束
    232     if(0 == file->size())
    233         //此处下载失败,不再进行重新下载操作
    234         downloadInfoTextEdit->append("Nothing be download.");
    235     else
    236         downloadInfoTextEdit->append("Download file success.");
    237 
    238     if(timer->isActive())
    239         timer->stop(); //停止超时计时器
    240 
    241     file->deleteLater();
    242     file = Q_NULLPTR;
    243 
    244     reply->deleteLater();
    245     reply = Q_NULLPTR;
    246 
    247     runningTaskButton->setEnabled(true);
    248     dirLocationButton->setEnabled(true);
    249 }
    250 
    251 void FtpgetWindow::slotReadyRead()
    252 {
    253     file->write(downloadReply->readAll());
    254     fileDownloadSize = file->size(); //更新下载字节数
    255 }
    256 
    257 /**
    258  * @brief 下载异常,重新进行下载
    259  * @param error
    260  */
    261 void FtpgetWindow::readReplyError(QNetworkReply::NetworkError error)
    262 {
    263     auto metaEnum = QMetaEnum::fromType<QNetworkReply::NetworkError>();
    264     //PS:字符串转换为枚举值
    265     //Qt::Alignment alignment = (Qt::Alignment)metaEnum.keyToValue("Qt::AlignLeft");
    266     //alignment = (Qt::Alignment)metaEnum.keysToValue("Qt::AlignLeft | Qt::AlignVCenter");
    267     //枚举值转换为字符串
    268     auto errStr = metaEnum.valueToKey(error);
    269     downloadInfoTextEdit->append("Download file occur error: " + QString(errStr));
    270 
    271     file->deleteLater();
    272     file = Q_NULLPTR;
    273 
    274     downloadReply->deleteLater();
    275     downloadReply = Q_NULLPTR;
    276 
    277     startDownloadFile(); //重新尝试下载文件
    278 }
    279 
    280 void FtpgetWindow::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
    281 {
    282     if(0 != bytesTotal)
    283     {
    284         progressBar->setMaximum(bytesTotal);
    285         progressBar->setValue(bytesReceived);
    286     }
    287 }

    (1)超时操作:

    在下载过程中,经常出现假死操作,因为不清楚如何进行续传操作,现有做法是取消当前下载任务并重新开始。

    在启动下载操所时,启动定时器,每隔30秒记录当前下载数值和上一次记录的下载数值比较,如果相同,则可以认为在30秒内无操作,发送超时信号,断开连接重新开始下载任务。

    (2)大文件下载:

    现有仅测试了上百M的文件,可以在下载结束的时候,一次读取所有字节并写入文件,但是这样的压力比较大。

    因此,当QNetworkReply发送信号告知有分段数据可供读取的时候,即读取并写入文件中。

    (3)大文件上传:

    调用put函数时,主要有两种方式,将文件信息读取出保存至QByteArray中,或者上传文件的操作指针。使用后者即可实现大型文件的上传操作。

    (4)下载进度信息:

    下载过程中,QNetworkReply会发送下载进度信息,用户可以根据此刷新QProgressBar控件,或者在命令行刷新进度条。

    以下代码为在命令行实现进度条刷新操作,关键在于每次输出进度信息的时候,不要添加换行符,并且在输出信息头部添加" "即可。

     1 /**
     2  * @brief 实现命令行下进度条,提示下载进度
     3  * @param bytesReceived
     4  * @param bytesTotal
     5  */
     6 void FtpGet::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
     7 {
     8     int barLength = 50;
     9     int percent = int(qreal(bytesReceived) / qreal(bytesTotal) * barLength);
    10     QString out = "
    Percent: " + QString(percent, '#') + QString(barLength - percent, ' ');
    11     out += " " + QString::number(bytesReceived) + " / " + QString::number(bytesTotal);
    12     std::cout << qPrintable(out) << std::flush;
    13 }

    > PS:
    > 如果您觉得我的文章对您有帮助,可以扫码领取下红包或扫码支持(随意多少,一分钱都是爱),谢谢!

    支付宝红包 | 支付宝 | 微信
    -|-|-
    ![](https://img2018.cnblogs.com/blog/764719/201906/764719-20190624102014468-193165117.png) | ![](https://img2018.cnblogs.com/blog/764719/201906/764719-20190624102025867-1690450943.png) | ![](https://img2018.cnblogs.com/blog/764719/201906/764719-20190624101626318-627523959.png) |

  • 相关阅读:
    微软企业库5.0学习笔记实战数据验证模块高级篇
    总结一些常用的CMS
    JS类库
    sql2
    前端开发必须知道的CSS
    JS实现非图片动态loading
    Microsoft SQL Server 2005 提供了一些工具来监控数据库
    js实现Tooltip
    Js动画基础
    仿iGoogle自定义首页模块拖拽
  • 原文地址:https://www.cnblogs.com/jason1990/p/7830271.html
Copyright © 2011-2022 走看看