QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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, Qgis::HttpMethod httpMethod, const QByteArray &data )
29  : mUrl( url )
30  , mDownloadCanceled( false )
31  , mHttpMethod( httpMethod )
32  , mData( data )
33 {
34  mFile.setFileName( outputFileName );
35  mAuthCfg = authcfg;
36  if ( !delayStart )
37  startDownload();
38 }
39 
40 
42 {
43  if ( mReply )
44  {
45  mReply->abort();
46  mReply->deleteLater();
47  }
48 }
49 
51 {
53 
54  QNetworkRequest request( mUrl );
55  QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsFileDownloader" ) );
56  if ( !mAuthCfg.isEmpty() )
57  {
58  QgsApplication::authManager()->updateNetworkRequest( request, mAuthCfg );
59  }
60 
61  if ( mReply )
62  {
63  disconnect( mReply, &QNetworkReply::readyRead, this, &QgsFileDownloader::onReadyRead );
64  disconnect( mReply, &QNetworkReply::finished, this, &QgsFileDownloader::onFinished );
65  disconnect( mReply, &QNetworkReply::downloadProgress, this, &QgsFileDownloader::onDownloadProgress );
66  mReply->abort();
67  mReply->deleteLater();
68  }
69 
70  switch ( mHttpMethod )
71  {
73  {
74  mReply = nam->get( request );
75  break;
76  }
78  {
79  mReply = nam->post( request, mData );
80  break;
81  }
82  }
83 
84  if ( !mAuthCfg.isEmpty() )
85  {
86  QgsApplication::authManager()->updateNetworkReply( mReply, mAuthCfg );
87  }
88 
89  connect( mReply, &QNetworkReply::readyRead, this, &QgsFileDownloader::onReadyRead );
90  connect( mReply, &QNetworkReply::finished, this, &QgsFileDownloader::onFinished );
91  connect( mReply, &QNetworkReply::downloadProgress, this, &QgsFileDownloader::onDownloadProgress );
92  connect( nam, qOverload< QNetworkReply *>( &QgsNetworkAccessManager::requestTimedOut ), this, &QgsFileDownloader::onRequestTimedOut, Qt::UniqueConnection );
93 #ifndef QT_NO_SSL
94  connect( nam, &QgsNetworkAccessManager::sslErrors, this, &QgsFileDownloader::onSslErrors, Qt::UniqueConnection );
95 #endif
96 }
97 
99 {
100  mDownloadCanceled = true;
101  emit downloadCanceled();
102  onFinished();
103 }
104 
105 void QgsFileDownloader::onRequestTimedOut( QNetworkReply *reply )
106 {
107  if ( reply == mReply )
108  error( tr( "Network request %1 timed out" ).arg( mUrl.toString() ) );
109 }
110 
111 #ifndef QT_NO_SSL
112 void QgsFileDownloader::onSslErrors( QNetworkReply *reply, const QList<QSslError> &errors )
113 {
114  if ( reply == mReply )
115  {
116  QStringList errorMessages;
117  errorMessages.reserve( errors.size() + 1 );
118  errorMessages << QStringLiteral( "SSL Errors: " );
119 
120  for ( const QSslError &error : errors )
121  errorMessages << error.errorString();
122 
123  error( errorMessages );
124  }
125 }
126 #endif
127 
128 
129 void QgsFileDownloader::error( const QStringList &errorMessages )
130 {
131  for ( const QString &error : errorMessages )
132  mErrors << error;
133 
134  if ( mReply )
135  mReply->abort();
136  emit downloadError( mErrors );
137 }
138 
139 void QgsFileDownloader::error( const QString &errorMessage )
140 {
141  error( QStringList() << errorMessage );
142 }
143 
144 void QgsFileDownloader::onReadyRead()
145 {
146  Q_ASSERT( mReply );
147  if ( mFile.fileName().isEmpty() )
148  {
149  error( tr( "No output filename specified" ) );
150  onFinished();
151  }
152  else if ( ! mFile.isOpen() && ! mFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
153  {
154  error( tr( "Cannot open output file: %1" ).arg( mFile.fileName() ) );
155  onFinished();
156  }
157  else
158  {
159  const QByteArray data = mReply->readAll();
160  mFile.write( data );
161  }
162 }
163 
164 void QgsFileDownloader::onFinished()
165 {
166  // when canceled
167  if ( ! mErrors.isEmpty() || mDownloadCanceled )
168  {
169  if ( mFile.isOpen() )
170  mFile.close();
171  if ( mFile.exists() )
172  mFile.remove();
173  }
174  else
175  {
176  // download finished normally
177  if ( mFile.isOpen() )
178  {
179  mFile.flush();
180  mFile.close();
181  }
182 
183  // get redirection url
184  const QVariant redirectionTarget = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
185  if ( mReply->error() )
186  {
187  mFile.remove();
188  error( tr( "Download failed: %1" ).arg( mReply->errorString() ) );
189  }
190  else if ( !redirectionTarget.isNull() )
191  {
192  const QUrl newUrl = mUrl.resolved( redirectionTarget.toUrl() );
193  mUrl = newUrl;
194  mReply->deleteLater();
195  if ( !mFile.open( QIODevice::WriteOnly ) )
196  {
197  mFile.remove();
198  error( tr( "Cannot open output file: %1" ).arg( mFile.fileName() ) );
199  }
200  else
201  {
202  mFile.resize( 0 );
203  mFile.close();
204  startDownload();
205  }
206  return;
207  }
208  else
209  {
210  emit downloadCompleted( mReply->url() );
211  }
212  }
213  emit downloadExited();
214  this->deleteLater();
215 }
216 
217 
218 void QgsFileDownloader::onDownloadProgress( qint64 bytesReceived, qint64 bytesTotal )
219 {
220  if ( mDownloadCanceled )
221  {
222  return;
223  }
224  emit downloadProgress( bytesReceived, bytesTotal );
225 }
226 
HttpMethod
Different methods of HTTP requests.
Definition: qgis.h:349
@ Post
POST method.
@ Get
GET method.
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,...
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.
QgsFileDownloader(const QUrl &url, const QString &outputFileName, const QString &authcfg=QString(), bool delayStart=false, Qgis::HttpMethod httpMethod=Qgis::HttpMethod::Get, const QByteArray &data=QByteArray())
QgsFileDownloader.
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)