109void QNetworkDiskCache::setCacheDirectory(
const QString &cacheDir)
111#if defined(QNETWORKDISKCACHE_DEBUG)
112 qDebug() <<
"QNetworkDiskCache::setCacheDirectory()" << cacheDir;
114 Q_D(QNetworkDiskCache);
115 if (cacheDir.isEmpty())
117 d->cacheDirectory = cacheDir;
118 QDir dir(d->cacheDirectory);
119 d->cacheDirectory = dir.absolutePath();
120 if (!d->cacheDirectory.endsWith(u'/'))
121 d->cacheDirectory += u'/';
130qint64 QNetworkDiskCache::cacheSize()
const
132#if defined(QNETWORKDISKCACHE_DEBUG)
133 qDebug(
"QNetworkDiskCache::cacheSize()");
135 Q_D(
const QNetworkDiskCache);
136 if (d->cacheDirectory.isEmpty())
138 if (d->currentCacheSize < 0) {
139 QNetworkDiskCache *that =
const_cast<QNetworkDiskCache*>(
this);
140 that->d_func()->currentCacheSize = that->expire();
142 return d->currentCacheSize;
148QIODevice *QNetworkDiskCache::prepare(
const QNetworkCacheMetaData &metaData)
150#if defined(QNETWORKDISKCACHE_DEBUG)
151 qDebug() <<
"QNetworkDiskCache::prepare()" << metaData.url();
153 Q_D(QNetworkDiskCache);
154 if (!metaData.isValid() || !metaData.url().isValid() || !metaData.saveToDisk())
157 if (d->cacheDirectory.isEmpty()) {
158 qWarning(
"QNetworkDiskCache::prepare() The cache directory is not set");
162 const auto sizeValue = metaData.headers().value(QHttpHeaders::WellKnownHeader::ContentLength);
163 const qint64 size = sizeValue.toLongLong();
164 if (size > (maximumCacheSize() * 3)/4)
167 std::unique_ptr<QCacheItem> cacheItem = std::make_unique<QCacheItem>();
168 cacheItem->metaData = metaData;
170 QIODevice *device =
nullptr;
171 if (cacheItem->canCompress()) {
172 cacheItem->data.open(QBuffer::ReadWrite);
173 device = &(cacheItem->data);
175 QString fileName = d->cacheFileName(cacheItem->metaData.url());
176 cacheItem->file =
new(std::nothrow) QSaveFile(fileName, &cacheItem->data);
177 if (!cacheItem->file || !cacheItem->file->open(QFileDevice::WriteOnly)) {
178 qWarning(
"QNetworkDiskCache::prepare() unable to open temporary file");
182 cacheItem->writeHeader(cacheItem->file);
183 device = cacheItem->file;
185 d->inserting[device] = cacheItem.release();
230 Q_Q(QNetworkDiskCache);
231 Q_ASSERT(cacheItem->metaData.saveToDisk());
233 QString fileName = cacheFileName(cacheItem->metaData.url());
234 Q_ASSERT(!fileName.isEmpty());
236 if (QFile::exists(fileName)) {
237 if (!removeFile(fileName)) {
238 qWarning() <<
"QNetworkDiskCache: couldn't remove the cache file " << fileName;
243 currentCacheSize = q->expire();
244 if (!cacheItem->file) {
245 cacheItem->file =
new QSaveFile(fileName, &cacheItem->data);
246 if (cacheItem->file->open(QFileDevice::WriteOnly)) {
247 cacheItem->writeHeader(cacheItem->file);
248 cacheItem->writeCompressedData(cacheItem->file);
253 && cacheItem->file->isOpen()
254 && cacheItem->file->error() == QFileDevice::NoError) {
258 qint64 size = cacheItem->file->size();
259 if (cacheItem->file->commit())
260 currentCacheSize += size;
262 delete std::exchange(cacheItem->file,
nullptr);
264 if (cacheItem->metaData.url() == lastItem.metaData.url())
271bool QNetworkDiskCache::remove(
const QUrl &url)
273#if defined(QNETWORKDISKCACHE_DEBUG)
274 qDebug() <<
"QNetworkDiskCache::remove()" << url;
276 Q_D(QNetworkDiskCache);
279 for (
auto it = d->inserting.cbegin(), end = d->inserting.cend(); it != end; ++it) {
280 QCacheItem *item = it.value();
281 if (item && item->metaData.url() == url) {
283 d->inserting.erase(it);
288 if (d->lastItem.metaData.url() == url)
290 return d->removeFile(d->cacheFileName(url));
334QNetworkCacheMetaData QNetworkDiskCache::fileMetaData(
const QString &fileName)
const
336#if defined(QNETWORKDISKCACHE_DEBUG)
337 qDebug() <<
"QNetworkDiskCache::fileMetaData()" << fileName;
339 Q_D(
const QNetworkDiskCache);
340 QFile file(fileName);
341 if (!file.open(QFile::ReadOnly))
342 return QNetworkCacheMetaData();
343 if (!d->lastItem.read(&file,
false)) {
345 QNetworkDiskCachePrivate *that =
const_cast<QNetworkDiskCachePrivate*>(d);
346 that->removeFile(fileName);
348 return d->lastItem.metaData;
354QIODevice *QNetworkDiskCache::data(
const QUrl &url)
356#if defined(QNETWORKDISKCACHE_DEBUG)
357 qDebug() <<
"QNetworkDiskCache::data()" << url;
359 Q_D(QNetworkDiskCache);
360 std::unique_ptr<QBuffer> buffer;
363 if (d->lastItem.metaData.url() == url && d->lastItem.data.isOpen()) {
364 buffer.reset(
new QBuffer);
365 buffer->setData(d->lastItem.data.data());
367 QFile file(d->cacheFileName(url));
368 if (!file.open(QFile::ReadOnly | QIODevice::Unbuffered))
371 if (!d->lastItem.read(&file,
true)) {
376 if (d->lastItem.data.isOpen()) {
378 buffer.reset(
new QBuffer);
379 buffer->setData(d->lastItem.data.data());
381 buffer.reset(
new QBuffer);
382 buffer->setData(file.readAll());
385 buffer->open(QBuffer::ReadOnly);
386 return buffer.release();
392void QNetworkDiskCache::updateMetaData(
const QNetworkCacheMetaData &metaData)
394#if defined(QNETWORKDISKCACHE_DEBUG)
395 qDebug() <<
"QNetworkDiskCache::updateMetaData()" << metaData.url();
397 QUrl url = metaData.url();
398 QIODevice *oldDevice = data(url);
400#if defined(QNETWORKDISKCACHE_DEBUG)
401 qDebug(
"QNetworkDiskCache::updateMetaData(), no device!");
406 QIODevice *newDevice = prepare(metaData);
408#if defined(QNETWORKDISKCACHE_DEBUG)
409 qDebug() <<
"QNetworkDiskCache::updateMetaData(), no new device!" << url;
414 while (!oldDevice->atEnd()) {
415 qint64 s = oldDevice->read(data, 1024);
416 newDevice->write(data, s);
467qint64 QNetworkDiskCache::expire()
469 Q_D(QNetworkDiskCache);
470 if (d->currentCacheSize >= 0 && d->currentCacheSize < maximumCacheSize())
471 return d->currentCacheSize;
473 if (cacheDirectory().isEmpty()) {
474 qWarning(
"QNetworkDiskCache::expire() The cache directory is not set");
483 std::chrono::milliseconds msecs;
487 std::vector<CacheItem> cacheItems;
488 qint64 totalSize = 0;
489 using F = QDirListing::IteratorFlag;
490 for (
const auto &dirEntry : QDirListing(cacheDirectory(), F::FilesOnly | F::Recursive)) {
494 const QFileInfo &info = dirEntry.fileInfo();
495 QDateTime fileTime = info.birthTime(QTimeZone::UTC);
496 if (!fileTime.isValid())
497 fileTime = info.metadataChangeTime(QTimeZone::UTC);
498 const std::chrono::milliseconds msecs{fileTime.toMSecsSinceEpoch()};
499 const qint64 size = info.size();
500 cacheItems.push_back(CacheItem{msecs, info.filePath(), size});
504 const qint64 goal = (maximumCacheSize() * 9) / 10;
505 if (totalSize < goal)
508 auto byFileTime = [&](
const auto &a,
const auto &b) {
return a.msecs < b.msecs; };
509 std::sort(cacheItems.begin(), cacheItems.end(), byFileTime);
511 [[maybe_unused]]
int removedFiles = 0;
512 for (
const CacheItem &cached : cacheItems) {
513 QFile::remove(cached.path);
515 totalSize -= cached.size;
516 if (totalSize < goal)
519#if defined(QNETWORKDISKCACHE_DEBUG)
520 if (removedFiles > 0) {
521 qDebug() <<
"QNetworkDiskCache::expire()"
522 <<
"Removed:" << removedFiles
523 <<
"Kept:" << cacheItems.count() - removedFiles;
550 cleanUrl.setPassword(QString());
551 cleanUrl.setFragment(QString());
553 const QByteArray hash = QCryptographicHash::hash(cleanUrl.toEncoded(), QCryptographicHash::Sha1);
555 const QByteArray id = QByteArray::number(*(qlonglong*)hash.data(), 36).left(8);
557 uint code = (uint)id.at(id.size()-1) % 16;
558 QString pathFragment = QString::number(code, 16) + u'/' + QLatin1StringView(id) +
CACHE_POSTFIX;
637bool QCacheItem::read(QFileDevice *device,
bool readData)
641 QDataStream in(device);
654 qint32 streamVersion;
657 if (streamVersion > in.version())
659 in.setVersion(streamVersion);
665 if (readData && compressed) {
667 data.setData(qUncompress(dataBA));
668 data.open(QBuffer::ReadOnly);
672 QString expectedFilename = QNetworkDiskCachePrivate::uniqueFileName(metaData.url());
673 if (!device->fileName().endsWith(expectedFilename))
676 return metaData.isValid() && !metaData.headers().isEmpty();