140void QNetworkDiskCache::setCacheDirectory(
const QString &cacheDir)
142#if defined(QNETWORKDISKCACHE_DEBUG)
143 qDebug() <<
"QNetworkDiskCache::setCacheDirectory()" << cacheDir;
145 Q_D(QNetworkDiskCache);
146 if (cacheDir.isEmpty())
148 d->cacheDirectory = cacheDir;
149 QDir dir(d->cacheDirectory);
150 d->cacheDirectory = dir.absolutePath();
151 if (!d->cacheDirectory.endsWith(u'/'))
152 d->cacheDirectory += u'/';
161qint64 QNetworkDiskCache::cacheSize()
const
163#if defined(QNETWORKDISKCACHE_DEBUG)
164 qDebug(
"QNetworkDiskCache::cacheSize()");
166 Q_D(
const QNetworkDiskCache);
167 if (d->cacheDirectory.isEmpty())
169 if (d->currentCacheSize < 0) {
170 QNetworkDiskCache *that =
const_cast<QNetworkDiskCache*>(
this);
171 that->d_func()->currentCacheSize = that->expire();
173 return d->currentCacheSize;
179QIODevice *QNetworkDiskCache::prepare(
const QNetworkCacheMetaData &metaData)
181#if defined(QNETWORKDISKCACHE_DEBUG)
182 qDebug() <<
"QNetworkDiskCache::prepare()" << metaData.url();
184 Q_D(QNetworkDiskCache);
185 if (!metaData.isValid() || !metaData.url().isValid() || !metaData.saveToDisk())
188 if (d->cacheDirectory.isEmpty()) {
189 qWarning(
"QNetworkDiskCache::prepare() The cache directory is not set");
193 const auto sizeValue = metaData.headers().value(QHttpHeaders::WellKnownHeader::ContentLength);
194 const qint64 size = sizeValue.toLongLong();
195 if (size > (maximumCacheSize() * 3)/4)
198 std::unique_ptr<QCacheItem> cacheItem = std::make_unique<QCacheItem>();
199 cacheItem->metaData = metaData;
201 QIODevice *device =
nullptr;
202 if (cacheItem->canCompress()) {
203 cacheItem->data.open(QBuffer::ReadWrite);
204 device = &(cacheItem->data);
206 QString fileName = d->cacheFileName(cacheItem->metaData.url());
207 cacheItem->file =
new(std::nothrow) QSaveFile(fileName, &cacheItem->data);
208 if (!cacheItem->file || !cacheItem->file->open(QFileDevice::WriteOnly)) {
209 qWarning(
"QNetworkDiskCache::prepare() unable to open temporary file");
213 cacheItem->writeHeader(cacheItem->file);
214 device = cacheItem->file;
216 d->inserting[device] = cacheItem.release();
248 static constexpr auto dirPermissions =
249 QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner;
253 helper.mkpath(cacheDirectory);
256 if (!helper.mkdir(dataDirectory, dirPermissions)
257 && !QFile::setPermissions(dataDirectory, dirPermissions)) {
258 qWarning(
"QNetworkDiskCache::prepareLayout: could not create or set permissions on %ls",
259 qUtf16Printable(dataDirectory));
262 for (uint i = 0; i < 16 ; i++) {
263 QString subdir = dataDirectory +
char16_t(QtMiscUtils::toHexLower(i));
264 helper.mkdir(subdir, dirPermissions);
271 Q_Q(QNetworkDiskCache);
272 Q_ASSERT(cacheItem->metaData.saveToDisk());
274 QString fileName = cacheFileName(cacheItem->metaData.url());
275 Q_ASSERT(!fileName.isEmpty());
277 if (QFile::exists(fileName)) {
278 if (!removeFile(fileName)) {
279 qWarning() <<
"QNetworkDiskCache: couldn't remove the cache file " << fileName;
284 currentCacheSize = q->expire();
285 if (!cacheItem->file) {
286 cacheItem->file =
new QSaveFile(fileName, &cacheItem->data);
287 if (cacheItem->file->open(QFileDevice::WriteOnly)) {
288 cacheItem->writeHeader(cacheItem->file);
289 cacheItem->writeCompressedData(cacheItem->file);
294 && cacheItem->file->isOpen()
295 && cacheItem->file->error() == QFileDevice::NoError) {
299 qint64 size = cacheItem->file->size();
300 if (cacheItem->file->commit())
301 currentCacheSize += size;
303 delete std::exchange(cacheItem->file,
nullptr);
305 if (cacheItem->metaData.url() == lastItem.metaData.url())
312bool QNetworkDiskCache::remove(
const QUrl &url)
314#if defined(QNETWORKDISKCACHE_DEBUG)
315 qDebug() <<
"QNetworkDiskCache::remove()" << url;
317 Q_D(QNetworkDiskCache);
320 for (
auto it = d->inserting.cbegin(), end = d->inserting.cend(); it != end; ++it) {
321 QCacheItem *item = it.value();
322 if (item && item->metaData.url() == url) {
324 d->inserting.erase(it);
329 if (d->lastItem.metaData.url() == url)
331 return d->removeFile(d->cacheFileName(url));
375QNetworkCacheMetaData QNetworkDiskCache::fileMetaData(
const QString &fileName)
const
377#if defined(QNETWORKDISKCACHE_DEBUG)
378 qDebug() <<
"QNetworkDiskCache::fileMetaData()" << fileName;
380 Q_D(
const QNetworkDiskCache);
381 QFile file(fileName);
382 if (!file.open(QFile::ReadOnly))
383 return QNetworkCacheMetaData();
384 if (!d->lastItem.read(&file,
false)) {
386 QNetworkDiskCachePrivate *that =
const_cast<QNetworkDiskCachePrivate*>(d);
387 that->removeFile(fileName);
389 return d->lastItem.metaData;
395QIODevice *QNetworkDiskCache::data(
const QUrl &url)
397#if defined(QNETWORKDISKCACHE_DEBUG)
398 qDebug() <<
"QNetworkDiskCache::data()" << url;
400 Q_D(QNetworkDiskCache);
401 std::unique_ptr<QBuffer> buffer;
404 if (d->lastItem.metaData.url() == url && d->lastItem.data.isOpen()) {
405 buffer.reset(
new QBuffer);
406 buffer->setData(d->lastItem.data.data());
408 QFile file(d->cacheFileName(url));
409 if (!file.open(QFile::ReadOnly | QIODevice::Unbuffered))
412 if (!d->lastItem.read(&file,
true)) {
417 if (d->lastItem.data.isOpen()) {
419 buffer.reset(
new QBuffer);
420 buffer->setData(d->lastItem.data.data());
422 buffer.reset(
new QBuffer);
423 buffer->setData(file.readAll());
426 buffer->open(QBuffer::ReadOnly);
427 return buffer.release();
433void QNetworkDiskCache::updateMetaData(
const QNetworkCacheMetaData &metaData)
435#if defined(QNETWORKDISKCACHE_DEBUG)
436 qDebug() <<
"QNetworkDiskCache::updateMetaData()" << metaData.url();
438 QUrl url = metaData.url();
439 QIODevice *oldDevice = data(url);
441#if defined(QNETWORKDISKCACHE_DEBUG)
442 qDebug(
"QNetworkDiskCache::updateMetaData(), no device!");
447 QIODevice *newDevice = prepare(metaData);
449#if defined(QNETWORKDISKCACHE_DEBUG)
450 qDebug() <<
"QNetworkDiskCache::updateMetaData(), no new device!" << url;
455 while (!oldDevice->atEnd()) {
456 qint64 s = oldDevice->read(data, 1024);
457 newDevice->write(data, s);
508qint64 QNetworkDiskCache::expire()
510 Q_D(QNetworkDiskCache);
511 if (d->currentCacheSize >= 0 && d->currentCacheSize < maximumCacheSize())
512 return d->currentCacheSize;
514 if (cacheDirectory().isEmpty()) {
515 qWarning(
"QNetworkDiskCache::expire() The cache directory is not set");
524 std::chrono::milliseconds msecs;
528 std::vector<CacheItem> cacheItems;
529 qint64 totalSize = 0;
530 using F = QDirListing::IteratorFlag;
531 for (
const auto &dirEntry : QDirListing(cacheDirectory(), F::FilesOnly | F::Recursive)) {
535 const QFileInfo &info = dirEntry.fileInfo();
536 QDateTime fileTime = info.birthTime(QTimeZone::UTC);
537 if (!fileTime.isValid())
538 fileTime = info.metadataChangeTime(QTimeZone::UTC);
539 const std::chrono::milliseconds msecs{fileTime.toMSecsSinceEpoch()};
540 const qint64 size = info.size();
541 cacheItems.push_back(CacheItem{msecs, info.filePath(), size});
545 const qint64 goal = (maximumCacheSize() * 9) / 10;
546 if (totalSize < goal)
549 auto byFileTime = [&](
const auto &a,
const auto &b) {
return a.msecs < b.msecs; };
550 std::sort(cacheItems.begin(), cacheItems.end(), byFileTime);
552 [[maybe_unused]]
int removedFiles = 0;
553 for (
const CacheItem &cached : cacheItems) {
554 QFile::remove(cached.path);
556 totalSize -= cached.size;
557 if (totalSize < goal)
560#if defined(QNETWORKDISKCACHE_DEBUG)
561 if (removedFiles > 0) {
562 qDebug() <<
"QNetworkDiskCache::expire()"
563 <<
"Removed:" << removedFiles
564 <<
"Kept:" << cacheItems.count() - removedFiles;
591 cleanUrl.setPassword(QString());
592 cleanUrl.setFragment(QString());
594 const QByteArray hash = QCryptographicHash::hash(cleanUrl.toEncoded(), QCryptographicHash::Sha1);
596 const QByteArray id = QByteArray::number(*(qlonglong*)hash.data(), 36).left(8);
598 uint code = (uint)id.at(id.size()-1) % 16;
599 QString pathFragment = QString::number(code, 16) + u'/' + QLatin1StringView(id) +
CACHE_POSTFIX;
678bool QCacheItem::read(QFileDevice *device,
bool readData)
682 QDataStream in(device);
695 qint32 streamVersion;
698 if (streamVersion > in.version())
700 in.setVersion(streamVersion);
706 if (readData && compressed) {
708 data.setData(qUncompress(dataBA));
709 data.open(QBuffer::ReadOnly);
713 QString expectedFilename = QNetworkDiskCachePrivate::uniqueFileName(metaData.url());
714 if (!device->fileName().endsWith(expectedFilename))
717 return metaData.isValid() && !metaData.headers().isEmpty();