QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
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, qgis::overload< QNetworkReply *>::of( &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  for ( auto end = errors.size(), i = 0; i != end; ++i )
105  {
106  errorMessages << errors[i].errorString();
107  }
108  error( errorMessages );
109  }
110 }
111 #endif
112 
113 
114 void QgsFileDownloader::error( const QStringList &errorMessages )
115 {
116  for ( auto end = errorMessages.size(), i = 0; i != end; ++i )
117  {
118  mErrors << errorMessages[i];
119  }
120  if ( mReply )
121  mReply->abort();
122  emit downloadError( mErrors );
123 }
124 
125 void QgsFileDownloader::error( const QString &errorMessage )
126 {
127  error( QStringList() << errorMessage );
128 }
129 
130 void QgsFileDownloader::onReadyRead()
131 {
132  Q_ASSERT( mReply );
133  if ( mFile.fileName().isEmpty() )
134  {
135  error( tr( "No output filename specified" ) );
136  onFinished();
137  }
138  else if ( ! mFile.isOpen() && ! mFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
139  {
140  error( tr( "Cannot open output file: %1" ).arg( mFile.fileName() ) );
141  onFinished();
142  }
143  else
144  {
145  QByteArray data = mReply->readAll();
146  mFile.write( data );
147  }
148 }
149 
150 void QgsFileDownloader::onFinished()
151 {
152  // when canceled
153  if ( ! mErrors.isEmpty() || mDownloadCanceled )
154  {
155  if ( mFile.isOpen() )
156  mFile.close();
157  if ( mFile.exists() )
158  mFile.remove();
159  }
160  else
161  {
162  // download finished normally
163  if ( mFile.isOpen() )
164  {
165  mFile.flush();
166  mFile.close();
167  }
168 
169  // get redirection url
170  QVariant redirectionTarget = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
171  if ( mReply->error() )
172  {
173  mFile.remove();
174  error( tr( "Download failed: %1" ).arg( mReply->errorString() ) );
175  }
176  else if ( !redirectionTarget.isNull() )
177  {
178  QUrl newUrl = mUrl.resolved( redirectionTarget.toUrl() );
179  mUrl = newUrl;
180  mReply->deleteLater();
181  if ( !mFile.open( QIODevice::WriteOnly ) )
182  {
183  mFile.remove();
184  error( tr( "Cannot open output file: %1" ).arg( mFile.fileName() ) );
185  }
186  else
187  {
188  mFile.resize( 0 );
189  mFile.close();
190  startDownload();
191  }
192  return;
193  }
194  else
195  {
196  emit downloadCompleted( mReply->url() );
197  }
198  }
199  emit downloadExited();
200  this->deleteLater();
201 }
202 
203 
204 void QgsFileDownloader::onDownloadProgress( qint64 bytesReceived, qint64 bytesTotal )
205 {
206  if ( mDownloadCanceled )
207  {
208  return;
209  }
210  emit downloadProgress( bytesReceived, bytesTotal );
211 }
212 
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)