Delete the Network Download (Manager)? examples

Their use of QtNetwork is already covered by the HTTP example.
While showcasing that QNAM easily deals with multiple simultaneous
requests, waiting until finished() is emitted to write anything is not
exactly idiomatic.
And managing your own queue to only have one request running at a time
is a weird example for an asynchronous framework.

In this regard, having an example for a complete download manager
(with a GUI) would be interesting, but may ultimately be very
time-consuming to make for limited gain.

Task-number: QTBUG-110643
Pick-to: 6.5
Change-Id: I6b2c1546b85fa89ab7ce1ff5565b0293b5710b74
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
Reviewed-by: Ievgenii Meshcheriakov <ievgenii.meshcheriakov@qt.io>
Reviewed-by: Konrad Kujawa <konrad.kujawa@qt.io>
This commit is contained in:
Mårten Nordheim 2023-02-27 15:16:34 +01:00
parent 8dbceaa398
commit eb61d49ab3
15 changed files with 0 additions and 624 deletions

View File

@ -4,8 +4,6 @@
if(NOT TARGET Qt6::Network)
return()
endif()
qt_internal_add_example(download)
qt_internal_add_example(downloadmanager)
if(NOT INTEGRITY)
qt_internal_add_example(dnslookup)
endif()

View File

@ -1,17 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example download
\title Network Download Example
\brief Demonstrates how to use networking APIs for multiple downloads.
\ingroup examples-network
The Network Download example shows how to perform multiple downloads in
parallel using the QNetworkAccessManager class.
This example is designed to be run from the command-line.
The \l{Network Download Manager Example} implements a more complex system
that places files in a queue for sequential downloading.
*/

View File

@ -1,17 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example downloadmanager
\title Network Download Manager Example
\brief Demonstrates how to use the networking APIs for multiple downloads.
\ingroup examples-network
The Network Download example shows how to implement a queue for multiple
downloads using the QNetworkAccessManager class.
This example is designed to be run from the command-line.
See the \l{Network Download Example} for a simpler version of this example
that obtains multiple files in parallel.
*/

View File

@ -1,30 +0,0 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(download LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/network/download")
find_package(Qt6 REQUIRED COMPONENTS Core Network)
qt_standard_project_setup()
qt_add_executable(download
main.cpp
)
target_link_libraries(download PRIVATE
Qt6::Core
Qt6::Network
)
install(TARGETS download
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -1,8 +0,0 @@
QT = core network
CONFIG += cmdline
SOURCES += main.cpp
# install
target.path = $$[QT_INSTALL_EXAMPLES]/network/download
INSTALLS += target

View File

@ -1,166 +0,0 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtCore>
#include <QtNetwork>
#include <cstdio>
QT_BEGIN_NAMESPACE
class QSslError;
QT_END_NAMESPACE
using namespace std;
class DownloadManager: public QObject
{
Q_OBJECT
QNetworkAccessManager manager;
QList<QNetworkReply *> currentDownloads;
public:
DownloadManager();
void doDownload(const QUrl &url);
static QString saveFileName(const QUrl &url);
bool saveToDisk(const QString &filename, QIODevice *data);
static bool isHttpRedirect(QNetworkReply *reply);
public slots:
void execute();
void downloadFinished(QNetworkReply *reply);
void sslErrors(const QList<QSslError> &errors);
};
DownloadManager::DownloadManager()
{
connect(&manager, &QNetworkAccessManager::finished,
this, &DownloadManager::downloadFinished);
}
void DownloadManager::doDownload(const QUrl &url)
{
QNetworkRequest request(url);
QNetworkReply *reply = manager.get(request);
#if QT_CONFIG(ssl)
connect(reply, &QNetworkReply::sslErrors,
this, &DownloadManager::sslErrors);
#endif
currentDownloads.append(reply);
}
QString DownloadManager::saveFileName(const QUrl &url)
{
QString path = url.path();
QString basename = QFileInfo(path).fileName();
if (basename.isEmpty())
basename = "download";
if (QFile::exists(basename)) {
// already exists, don't overwrite
int i = 0;
basename += '.';
while (QFile::exists(basename + QString::number(i)))
++i;
basename += QString::number(i);
}
return basename;
}
bool DownloadManager::saveToDisk(const QString &filename, QIODevice *data)
{
QFile file(filename);
if (!file.open(QIODevice::WriteOnly)) {
fprintf(stderr, "Could not open %s for writing: %s\n",
qPrintable(filename),
qPrintable(file.errorString()));
return false;
}
file.write(data->readAll());
file.close();
return true;
}
bool DownloadManager::isHttpRedirect(QNetworkReply *reply)
{
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
return statusCode == 301 || statusCode == 302 || statusCode == 303
|| statusCode == 305 || statusCode == 307 || statusCode == 308;
}
void DownloadManager::execute()
{
QStringList args = QCoreApplication::instance()->arguments();
args.takeFirst(); // skip the first argument, which is the program's name
if (args.isEmpty()) {
printf("Qt Download example - downloads all URLs in parallel\n"
"Usage: download url1 [url2... urlN]\n"
"\n"
"Downloads the URLs passed in the command-line to the local directory\n"
"If the target file already exists, a .0, .1, .2, etc. is appended to\n"
"differentiate.\n");
QCoreApplication::instance()->quit();
return;
}
for (const QString &arg : std::as_const(args)) {
QUrl url = QUrl::fromEncoded(arg.toLocal8Bit());
doDownload(url);
}
}
void DownloadManager::sslErrors(const QList<QSslError> &sslErrors)
{
#if QT_CONFIG(ssl)
for (const QSslError &error : sslErrors)
fprintf(stderr, "SSL error: %s\n", qPrintable(error.errorString()));
#else
Q_UNUSED(sslErrors);
#endif
}
void DownloadManager::downloadFinished(QNetworkReply *reply)
{
QUrl url = reply->url();
if (reply->error()) {
fprintf(stderr, "Download of %s failed: %s\n",
url.toEncoded().constData(),
qPrintable(reply->errorString()));
} else {
if (isHttpRedirect(reply)) {
fputs("Request was redirected.\n", stderr);
} else {
QString filename = saveFileName(url);
if (saveToDisk(filename, reply)) {
printf("Download of %s succeeded (saved to %s)\n",
url.toEncoded().constData(), qPrintable(filename));
}
}
}
currentDownloads.removeAll(reply);
reply->deleteLater();
if (currentDownloads.isEmpty()) {
// all downloads finished
QCoreApplication::instance()->quit();
}
}
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
DownloadManager manager;
QTimer::singleShot(0, &manager, &DownloadManager::execute);
app.exec();
}
#include "main.moc"

View File

@ -1,32 +0,0 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(downloadmanager LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/network/downloadmanager")
find_package(Qt6 REQUIRED COMPONENTS Core Network)
qt_standard_project_setup()
qt_add_executable(downloadmanager
downloadmanager.cpp downloadmanager.h
main.cpp
textprogressbar.cpp textprogressbar.h
)
target_link_libraries(downloadmanager PRIVATE
Qt6::Core
Qt6::Network
)
install(TARGETS downloadmanager
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -1,165 +0,0 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "downloadmanager.h"
#include <QTextStream>
#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)
{
QString path = url.path();
QString basename = QFileInfo(path).fileName();
if (basename.isEmpty())
basename = "download";
if (QFile::exists(basename)) {
// already exists, don't overwrite
int i = 0;
basename += '.';
while (QFile::exists(basename + QString::number(i)))
++i;
basename += QString::number(i);
}
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);
output.setFileName(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)
{
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';
}

View File

@ -1,46 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef DOWNLOADMANAGER_H
#define DOWNLOADMANAGER_H
#include <QtNetwork>
#include <QtCore>
#include "textprogressbar.h"
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

View File

@ -1,17 +0,0 @@
QT = core network
CONFIG += cmdline
HEADERS += downloadmanager.h textprogressbar.h
SOURCES += downloadmanager.cpp main.cpp textprogressbar.cpp
# install
target.path = $$[QT_INSTALL_EXAMPLES]/network/downloadmanager
INSTALLS += target
OTHER_FILES += \
debian/changelog \
debian/compat \
debian/control \
debian/copyright \
debian/README \
debian/rules

View File

@ -1,35 +0,0 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QCoreApplication>
#include <QStringList>
#include "downloadmanager.h"
#include <cstdio>
int main(int argc, char **argv)
{
using namespace std;
QCoreApplication app(argc, argv);
QStringList arguments = app.arguments();
arguments.takeFirst(); // remove the first argument, which is the program's name
if (arguments.isEmpty()) {
printf("Qt Download example\n"
"Usage: downloadmanager url1 [url2... urlN]\n"
"\n"
"Downloads the URLs passed in the command-line to the local directory\n"
"If the target file already exists, a .0, .1, .2, etc. is appended to\n"
"differentiate.\n");
return 0;
}
DownloadManager manager;
manager.append(arguments);
QObject::connect(&manager, &DownloadManager::finished,
&app, &QCoreApplication::quit);
app.exec();
}

View File

@ -1,60 +0,0 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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;
}

View File

@ -1,24 +0,0 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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

View File

@ -2,9 +2,6 @@ requires(qtHaveModule(network))
TEMPLATE = subdirs
QT_FOR_CONFIG += network-private
SUBDIRS = \
download \
downloadmanager
!integrity: SUBDIRS += dnslookup
qtHaveModule(widgets) {
SUBDIRS += \

View File

@ -22,8 +22,6 @@
\li \l{network/blockingfortuneclient}{Blocking Fortune Client}\raisedaster
\li \l{network/broadcastreceiver}{Broadcast Receiver}
\li \l{network/broadcastsender}{Broadcast Sender}
\li \l{network/download}{Download}
\li \l{network/downloadmanager}{Download Manager}
\li \l{network/network-chat}{Network Chat}
\li \l{network/fortuneclient}{Fortune Client}\raisedaster
\li \l{network/fortuneserver}{Fortune Server}\raisedaster