QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgsalgorithmfiledownloader.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmfiledownloader.cpp
3 ---------------------
4 begin : October 2017
5 copyright : (C) 2017 by Etienne Trimaille
6 email : etienne at kartoza dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19
20#include "qgis.h"
21#include "qgsfiledownloader.h"
22#include "qgsfileutils.h"
24
25#include <QEventLoop>
26#include <QFileInfo>
27#include <QTimer>
28#include <QUrl>
29
30#include "moc_qgsalgorithmfiledownloader.cpp"
31
33
34QString QgsFileDownloaderAlgorithm::name() const
35{
36 return QStringLiteral( "filedownloader" );
37}
38
39QString QgsFileDownloaderAlgorithm::displayName() const
40{
41 return tr( "Download file via HTTP(S)" );
42}
43
44QString QgsFileDownloaderAlgorithm::shortDescription() const
45{
46 return tr( "Downloads a URL to the file system with an HTTP(S) GET or POST request." );
47}
48
49QStringList QgsFileDownloaderAlgorithm::tags() const
50{
51 return tr( "file,downloader,internet,url,fetch,get,post,request,https" ).split( ',' );
52}
53
54QString QgsFileDownloaderAlgorithm::group() const
55{
56 return tr( "File tools" );
57}
58
59QString QgsFileDownloaderAlgorithm::groupId() const
60{
61 return QStringLiteral( "filetools" );
62}
63
64QString QgsFileDownloaderAlgorithm::shortHelpString() const
65{
66 return tr( "This algorithm downloads a URL to the file system with an HTTP(S) GET or POST request" );
67}
68
69QgsFileDownloaderAlgorithm *QgsFileDownloaderAlgorithm::createInstance() const
70{
71 return new QgsFileDownloaderAlgorithm();
72}
73
74void QgsFileDownloaderAlgorithm::initAlgorithm( const QVariantMap & )
75{
76 addParameter( new QgsProcessingParameterString( QStringLiteral( "URL" ), tr( "URL" ), QVariant(), false, false ) );
77
78 auto methodParam = std::make_unique<QgsProcessingParameterEnum>(
79 QStringLiteral( "METHOD" ),
80 QObject::tr( "Method" ),
81 QStringList()
82 << QObject::tr( "GET" )
83 << QObject::tr( "POST" ),
84 false,
85 0
86 );
87 methodParam->setHelp( QObject::tr( "The HTTP method to use for the request" ) );
88 methodParam->setFlags( methodParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
89 addParameter( methodParam.release() );
90
91 auto dataParam = std::make_unique<QgsProcessingParameterString>(
92 QStringLiteral( "DATA" ), tr( "Data" ), QVariant(), false, true
93 );
94 dataParam->setHelp( QObject::tr( "The data to add in the body if the request is a POST" ) );
95 dataParam->setFlags( dataParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
96 addParameter( dataParam.release() );
97 addParameter( new QgsProcessingParameterFileDestination( QStringLiteral( "OUTPUT" ), tr( "File destination" ), QObject::tr( "All files (*.*)" ), QVariant(), false ) );
98}
99
100QVariantMap QgsFileDownloaderAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
101{
102 mFeedback = feedback;
103 QString url = parameterAsString( parameters, QStringLiteral( "URL" ), context );
104 if ( url.isEmpty() )
105 throw QgsProcessingException( tr( "No URL specified" ) );
106
107 QString data = parameterAsString( parameters, QStringLiteral( "DATA" ), context );
108 QString outputFile = parameterAsFileOutput( parameters, QStringLiteral( "OUTPUT" ), context );
109
110 QEventLoop loop;
111 QTimer timer;
112 QUrl downloadedUrl;
113 QStringList errors;
114
115 Qgis::HttpMethod httpMethod = static_cast<Qgis::HttpMethod>( parameterAsEnum( parameters, QStringLiteral( "METHOD" ), context ) );
116
117 if ( httpMethod == Qgis::HttpMethod::Get && !data.isEmpty() )
118 {
119 feedback->pushWarning( tr( "DATA parameter is not used when it's a GET request." ) );
120 data = QString();
121 }
122
123 QgsFileDownloader *downloader = new QgsFileDownloader( QUrl( url ), outputFile, QString(), true, httpMethod, data.toUtf8() );
124 connect( mFeedback, &QgsFeedback::canceled, downloader, &QgsFileDownloader::cancelDownload );
125 connect( downloader, &QgsFileDownloader::downloadError, this, [&errors, &loop]( const QStringList &e ) { errors = e; loop.exit(); } );
126 connect( downloader, &QgsFileDownloader::downloadProgress, this, &QgsFileDownloaderAlgorithm::receiveProgressFromDownloader );
127 connect( downloader, &QgsFileDownloader::downloadCompleted, this, [&downloadedUrl]( const QUrl url ) { downloadedUrl = url; } );
128 connect( downloader, &QgsFileDownloader::downloadExited, this, [&loop]() { loop.exit(); } );
129 connect( &timer, &QTimer::timeout, this, &QgsFileDownloaderAlgorithm::sendProgressFeedback );
130 downloader->startDownload();
131 timer.start( 1000 );
132
133 loop.exec();
134
135 timer.stop();
136 if ( errors.size() > 0 )
137 throw QgsProcessingException( errors.join( '\n' ) );
138
139 const bool exists = QFileInfo::exists( outputFile );
140 if ( !feedback->isCanceled() && !exists )
141 throw QgsProcessingException( tr( "Output file doesn't exist." ) );
142
143 url = downloadedUrl.toDisplayString();
144 feedback->pushInfo( QObject::tr( "Successfully downloaded %1" ).arg( url ) );
145
146 if ( parameters.value( QStringLiteral( "OUTPUT" ) ) == QgsProcessing::TEMPORARY_OUTPUT )
147 {
148 // the output is temporary and its file name automatically generated, try to add a file extension
149 const int length = url.size();
150 const int lastDotIndex = url.lastIndexOf( "." );
151 const int lastSlashIndex = url.lastIndexOf( "/" );
152 if ( lastDotIndex > -1 && lastDotIndex > lastSlashIndex && length - lastDotIndex <= 6 )
153 {
154 QFile tmpFile( outputFile );
155 tmpFile.rename( tmpFile.fileName() + url.mid( lastDotIndex ) );
156 outputFile += url.mid( lastDotIndex );
157 }
158 }
159
160 QVariantMap outputs;
161 outputs.insert( QStringLiteral( "OUTPUT" ), exists ? outputFile : QString() );
162 return outputs;
163}
164
165void QgsFileDownloaderAlgorithm::sendProgressFeedback()
166{
167 if ( !mReceived.isEmpty() && mLastReport != mReceived )
168 {
169 mLastReport = mReceived;
170 if ( mTotal.isEmpty() )
171 mFeedback->pushInfo( tr( "%1 downloaded" ).arg( mReceived ) );
172 else
173 mFeedback->pushInfo( tr( "%1 of %2 downloaded" ).arg( mReceived, mTotal ) );
174 }
175}
176
177void QgsFileDownloaderAlgorithm::receiveProgressFromDownloader( qint64 bytesReceived, qint64 bytesTotal )
178{
179 mReceived = QgsFileUtils::representFileSize( bytesReceived );
180 if ( bytesTotal > 0 )
181 {
182 if ( mTotal.isEmpty() )
183 mTotal = QgsFileUtils::representFileSize( bytesTotal );
184
185 mFeedback->setProgress( ( bytesReceived * 100 ) / bytesTotal );
186 }
187}
188
HttpMethod
Different methods of HTTP requests.
Definition qgis.h:1037
@ Get
GET method.
Definition qgis.h:1038
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:3763
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
void canceled()
Internal routines can connect to this signal if they use event loop.
A utility class for downloading files.
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 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.
static QString representFileSize(qint64 bytes)
Returns the human size from bytes.
Contains information about the context in which a processing algorithm is executed.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
virtual void pushWarning(const QString &warning)
Pushes a warning informational message from the algorithm.
A generic file based destination parameter, for specifying the destination path for a file (non-map l...
A string parameter for processing algorithms.
static const QString TEMPORARY_OUTPUT
Constant used to indicate that a Processing algorithm output should be a temporary layer/file.