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