6#include <QtCore/QDateTime>
9#include <QtNetwork/QNetworkDiskCache>
10#include <QtNetwork/QNetworkRequest>
21 , _diskCache(new QNetworkDiskCache(this))
23 qCDebug(QGCCachedFileDownloadLog) <<
"Created with cache dir:" << cacheDirectory;
25 _initializeCache(cacheDirectory);
28 this, &QGCCachedFileDownload::_onDownloadFinished);
30 this, &QGCCachedFileDownload::_onDownloadProgress);
33QGCCachedFileDownload::QGCCachedFileDownload(QObject *parent)
36 , _diskCache(new QNetworkDiskCache(this))
38 qCDebug(QGCCachedFileDownloadLog) <<
"Created without cache dir";
41 this, &QGCCachedFileDownload::_onDownloadFinished);
43 this, &QGCCachedFileDownload::_onDownloadProgress);
46QGCCachedFileDownload::~QGCCachedFileDownload()
48 qCDebug(QGCCachedFileDownloadLog) <<
"Destroying";
55QString QGCCachedFileDownload::cacheDirectory()
const
57 return _diskCache->cacheDirectory();
60void QGCCachedFileDownload::setCacheDirectory(
const QString &directory)
62 if (cacheDirectory() != directory) {
63 _initializeCache(directory);
68qint64 QGCCachedFileDownload::maxCacheSize()
const
70 return _diskCache->maximumCacheSize();
73void QGCCachedFileDownload::setMaxCacheSize(qint64 bytes)
75 if (maxCacheSize() != bytes) {
76 _diskCache->setMaximumCacheSize(bytes);
81qint64 QGCCachedFileDownload::cacheSize()
const
83 return _diskCache->cacheSize();
90bool QGCCachedFileDownload::isCached(
const QString &url,
int maxAgeSec)
const
92 const QNetworkCacheMetaData metadata = _diskCache->metaData(QUrl::fromUserInput(url));
93 if (!metadata.isValid()) {
101 const QDateTime timestamp = _getCacheTimestamp(url);
102 if (!timestamp.isValid()) {
106 return timestamp.addSecs(maxAgeSec) >= QDateTime::currentDateTime();
109QString QGCCachedFileDownload::cachedPath(
const QString &url)
const
111 QIODevice *device = _diskCache->data(QUrl::fromUserInput(url));
112 if (device ==
nullptr) {
116 const QByteArray data = device->readAll();
121 if (tempPath.isEmpty()) {
126 QFile::remove(tempPath);
133int QGCCachedFileDownload::cacheAge(
const QString &url)
const
135 const QDateTime timestamp = _getCacheTimestamp(url);
136 if (!timestamp.isValid()) {
140 return static_cast<int>(timestamp.secsTo(QDateTime::currentDateTime()));
150 qCWarning(QGCCachedFileDownloadLog) <<
"Download already in progress";
155 qCWarning(QGCCachedFileDownloadLog) <<
"Empty URL";
156 _setErrorString(tr(
"Empty URL"));
160 if (cacheDirectory().isEmpty()) {
161 qCWarning(QGCCachedFileDownloadLog) <<
"Cache directory not set";
162 _setErrorString(tr(
"Cache directory not configured"));
167 _maxCacheAgeSec = maxCacheAgeSec;
168 _networkAttemptFailed =
false;
169 _forceNetwork =
false;
170 _cancelRequested =
false;
172 _url = QUrl::fromUserInput(url);
176 const QNetworkCacheMetaData metadata = _diskCache->metaData(_url);
177 if (metadata.isValid()) {
178 const QDateTime cacheTime = _getCacheTimestamp(url);
179 bool expired =
false;
180 if (maxCacheAgeSec > 0) {
181 if (!cacheTime.isValid()) {
184 expired = cacheTime.addSecs(maxCacheAgeSec) < QDateTime::currentDateTime();
189 qCDebug(QGCCachedFileDownloadLog) <<
"Using cached version for:" << url;
190 return _startDownload(url,
false,
true);
194 qCDebug(QGCCachedFileDownloadLog) <<
"Cache expired, trying network:" << url;
195 _forceNetwork =
true;
196 return _startDownload(url,
true,
false);
200 qCDebug(QGCCachedFileDownloadLog) <<
"No cache, downloading:" << url;
201 return _startDownload(url,
false,
false);
207 qCWarning(QGCCachedFileDownloadLog) <<
"Download already in progress";
212 qCWarning(QGCCachedFileDownloadLog) <<
"Empty URL";
213 _setErrorString(tr(
"Empty URL"));
217 if (cacheDirectory().isEmpty()) {
218 qCWarning(QGCCachedFileDownloadLog) <<
"Cache directory not set";
219 _setErrorString(tr(
"Cache directory not configured"));
225 _networkAttemptFailed =
false;
226 _forceNetwork =
false;
227 _cancelRequested =
false;
229 _url = QUrl::fromUserInput(url);
232 return _startDownload(url,
false,
true);
238 qCWarning(QGCCachedFileDownloadLog) <<
"Download already in progress";
243 qCWarning(QGCCachedFileDownloadLog) <<
"Empty URL";
244 _setErrorString(tr(
"Empty URL"));
250 _networkAttemptFailed =
false;
251 _forceNetwork =
true;
252 _cancelRequested =
false;
254 _url = QUrl::fromUserInput(url);
257 return _startDownload(url,
true,
false);
266 _cancelRequested =
true;
268 if (_fileDownload !=
nullptr) {
272 _setErrorString(tr(
"Download cancelled"));
276 _emitFinished(
false, {}, _errorString);
285 qCDebug(QGCCachedFileDownloadLog) <<
"Clearing cache";
292 const bool removed = _diskCache->remove(QUrl::fromUserInput(url));
303void QGCCachedFileDownload::_onDownloadFinished(
bool success,
304 const QString &localPath,
305 const QString &errorMessage)
307 if (_cancelRequested) {
308 _cancelRequested =
false;
312 qCDebug(QGCCachedFileDownloadLog) <<
"Download finished - success:" << success
313 <<
"path:" << localPath
318 _updateCacheTimestamp(_pendingUrl);
320 _localPath = localPath;
322 _setFromCache(_fileDownload->lastResultFromCache());
326 _emitFinished(
true, localPath, {});
328 }
else if (_forceNetwork && !_networkAttemptFailed) {
330 _networkAttemptFailed =
true;
331 qCDebug(QGCCachedFileDownloadLog) <<
"Network failed, trying cache fallback";
333 if (!_startDownload(_pendingUrl,
false,
true)) {
335 _setErrorString(errorMessage);
343 _setErrorString(errorMessage);
350void QGCCachedFileDownload::_onDownloadProgress(qint64 bytesReceived, qint64 totalBytes)
352 if (totalBytes > 0) {
353 _setProgress(
static_cast<qreal
>(bytesReceived) /
static_cast<qreal
>(totalBytes));
362void QGCCachedFileDownload::_initializeCache(
const QString &directory)
364 if (!directory.isEmpty()) {
365 QDir().mkpath(directory);
366 _diskCache->setCacheDirectory(directory);
367 _fileDownload->setCache(_diskCache);
368 qCDebug(QGCCachedFileDownloadLog) <<
"Cache initialized:" << directory;
372void QGCCachedFileDownload::_setRunning(
bool running)
374 if (_running != running) {
380void QGCCachedFileDownload::_setProgress(qreal progress)
382 if (!qFuzzyCompare(_progress, progress)) {
383 _progress = progress;
388void QGCCachedFileDownload::_setErrorString(
const QString &
error)
390 if (_errorString !=
error) {
391 _errorString =
error;
396void QGCCachedFileDownload::_setFromCache(
bool fromCache)
398 if (_fromCache != fromCache) {
399 _fromCache = fromCache;
404void QGCCachedFileDownload::_updateCacheTimestamp(
const QString &url)
406 QNetworkCacheMetaData metadata = _diskCache->metaData(QUrl::fromUserInput(url));
407 if (metadata.isValid()) {
408 QNetworkCacheMetaData::AttributesMap attributes = metadata.attributes();
409 attributes.insert(QNetworkRequest::Attribute::User, QDateTime::currentDateTime());
410 metadata.setAttributes(attributes);
411 _diskCache->updateMetaData(metadata);
415QDateTime QGCCachedFileDownload::_getCacheTimestamp(
const QString &url)
const
417 const QNetworkCacheMetaData metadata = _diskCache->metaData(QUrl::fromUserInput(url));
418 if (!metadata.isValid()) {
422 const auto &attributes = metadata.attributes();
423 const auto it = attributes.find(QNetworkRequest::Attribute::User);
424 if (it != attributes.end()) {
425 return it->toDateTime();
431bool QGCCachedFileDownload::_startDownload(
const QString &url,
bool forceNetwork,
bool preferCache)
435 _setFromCache(
false);
441 QNetworkRequest::CacheLoadControlAttribute,
442 QVariant{QNetworkRequest::AlwaysNetwork},
444 }
else if (preferCache) {
446 QNetworkRequest::CacheLoadControlAttribute,
447 QVariant{QNetworkRequest::PreferCache},
451 if (!_fileDownload->
start(url, config)) {
453 _setFromCache(
false);
460void QGCCachedFileDownload::_emitFinished(
bool success,
462 const QString &
error)
Cached file download with time-based expiration and fallback support.
Unified file download utility with decompression, verification, and QML support.
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void maxCacheSizeChanged(qint64 bytes)
Emitted when max cache size changes.
bool downloadPreferCache(const QString &url)
void urlChanged(const QUrl &url)
Emitted when URL changes.
void cancel()
Cancel current download.
bool removeFromCache(const QString &url)
void downloadProgress(qint64 bytesReceived, qint64 totalBytes)
Emitted during download with byte counts.
void localPathChanged(const QString &localPath)
Emitted when local path changes.
void cacheDirectoryChanged(const QString &directory)
Emitted when cache directory changes.
void cacheSizeChanged(qint64 bytes)
Emitted when cache size changes.
void progressChanged(qreal progress)
Emitted when download progress changes.
void clearCache()
Clear all cached files.
void errorStringChanged(const QString &errorString)
Emitted when error string changes.
bool downloadNoCache(const QString &url)
bool download(const QString &url, int maxCacheAgeSec)
void runningChanged(bool running)
Emitted when running state changes.
void finished(bool success, const QString &localPath, const QString &errorMessage, bool fromCache)
void fromCacheChanged(bool fromCache)
Emitted when fromCache state changes.
void finished(bool success, const QString &localPath, const QString &errorMessage)
bool start(const QString &remoteUrl)
void cancel()
Cancel current download.
void downloadProgress(qint64 bytesReceived, qint64 totalBytes)
Emitted during download with byte counts.
QString uniqueTempPath(const QString &templateName)
bool atomicWrite(const QString &filePath, const QByteArray &data)
QString errorMessage(const QNetworkReply *reply)
Common request configuration options.
QList< QPair< QNetworkRequest::Attribute, QVariant > > requestAttributes