Qt 实现根据 URL 下载文件

新建 directory.h

#ifndef DIRECTORY_H
#define DIRECTORY_H

#include <QObject>

class Directory {
  public:
    Directory();
    ~Directory();
    QStringList appConfigDir();
    QStringList audioDir();
    QStringList configDir();
    QStringList downloadDir();
    QStringList desktopDir();
    QStringList documentDir();
    QStringList homeDir();
    QStringList videoDir();
    QStringList pictureDir();
    QStringList tempDir();
    QStringList cacheDir();
};

#endif // DIRECTORY_H

新建 directory.cpp

#include "directory.h"

#include <QStandardPaths>

Directory::Directory() {}

QStringList Directory::appConfigDir() {
    return QStandardPaths::standardLocations(
        QStandardPaths::AppConfigLocation );
}

QStringList Directory::audioDir() {
    return QStandardPaths::standardLocations( QStandardPaths::MusicLocation );
}

QStringList Directory::configDir() {
    return QStandardPaths::standardLocations( QStandardPaths::ConfigLocation );
}

QStringList Directory::downloadDir() {
    return QStandardPaths::standardLocations(
        QStandardPaths::DownloadLocation );
}

QStringList Directory::desktopDir() {
    return QStandardPaths::standardLocations( QStandardPaths::DesktopLocation );
}

QStringList Directory::documentDir() {
    return QStandardPaths::standardLocations(
        QStandardPaths::DocumentsLocation );
}

QStringList Directory::homeDir() {
    return QStandardPaths::standardLocations( QStandardPaths::HomeLocation );
}

QStringList Directory::videoDir() {
    return QStandardPaths::standardLocations( QStandardPaths::MoviesLocation );
}

QStringList Directory::pictureDir() {
    return QStandardPaths::standardLocations(
        QStandardPaths::PicturesLocation );
}

QStringList Directory::tempDir() {
    return QStandardPaths::standardLocations( QStandardPaths::TempLocation );
}

QStringList Directory::cacheDir() {
    return QStandardPaths::standardLocations( QStandardPaths::CacheLocation );
}

Directory::~Directory() {}

新建 textprogressbar.h

#ifndef TEXTPROGRESSBAR_H
#define TEXTPROGRESSBAR_H

#include <QString>

class TextProgressBar {
  public:
    void clear();
    void update();
    void setMessage( const QString &message );
    void setStatus( qint64 value, qint64 maximum );

  private:
    QString message;
    qint64  value     = 0;
    qint64  maximum   = -1;
    int     iteration = 0;
};

#endif

新建 textprogressbar.cpp

#include "textprogressbar.h"

#include <QByteArray>

#include <cstdio>

using namespace std;

void TextProgressBar::clear() {
    printf( "\n" );
    fflush( stdout );

    value     = 0;
    maximum   = -1;
    iteration = 0;
}

void TextProgressBar::update() {
    ++iteration;

    if ( maximum > 0 ) {
        // we know the maximum
        // draw a progress bar
        int percent = value * 100 / maximum;
        int hashes  = percent / 2;

        QByteArray progressbar( hashes, '#' );
        if ( percent % 2 ) progressbar += '>';

        printf( "\r[%-50s] %3d%% %s     ", progressbar.constData(), percent,
                qPrintable( message ) );
    } else {
        // we don't know the maximum, so we can't draw a progress bar
        int        center = ( iteration % 48 ) + 1; // 50 spaces, minus 2
        QByteArray before( qMax( center - 2, 0 ), ' ' );
        QByteArray after( qMin( center + 2, 50 ), ' ' );

        printf( "\r[%s###%s]      %s      ", before.constData(),
                after.constData(), qPrintable( message ) );
    }
}

void TextProgressBar::setMessage( const QString &m ) { message = m; }

void TextProgressBar::setStatus( qint64 val, qint64 max ) {
    value   = val;
    maximum = max;
}

新建 downloadmanager.h

#ifndef DOWNLOADMANAGER_H
#define DOWNLOADMANAGER_H

#include "textprogressbar.h"

#include <QElapsedTimer>
#include <QFile>
#include <QNetworkAccessManager>
#include <QObject>
#include <QQueue>

class DownloadManager : public QObject {
    Q_OBJECT

  public:
    explicit DownloadManager( QObject *parent = nullptr );

    void           append( const QUrl &url );
    void           append( const QStringList &urls );
    static QString saveFileName( const QUrl &url);

  signals:
    void finished();

  private slots:
    void startNextDownload();
    void downloadProgress( qint64 bytesReceived, qint64 bytesTotal );
    void downloadFinished();
    void downloadReadyRead();

  private:
    bool isHttpRedirect() const;
    void reportRedirect();

    QNetworkAccessManager manager;
    QQueue<QUrl>          downloadQueue;
    QNetworkReply        *currentDownload = nullptr;
    QFile                 output;
    QElapsedTimer         downloadTimer;
    TextProgressBar       progressBar;

    int downloadedCount = 0;
    int totalCount      = 0;
};

#endif

新建 downloadmanager.cpp

#include "downloadmanager.h"
#include "directory.h"

#include <QApplication>
#include <QFileDialog>
#include <QFileInfo>
#include <QNetworkReply>
#include <QTextStream>
#include <QTimer>

#include <cstdio>

using namespace std;

DownloadManager::DownloadManager( QObject *parent )
    : QObject( parent ) {}

void DownloadManager::append( const QStringList &urls ) {
    for ( const QString &urlAsString : urls )
        append( QUrl::fromEncoded( urlAsString.toLocal8Bit() ) );

    if ( downloadQueue.isEmpty() ) {
        QTimer::singleShot( 0, this, &DownloadManager::finished );
    }
}

void DownloadManager::append( const QUrl &url ) {
    if ( downloadQueue.isEmpty() ) {
        QTimer::singleShot( 0, this, &DownloadManager::startNextDownload );
    }

    downloadQueue.enqueue( url );
    ++totalCount;
}

QString DownloadManager::saveFileName( const QUrl &url, bool askForSavePath ) {
    Directory *directory   = new Directory();
    QString    downloadDir = directory->downloadDir().at( 0 );
    QString    path        = url.path();
    QString    basename    = downloadDir + "/" + QFileInfo( path ).fileName();

    if ( basename.isEmpty() ) basename = "download";

    if ( QFile::exists( basename ) ) {
        // already exists, don't overwrite
        int i = 1;
        basename += "(";
        while ( QFile::exists( basename + QString::number( i ) ) )
            ++i;

        basename += QString::number( i );
        basename += ")";
    }
    return basename;
}

void DownloadManager::startNextDownload() {
    if ( downloadQueue.isEmpty() ) {
        printf( "%d/%d files downloaded successfully\n", downloadedCount,
                totalCount );
        emit finished();
        return;
    }

    QUrl    url      = downloadQueue.dequeue();
    QString filename = saveFileName( url, askForSavePath );
    if ( filename.isEmpty() ) {
        printf( "Cancel downlaod task\n" );
        downloadQueue.empty();
        return; // skip download
    }
    output.setFileName( filename );

    printf( "filename: %s\n", qPrintable( filename ) );

    if ( !output.open( QIODevice::WriteOnly ) ) {
        fprintf( stderr,
                 "Problem opening save file '%s' for download '%s': %s\n",
                 qPrintable( filename ), url.toEncoded().constData(),
                 qPrintable( output.errorString() ) );

        startNextDownload();
        return; // skip this download
    }

    QNetworkRequest request( url );
    currentDownload = manager.get( request );
    connect( currentDownload, &QNetworkReply::downloadProgress, this,
             &DownloadManager::downloadProgress );
    connect( currentDownload, &QNetworkReply::finished, this,
             &DownloadManager::downloadFinished );
    connect( currentDownload, &QNetworkReply::readyRead, this,
             &DownloadManager::downloadReadyRead );

    // prepare the output
    printf( "Downloading %s...\n", url.toEncoded().constData() );
    downloadTimer.start();
}

void DownloadManager::downloadProgress( qint64 bytesReceived,
                                        qint64 bytesTotal ) {

    emit progress( bytesReceived, bytesReceived );

    progressBar.setStatus( bytesReceived, bytesTotal );

    // calculate the download speed
    double  speed = bytesReceived * 1000.0 / downloadTimer.elapsed();
    QString unit;
    if ( speed < 1024 ) {
        unit = "bytes/sec";
    } else if ( speed < 1024 * 1024 ) {
        speed /= 1024;
        unit = "kB/s";
    } else {
        speed /= 1024 * 1024;
        unit = "MB/s";
    }

    progressBar.setMessage(
        QString::fromLatin1( "%1 %2" ).arg( speed, 3, 'f', 1 ).arg( unit ) );
    progressBar.update();
}

void DownloadManager::downloadFinished() {
    progressBar.clear();
    output.close();

    if ( currentDownload->error() ) {
        // download failed
        fprintf( stderr, "Failed: %s\n",
                 qPrintable( currentDownload->errorString() ) );
        output.remove();
    } else {
        // let's check if it was actually a redirect
        if ( isHttpRedirect() ) {
            reportRedirect();
            output.remove();
        } else {
            printf( "Succeeded.\n" );
            ++downloadedCount;
        }
    }

    currentDownload->deleteLater();
    startNextDownload();
}

void DownloadManager::downloadReadyRead() {
    output.write( currentDownload->readAll() );
}

bool DownloadManager::isHttpRedirect() const {
    int statusCode =
        currentDownload->attribute( QNetworkRequest::HttpStatusCodeAttribute )
            .toInt();
    return statusCode == 301 || statusCode == 302 || statusCode == 303 ||
           statusCode == 305 || statusCode == 307 || statusCode == 308;
}

void DownloadManager::reportRedirect() {
    int statusCode =
        currentDownload->attribute( QNetworkRequest::HttpStatusCodeAttribute )
            .toInt();
    QUrl requestUrl = currentDownload->request().url();
    QTextStream( stderr ) << "Request: " << requestUrl.toDisplayString()
                          << " was redirected with code: " << statusCode
                          << '\n';

    QVariant target = currentDownload->attribute(
        QNetworkRequest::RedirectionTargetAttribute );
    if ( !target.isValid() ) return;
    QUrl redirectUrl = target.toUrl();
    if ( redirectUrl.isRelative() )
        redirectUrl = requestUrl.resolved( redirectUrl );
    QTextStream( stderr ) << "Redirected to: " << redirectUrl.toDisplayString()
                          << '\n';
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值