新建 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';
}