QGIS API Documentation  3.20.0-Odense (decaadbb31)
qgsfiledownloader.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsfiledownloader.cpp
3  --------------------------------------
4  Date : November 2016
5  Copyright : (C) 2016 by Alessandro Pasotti
6  Email : apasotti at boundlessgeo dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsfiledownloader.h"
18 #include "qgsapplication.h"
19 #include "qgsauthmanager.h"
20 
21 #include <QNetworkAccessManager>
22 #include <QNetworkRequest>
23 #include <QNetworkReply>
24 #ifndef QT_NO_SSL
25 #include <QSslError>
26 #endif
27 
28 QgsFileDownloader::QgsFileDownloader( const QUrl &url, const QString &outputFileName, const QString &authcfg, bool delayStart )
29  : mUrl( url )
30  , mDownloadCanceled( false )
31 {
32  mFile.setFileName( outputFileName );
33  mAuthCfg = authcfg;
34  if ( !delayStart )
35  startDownload();
36 }
37 
38 
40 {
41  if ( mReply )
42  {
43  mReply->abort();
44  mReply->deleteLater();
45  }
46 }
47 
49 {
51 
52  QNetworkRequest request( mUrl );
53  QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsFileDownloader" ) );
54  if ( !mAuthCfg.isEmpty() )
55  {
56  QgsApplication::authManager()->updateNetworkRequest( request, mAuthCfg );
57  }
58 
59  if ( mReply )
60  {
61  disconnect( mReply, &QNetworkReply::readyRead, this, &QgsFileDownloader::onReadyRead );
62  disconnect( mReply, &QNetworkReply::finished, this, &QgsFileDownloader::onFinished );
63  disconnect( mReply, &QNetworkReply::downloadProgress, this, &QgsFileDownloader::onDownloadProgress );
64  mReply->abort();
65  mReply->deleteLater();
66  }
67 
68  mReply = nam->get( request );
69  if ( !mAuthCfg.isEmpty() )
70  {
71  QgsApplication::authManager()->updateNetworkReply( mReply, mAuthCfg );
72  }
73 
74  connect( mReply, &QNetworkReply::readyRead, this, &QgsFileDownloader::onReadyRead );
75  connect( mReply, &QNetworkReply::finished, this, &QgsFileDownloader::onFinished );
76  connect( mReply, &QNetworkReply::downloadProgress, this, &QgsFileDownloader::onDownloadProgress );
77  connect( nam, qOverload< QNetworkReply *>( &QgsNetworkAccessManager::requestTimedOut ), this, &QgsFileDownloader::onRequestTimedOut, Qt::UniqueConnection );
78 #ifndef QT_NO_SSL
79  connect( nam, &QgsNetworkAccessManager::sslErrors, this, &QgsFileDownloader::onSslErrors, Qt::UniqueConnection );
80 #endif
81 }
82 
84 {
85  mDownloadCanceled = true;
86  emit downloadCanceled();
87  onFinished();
88 }
89 
90 void QgsFileDownloader::onRequestTimedOut( QNetworkReply *reply )
91 {
92  if ( reply == mReply )
93  error( tr( "Network request %1 timed out" ).arg( mUrl.toString() ) );
94 }
95 
96 #ifndef QT_NO_SSL
97 void QgsFileDownloader::onSslErrors( QNetworkReply *reply, const QList<QSslError> &errors )
98 {
99  if ( reply == mReply )
100  {
101  QStringList errorMessages;
102  errorMessages.reserve( errors.size() + 1 );
103  errorMessages << QStringLiteral( "SSL Errors: " );
104 
105  for ( const QSslError &error : errors )
106  errorMessages << error.errorString();
107 
108  error( errorMessages );
109  }
110 }
111 #endif
112 
113 
114 void QgsFileDownloader::error( const QStringList &errorMessages )
115 {
116  for ( const QString &error : errorMessages )
117  mErrors << error;
118 
119  if ( mReply )
120  mReply->abort();
121  emit downloadError( mErrors );
122 }
123 
124 void QgsFileDownloader::error( const QString &errorMessage )
125 {
126  error( QStringList() << errorMessage );
127 }
128 
129 void QgsFileDownloader::onReadyRead()
130 {
131  Q_ASSERT( mReply );
132  if ( mFile.fileName().isEmpty() )
133  {
134  error( tr( "No output filename specified" ) );
135  onFinished();
136  }
137  else if ( ! mFile.isOpen() && ! mFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
138  {
139  error( tr( "Cannot open output file: %1" ).arg( mFile.fileName() ) );
140  onFinished();
141  }
142  else
143  {
144  QByteArray data = mReply->readAll();
145  mFile.write( data );
146  }
147 }
148 
149 void QgsFileDownloader::onFinished()
150 {
151  // when canceled
152  if ( ! mErrors.isEmpty() || mDownloadCanceled )
153  {
154  if ( mFile.isOpen() )
155  mFile.close();
156  if ( mFile.exists() )
157  mFile.remove();
158  }
159  else
160  {
161  // download finished normally
162  if ( mFile.isOpen() )
163  {
164  mFile.flush();
165  mFile.close();
166  }
167 
168  // get redirection url
169  QVariant redirectionTarget = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
170  if ( mReply->error() )
171  {
172  mFile.remove();
173  error( tr( "Download failed: %1" ).arg( mReply->errorString() ) );
174  }
175  else if ( !redirectionTarget.isNull() )
176  {
177  QUrl newUrl = mUrl.resolved( redirectionTarget.toUrl() );
178  mUrl = newUrl;
179  mReply->deleteLater();
180  if ( !mFile.open( QIODevice::WriteOnly ) )
181  {
182  mFile.remove();
183  error( tr( "Cannot open output file: %1" ).arg( mFile.fileName() ) );
184  }
185  else
186  {
187  mFile.resize( 0 );
188  mFile.close();
189  startDownload();
190  }
191  return;
192  }
193  else
194  {
195  emit downloadCompleted( mReply->url() );
196  }
197  }
198  emit downloadExited();
199  this->deleteLater();
200 }
201 
202 
203 void QgsFileDownloader::onDownloadProgress( qint64 bytesReceived, qint64 bytesTotal )
204 {
205  if ( mDownloadCanceled )
206  {
207  return;
208  }
209  emit downloadProgress( bytesReceived, bytesTotal );
210 }
211 
static QgsAuthManager * authManager()
Returns the application's authentication manager instance.
bool updateNetworkRequest(QNetworkRequest &request, const QString &authcfg, const QString &dataprovider=QString())
Provider call to update a QNetworkRequest with an authentication config.
bool updateNetworkReply(QNetworkReply *reply, const QString &authcfg, const QString &dataprovider=QString())
Provider call to update a QNetworkReply with an authentication config (used to skip known SSL errors,...
QgsFileDownloader(const QUrl &url, const QString &outputFileName, const QString &authcfg=QString(), bool delayStart=false)
QgsFileDownloader.
void cancelDownload()
Call to abort the download and delete this object after the cancellation has been processed.
void downloadExited()
Emitted always when the downloader exits.
void downloadCanceled()
Emitted when the download was canceled by the user.
void downloadError(QStringList errorMessages)
Emitted when an error makes the download fail.
void startDownload()
Called to start the download.
void downloadCompleted(const QUrl &url)
Emitted when the download has completed successfully.
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
Emitted when data are ready to be processed.
~QgsFileDownloader() override
network access manager for QGIS
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
void requestTimedOut(QgsNetworkRequestParameters request)
Emitted when a network request has timed out.
#define QgsSetRequestInitiatorClass(request, _class)