QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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 
48 
50 {
52 
53  QNetworkRequest request( mUrl );
54  QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsFileDownloader" ) );
55  if ( !mAuthCfg.isEmpty() )
56  {
57  QgsApplication::authManager()->updateNetworkRequest( request, mAuthCfg );
58  }
59 
60  if ( mReply )
61  {
62  disconnect( mReply, &QNetworkReply::readyRead, this, &QgsFileDownloader::onReadyRead );
63  disconnect( mReply, &QNetworkReply::finished, this, &QgsFileDownloader::onFinished );
64  disconnect( mReply, &QNetworkReply::downloadProgress, this, &QgsFileDownloader::onDownloadProgress );
65  mReply->abort();
66  mReply->deleteLater();
67  }
68 
69  mReply = nam->get( request );
70  if ( !mAuthCfg.isEmpty() )
71  {
72  QgsApplication::authManager()->updateNetworkReply( mReply, mAuthCfg );
73  }
74 
75  connect( mReply, &QNetworkReply::readyRead, this, &QgsFileDownloader::onReadyRead );
76  connect( mReply, &QNetworkReply::finished, this, &QgsFileDownloader::onFinished );
77  connect( mReply, &QNetworkReply::downloadProgress, this, &QgsFileDownloader::onDownloadProgress );
78  connect( nam, qgis::overload< QNetworkReply *>::of( &QgsNetworkAccessManager::requestTimedOut ), this, &QgsFileDownloader::onRequestTimedOut, Qt::UniqueConnection );
79 #ifndef QT_NO_SSL
80  connect( nam, &QgsNetworkAccessManager::sslErrors, this, &QgsFileDownloader::onSslErrors, Qt::UniqueConnection );
81 #endif
82 }
83 
85 {
86  mDownloadCanceled = true;
87  emit downloadCanceled();
88  onFinished();
89 }
90 
91 void QgsFileDownloader::onRequestTimedOut( QNetworkReply *reply )
92 {
93  if ( reply == mReply )
94  error( tr( "Network request %1 timed out" ).arg( mUrl.toString() ) );
95 }
96 
97 #ifndef QT_NO_SSL
98 void QgsFileDownloader::onSslErrors( QNetworkReply *reply, const QList<QSslError> &errors )
99 {
100  if ( reply == mReply )
101  {
102  QStringList errorMessages;
103  errorMessages.reserve( errors.size() + 1 );
104  errorMessages << QStringLiteral( "SSL Errors: " );
105  for ( auto end = errors.size(), i = 0; i != end; ++i )
106  {
107  errorMessages << errors[i].errorString();
108  }
109  error( errorMessages );
110  }
111 }
112 #endif
113 
114 
115 void QgsFileDownloader::error( const QStringList &errorMessages )
116 {
117  for ( auto end = errorMessages.size(), i = 0; i != end; ++i )
118  {
119  mErrors << errorMessages[i];
120  }
121  if ( mReply )
122  mReply->abort();
123  emit downloadError( mErrors );
124 }
125 
126 void QgsFileDownloader::error( const QString &errorMessage )
127 {
128  error( QStringList() << errorMessage );
129 }
130 
131 void QgsFileDownloader::onReadyRead()
132 {
133  Q_ASSERT( mReply );
134  if ( mFile.fileName().isEmpty() )
135  {
136  error( tr( "No output filename specified" ) );
137  onFinished();
138  }
139  else if ( ! mFile.isOpen() && ! mFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
140  {
141  error( tr( "Cannot open output file: %1" ).arg( mFile.fileName() ) );
142  onFinished();
143  }
144  else
145  {
146  QByteArray data = mReply->readAll();
147  mFile.write( data );
148  }
149 }
150 
151 void QgsFileDownloader::onFinished()
152 {
153  // when canceled
154  if ( ! mErrors.isEmpty() || mDownloadCanceled )
155  {
156  if ( mFile.isOpen() )
157  mFile.close();
158  if ( mFile.exists() )
159  mFile.remove();
160  }
161  else
162  {
163  // download finished normally
164  if ( mFile.isOpen() )
165  {
166  mFile.flush();
167  mFile.close();
168  }
169 
170  // get redirection url
171  QVariant redirectionTarget = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
172  if ( mReply->error() )
173  {
174  mFile.remove();
175  error( tr( "Download failed: %1" ).arg( mReply->errorString() ) );
176  }
177  else if ( !redirectionTarget.isNull() )
178  {
179  QUrl newUrl = mUrl.resolved( redirectionTarget.toUrl() );
180  mUrl = newUrl;
181  mReply->deleteLater();
182  if ( !mFile.open( QIODevice::WriteOnly ) )
183  {
184  mFile.remove();
185  error( tr( "Cannot open output file: %1" ).arg( mFile.fileName() ) );
186  }
187  else
188  {
189  mFile.resize( 0 );
190  mFile.close();
191  startDownload();
192  }
193  return;
194  }
195  else
196  {
197  emit downloadCompleted();
198  }
199  }
200  emit downloadExited();
201  this->deleteLater();
202 }
203 
204 
205 void QgsFileDownloader::onDownloadProgress( qint64 bytesReceived, qint64 bytesTotal )
206 {
207  if ( mDownloadCanceled )
208  {
209  return;
210  }
211  emit downloadProgress( bytesReceived, bytesTotal );
212 }
213 
#define QgsSetRequestInitiatorClass(request, _class)
void startDownload()
Called to start the download.
void requestTimedOut(QgsNetworkRequestParameters request)
Emitted when a network request has timed out.
~QgsFileDownloader() override
QgsFileDownloader(const QUrl &url, const QString &outputFileName, const QString &authcfg=QString(), bool delayStart=false)
QgsFileDownloader.
static QgsAuthManager * authManager()
Returns the application&#39;s authentication manager instance.
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
bool updateNetworkRequest(QNetworkRequest &request, const QString &authcfg, const QString &dataprovider=QString())
Provider call to update a QNetworkRequest with an authentication config.
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
Emitted when data are ready to be processed.
void downloadCanceled()
Emitted when the download was canceled by the user.
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...
void downloadCompleted()
Emitted when the download has completed successfully.
void downloadError(QStringList errorMessages)
Emitted when an error makes the download fail.
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.
network access manager for QGISThis class implements the QGIS network access manager.