QGCFileDownload.cc 4.8 KB
Newer Older
1
2
/****************************************************************************
 *
Gus Grubba's avatar
Gus Grubba committed
3
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
4
5
6
7
8
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/
Don Gagne's avatar
Don Gagne committed
9
10
11
12
13
14


#include "QGCFileDownload.h"

#include <QFileInfo>
#include <QStandardPaths>
Don Gagne's avatar
Don Gagne committed
15
#include <QNetworkProxy>
Don Gagne's avatar
Don Gagne committed
16
17
18
19
20
21
22

QGCFileDownload::QGCFileDownload(QObject* parent)
    : QNetworkAccessManager(parent)
{

}

Don Gagne's avatar
Don Gagne committed
23
bool QGCFileDownload::download(const QString& remoteFile, bool redirect)
Don Gagne's avatar
Don Gagne committed
24
{
Don Gagne's avatar
Don Gagne committed
25
26
27
28
    if (!redirect) {
        _originalRemoteFile = remoteFile;
    }

Don Gagne's avatar
Don Gagne committed
29
30
31
32
33
34
35
36
37
38
39
40
    if (remoteFile.isEmpty()) {
        qWarning() << "downloadFile empty";
        return false;
    }
    
    // Split out filename from path
    QString remoteFileName = QFileInfo(remoteFile).fileName();
    if (remoteFileName.isEmpty()) {
        qWarning() << "Unabled to parse filename from downloadFile" << remoteFile;
        return false;
    }

Don Gagne's avatar
Don Gagne committed
41
42
43
44
45
46
    // Strip out parameters from remote filename
    int parameterIndex = remoteFileName.indexOf("?");
    if (parameterIndex != -1) {
        remoteFileName  = remoteFileName.left(parameterIndex);
    }

Don Gagne's avatar
Don Gagne committed
47
48
49
50
51
52
53
54
55
56
57
58
    // Determine location to download file to
    QString localFile = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
    if (localFile.isEmpty()) {
        localFile = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
        if (localFile.isEmpty()) {
            qDebug() << "Unabled to find writable download location. Tried downloads and temp directory.";
            return false;
        }
    }
    localFile += "/"  + remoteFileName;

    QUrl remoteUrl;
Don Gagne's avatar
Don Gagne committed
59
    if (remoteFile.startsWith("http:") || remoteFile.startsWith("https:")) {
Don Gagne's avatar
Don Gagne committed
60
61
62
63
64
65
66
67
68
69
        remoteUrl.setUrl(remoteFile);
    } else {
        remoteUrl = QUrl::fromLocalFile(remoteFile);
    }
    if (!remoteUrl.isValid()) {
        qWarning() << "Remote URL is invalid" << remoteFile;
        return false;
    }
    
    QNetworkRequest networkRequest(remoteUrl);
Don Gagne's avatar
Don Gagne committed
70
71
72
73

    QNetworkProxy tProxy;
    tProxy.setType(QNetworkProxy::DefaultProxy);
    setProxy(tProxy);
Don Gagne's avatar
Don Gagne committed
74
75
76
    
    // Store local file location in user attribute so we can retrieve when the download finishes
    networkRequest.setAttribute(QNetworkRequest::User, localFile);
Don Gagne's avatar
Don Gagne committed
77

Don Gagne's avatar
Don Gagne committed
78
79
80
81
82
83
84
85
    QNetworkReply* networkReply = get(networkRequest);
    if (!networkReply) {
        qWarning() << "QNetworkAccessManager::get failed";
        return false;
    }

    connect(networkReply, &QNetworkReply::downloadProgress, this, &QGCFileDownload::downloadProgress);
    connect(networkReply, &QNetworkReply::finished, this, &QGCFileDownload::_downloadFinished);
86
87
    connect(networkReply, static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error),
            this, &QGCFileDownload::_downloadError);
Don Gagne's avatar
Don Gagne committed
88
89
90
91
92
93
94
95
96
97

    return true;
}

void QGCFileDownload::_downloadFinished(void)
{
    QNetworkReply* reply = qobject_cast<QNetworkReply*>(QObject::sender());
    
    // When an error occurs or the user cancels the download, we still end up here. So bail out in
    // those cases.
DonLakeFlyer's avatar
DonLakeFlyer committed
98
99
    if (reply->error() != QNetworkReply::NoError) {        
        reply->deleteLater();
Don Gagne's avatar
Don Gagne committed
100
101
        return;
    }
Don Gagne's avatar
Don Gagne committed
102
103
104
105
106
107
108
109
110
111

    // Check for redirection
    QVariant redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
    if (!redirectionTarget.isNull()) {
        QUrl redirectUrl = reply->url().resolved(redirectionTarget.toUrl());
        download(redirectUrl.toString(), true /* redirect */);
        reply->deleteLater();
        return;
    }

Don Gagne's avatar
Don Gagne committed
112
113
114
    // Download file location is in user attribute
    QString downloadFilename = reply->request().attribute(QNetworkRequest::User).toString();

DonLakeFlyer's avatar
DonLakeFlyer committed
115
116
117
118
    if (!downloadFilename.isEmpty()) {
        // Store downloaded file in download location
        QFile file(downloadFilename);
        if (!file.open(QIODevice::WriteOnly)) {
DonLakeFlyer's avatar
DonLakeFlyer committed
119
            emit error(tr("Could not save downloaded file to %1. Error: %2").arg(downloadFilename).arg(file.errorString()));
DonLakeFlyer's avatar
DonLakeFlyer committed
120
121
122
123
124
125
126
127
128
129
130
131
            return;
        }

        file.write(reply->readAll());
        file.close();

        emit downloadFinished(_originalRemoteFile, downloadFilename);
    } else {
        QString errorMsg = "Internal error";
        qWarning() << errorMsg;
        emit error(errorMsg);
    }
DonLakeFlyer's avatar
DonLakeFlyer committed
132
133

    reply->deleteLater();
Don Gagne's avatar
Don Gagne committed
134
135
136
137
138
139
140
141
}

/// @brief Called when an error occurs during download
void QGCFileDownload::_downloadError(QNetworkReply::NetworkError code)
{
    QString errorMsg;
    
    if (code == QNetworkReply::OperationCanceledError) {
DonLakeFlyer's avatar
DonLakeFlyer committed
142
        errorMsg = tr("Download cancelled");
143
144

    } else if (code == QNetworkReply::ContentNotFoundError) {
DonLakeFlyer's avatar
DonLakeFlyer committed
145
        errorMsg = tr("Error: File Not Found");
146

Don Gagne's avatar
Don Gagne committed
147
    } else {
DonLakeFlyer's avatar
DonLakeFlyer committed
148
        errorMsg = tr("Error during download. Error: %1").arg(code);
Don Gagne's avatar
Don Gagne committed
149
150
151
152
    }

    emit error(errorMsg);
}