// Copyright (C) 2017 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include #include #include QT_BEGIN_NAMESPACE class QSslError; QT_END_NAMESPACE using namespace std; class DownloadManager: public QObject { Q_OBJECT QNetworkAccessManager manager; QList 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 &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 &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, SLOT(execute())); app.exec(); } #include "main.moc"