QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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 <QString>
28#include <QTimer>
29#include <QUrl>
30
31#include "moc_qgsalgorithmfiledownloader.cpp"
32
33using namespace Qt::StringLiterals;
34
36
37QString QgsFileDownloaderAlgorithm::name() const
38{
39 return u"filedownloader"_s;
40}
41
42QString QgsFileDownloaderAlgorithm::displayName() const
43{
44 return tr( "Download file via HTTP(S)" );
45}
46
47QString QgsFileDownloaderAlgorithm::shortDescription() const
48{
49 return tr( "Downloads a URL to the file system with an HTTP(S) GET or POST request." );
50}
51
52QStringList QgsFileDownloaderAlgorithm::tags() const
53{
54 return tr( "file,downloader,internet,url,fetch,get,post,request,https" ).split( ',' );
55}
56
57QString QgsFileDownloaderAlgorithm::group() const
58{
59 return tr( "File tools" );
60}
61
62QString QgsFileDownloaderAlgorithm::groupId() const
63{
64 return u"filetools"_s;
65}
66
67QString QgsFileDownloaderAlgorithm::shortHelpString() const
68{
69 return tr( "This algorithm downloads a URL to the file system with an HTTP(S) GET or POST request" );
70}
71
72QgsFileDownloaderAlgorithm *QgsFileDownloaderAlgorithm::createInstance() const
73{
74 return new QgsFileDownloaderAlgorithm();
75}
76
77void QgsFileDownloaderAlgorithm::initAlgorithm( const QVariantMap & )
78{
79 addParameter( new QgsProcessingParameterString( u"URL"_s, tr( "URL" ), QVariant(), false, false ) );
80
81 auto methodParam = std::make_unique<QgsProcessingParameterEnum>( u"METHOD"_s, QObject::tr( "Method" ), QStringList() << QObject::tr( "GET" ) << QObject::tr( "POST" ), false, 0 );
82 methodParam->setHelp( QObject::tr( "The HTTP method to use for the request" ) );
83 methodParam->setFlags( methodParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
84 addParameter( methodParam.release() );
85
86 auto dataParam = std::make_unique<QgsProcessingParameterString>( u"DATA"_s, tr( "Data" ), QVariant(), false, true );
87 dataParam->setHelp( QObject::tr( "The data to add in the body if the request is a POST" ) );
88 dataParam->setFlags( dataParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
89 addParameter( dataParam.release() );
90 addParameter( new QgsProcessingParameterFileDestination( u"OUTPUT"_s, tr( "File destination" ), QObject::tr( "All files (*.*)" ), QVariant(), false ) );
91}
92
93QVariantMap QgsFileDownloaderAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
94{
95 mFeedback = feedback;
96 QString url = parameterAsString( parameters, u"URL"_s, context );
97 if ( url.isEmpty() )
98 throw QgsProcessingException( tr( "No URL specified" ) );
99
100 QString data = parameterAsString( parameters, u"DATA"_s, context );
101 QString outputFile = parameterAsFileOutput( parameters, u"OUTPUT"_s, context );
102
103 QEventLoop loop;
104 QTimer timer;
105 QUrl downloadedUrl;
106 QStringList errors;
107
108 Qgis::HttpMethod httpMethod = static_cast<Qgis::HttpMethod>( parameterAsEnum( parameters, u"METHOD"_s, context ) );
109
110 if ( httpMethod == Qgis::HttpMethod::Get && !data.isEmpty() )
111 {
112 feedback->pushWarning( tr( "DATA parameter is not used when it's a GET request." ) );
113 data = QString();
114 }
115
116 QgsFileDownloader *downloader = new QgsFileDownloader( QUrl( url ), outputFile, QString(), true, httpMethod, data.toUtf8() );
117 connect( mFeedback, &QgsFeedback::canceled, downloader, &QgsFileDownloader::cancelDownload );
118 connect( downloader, &QgsFileDownloader::downloadError, this, [&errors, &loop]( const QStringList &e ) {
119 errors = e;
120 loop.exit();
121 } );
122 connect( downloader, &QgsFileDownloader::downloadProgress, this, &QgsFileDownloaderAlgorithm::receiveProgressFromDownloader );
123 connect( downloader, &QgsFileDownloader::downloadCompleted, this, [&downloadedUrl]( const QUrl url ) { downloadedUrl = url; } );
124 connect( downloader, &QgsFileDownloader::downloadExited, this, [&loop]() { loop.exit(); } );
125 connect( &timer, &QTimer::timeout, this, &QgsFileDownloaderAlgorithm::sendProgressFeedback );
126 downloader->startDownload();
127 timer.start( 1000 );
128
129 loop.exec();
130
131 timer.stop();
132 if ( errors.size() > 0 )
133 throw QgsProcessingException( errors.join( '\n' ) );
134
135 const bool exists = QFileInfo::exists( outputFile );
136 if ( !feedback->isCanceled() && !exists )
137 throw QgsProcessingException( tr( "Output file doesn't exist." ) );
138
139 url = downloadedUrl.toDisplayString();
140 feedback->pushInfo( QObject::tr( "Successfully downloaded %1" ).arg( url ) );
141
142 if ( parameters.value( u"OUTPUT"_s ) == QgsProcessing::TEMPORARY_OUTPUT )
143 {
144 // the output is temporary and its file name automatically generated, try to add a file extension
145 const int length = url.size();
146 const int lastDotIndex = url.lastIndexOf( "." );
147 const int lastSlashIndex = url.lastIndexOf( "/" );
148 if ( lastDotIndex > -1 && lastDotIndex > lastSlashIndex && length - lastDotIndex <= 6 )
149 {
150 QFile tmpFile( outputFile );
151 tmpFile.rename( tmpFile.fileName() + url.mid( lastDotIndex ) );
152 outputFile += url.mid( lastDotIndex );
153 }
154 }
155
156 QVariantMap outputs;
157 outputs.insert( u"OUTPUT"_s, exists ? outputFile : QString() );
158 return outputs;
159}
160
161void QgsFileDownloaderAlgorithm::sendProgressFeedback()
162{
163 if ( !mReceived.isEmpty() && mLastReport != mReceived )
164 {
165 mLastReport = mReceived;
166 if ( mTotal.isEmpty() )
167 mFeedback->pushInfo( tr( "%1 downloaded" ).arg( mReceived ) );
168 else
169 mFeedback->pushInfo( tr( "%1 of %2 downloaded" ).arg( mReceived, mTotal ) );
170 }
171}
172
173void QgsFileDownloaderAlgorithm::receiveProgressFromDownloader( qint64 bytesReceived, qint64 bytesTotal )
174{
175 mReceived = QgsFileUtils::representFileSize( bytesReceived );
176 if ( bytesTotal > 0 )
177 {
178 if ( mTotal.isEmpty() )
179 mTotal = QgsFileUtils::representFileSize( bytesTotal );
180
181 mFeedback->setProgress( ( bytesReceived * 100 ) / bytesTotal );
182 }
183}
184
HttpMethod
Different methods of HTTP requests.
Definition qgis.h:1063
@ Get
GET method.
Definition qgis.h:1064
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:3880
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:56
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.