QGIS API Documentation  3.20.0-Odense (decaadbb31)
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 "qgsfiledownloader.h"
20 #include "qgsfileutils.h"
21 #include <QEventLoop>
22 #include <QFileInfo>
23 #include <QTimer>
24 #include <QUrl>
25 
27 
28 QString QgsFileDownloaderAlgorithm::name() const
29 {
30  return QStringLiteral( "filedownloader" );
31 }
32 
33 QString QgsFileDownloaderAlgorithm::displayName() const
34 {
35  return tr( "Download file" );
36 }
37 
38 QStringList QgsFileDownloaderAlgorithm::tags() const
39 {
40  return tr( "file,downloader,internet,url,fetch,get,https" ).split( ',' );
41 }
42 
43 QString QgsFileDownloaderAlgorithm::group() const
44 {
45  return tr( "File tools" );
46 }
47 
48 QString QgsFileDownloaderAlgorithm::groupId() const
49 {
50  return QStringLiteral( "filetools" );
51 }
52 
53 QString QgsFileDownloaderAlgorithm::shortHelpString() const
54 {
55  return tr( "This algorithm downloads a URL on the file system." );
56 }
57 
58 QgsFileDownloaderAlgorithm *QgsFileDownloaderAlgorithm::createInstance() const
59 {
60  return new QgsFileDownloaderAlgorithm();
61 }
62 
63 void QgsFileDownloaderAlgorithm::initAlgorithm( const QVariantMap & )
64 {
65  addParameter( new QgsProcessingParameterString( QStringLiteral( "URL" ), tr( "URL" ), QVariant(), false, false ) );
66  addParameter( new QgsProcessingParameterFileDestination( QStringLiteral( "OUTPUT" ),
67  tr( "File destination" ), QObject::tr( "All files (*.*)" ), QVariant(), true ) );
68 }
69 
70 QVariantMap QgsFileDownloaderAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
71 {
72  mFeedback = feedback;
73  QString url = parameterAsString( parameters, QStringLiteral( "URL" ), context );
74  if ( url.isEmpty() )
75  throw QgsProcessingException( tr( "No URL specified" ) );
76  QString outputFile = parameterAsFileOutput( parameters, QStringLiteral( "OUTPUT" ), context );
77 
78  QEventLoop loop;
79  QTimer timer;
80  QUrl downloadedUrl;
81  QgsFileDownloader *downloader = new QgsFileDownloader( QUrl( url ), outputFile, QString(), true );
82  connect( mFeedback, &QgsFeedback::canceled, downloader, &QgsFileDownloader::cancelDownload );
83  connect( downloader, &QgsFileDownloader::downloadError, this, &QgsFileDownloaderAlgorithm::reportErrors );
84  connect( downloader, &QgsFileDownloader::downloadProgress, this, &QgsFileDownloaderAlgorithm::receiveProgressFromDownloader );
85  connect( downloader, &QgsFileDownloader::downloadCompleted, this, [&downloadedUrl]( const QUrl url ) { downloadedUrl = url; } );
86  connect( downloader, &QgsFileDownloader::downloadExited, &loop, &QEventLoop::quit );
87  connect( &timer, &QTimer::timeout, this, &QgsFileDownloaderAlgorithm::sendProgressFeedback );
88  downloader->startDownload();
89  timer.start( 1000 );
90 
91  loop.exec();
92 
93  timer.stop();
94  bool exists = QFileInfo::exists( outputFile );
95  if ( !feedback->isCanceled() && !exists )
96  throw QgsProcessingException( tr( "Output file doesn't exist." ) );
97 
98  url = downloadedUrl.toDisplayString();
99  feedback->pushInfo( QObject::tr( "Successfully downloaded %1" ).arg( url ) );
100 
101  if ( outputFile.startsWith( QgsProcessingUtils::tempFolder() ) )
102  {
103  // the output is temporary and its file name automatically generated, try to add a file extension
104  const int length = url.size();
105  const int lastDotIndex = url.lastIndexOf( "." );
106  const int lastSlashIndex = url.lastIndexOf( "/" );
107  if ( lastDotIndex > -1 && lastDotIndex > lastSlashIndex && length - lastDotIndex <= 6 )
108  {
109  QFile tmpFile( outputFile );
110  tmpFile.rename( tmpFile.fileName() + url.mid( lastDotIndex ) );
111  outputFile += url.mid( lastDotIndex );
112  }
113  }
114 
115  QVariantMap outputs;
116  outputs.insert( QStringLiteral( "OUTPUT" ), exists ? outputFile : QString() );
117  return outputs;
118 }
119 
120 void QgsFileDownloaderAlgorithm::reportErrors( const QStringList &errors )
121 {
122  throw QgsProcessingException( errors.join( '\n' ) );
123 }
124 
125 void QgsFileDownloaderAlgorithm::sendProgressFeedback()
126 {
127  if ( !mReceived.isEmpty() && mLastReport != mReceived )
128  {
129  mLastReport = mReceived;
130  if ( mTotal.isEmpty() )
131  mFeedback->pushInfo( tr( "%1 downloaded." ).arg( mReceived ) );
132  else
133  mFeedback->pushInfo( tr( "%1 of %2 downloaded." ).arg( mReceived, mTotal ) );
134  }
135 }
136 
137 void QgsFileDownloaderAlgorithm::receiveProgressFromDownloader( qint64 bytesReceived, qint64 bytesTotal )
138 {
139  mReceived = QgsFileUtils::representFileSize( bytesReceived );
140  if ( bytesTotal > 0 )
141  {
142  if ( mTotal.isEmpty() )
143  mTotal = QgsFileUtils::representFileSize( bytesTotal );
144 
145  mFeedback->setProgress( ( bytesReceived * 100 ) / bytesTotal );
146  }
147 }
148 
void canceled()
Internal routines can connect to this signal if they use event loop.
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
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.
Definition: qgsexception.h:83
Base class for providing feedback from a processing algorithm.
virtual void pushInfo(const QString &info)
Pushes a general 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()
Returns a session specific processing temporary folder for use in processing algorithms.