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();
217 static constexpr auto dirPermissions =
218 QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner;
222 helper.mkpath(cacheDirectory);
225 if (!helper.mkdir(dataDirectory, dirPermissions)
226 && !QFile::setPermissions(dataDirectory, dirPermissions)) {
227 qWarning(
"QNetworkDiskCache::prepareLayout: could not create or set permissions on %ls",
228 qUtf16Printable(dataDirectory));
231 for (uint i = 0; i < 16 ; i++) {
232 QString str = QString::number(i, 16);
233 QString subdir = dataDirectory + str;
234 helper.mkdir(subdir, dirPermissions);
241 Q_Q(QNetworkDiskCache);
242 Q_ASSERT(cacheItem->metaData.saveToDisk());
244 QString fileName = cacheFileName(cacheItem->metaData.url());
245 Q_ASSERT(!fileName.isEmpty());
247 if (QFile::exists(fileName)) {
248 if (!removeFile(fileName)) {
249 qWarning() <<
"QNetworkDiskCache: couldn't remove the cache file " << fileName;
254 currentCacheSize = q->expire();
255 if (!cacheItem->file) {
256 cacheItem->file =
new QSaveFile(fileName, &cacheItem->data);
257 if (cacheItem->file->open(QFileDevice::WriteOnly)) {
258 cacheItem->writeHeader(cacheItem->file);
259 cacheItem->writeCompressedData(cacheItem->file);
264 && cacheItem->file->isOpen()
265 && cacheItem->file->error() == QFileDevice::NoError) {
269 qint64 size = cacheItem->file->size();
270 if (cacheItem->file->commit())
271 currentCacheSize += size;
273 delete std::exchange(cacheItem->file,
nullptr);
275 if (cacheItem->metaData.url() == lastItem.metaData.url())
282bool QNetworkDiskCache::remove(
const QUrl &url)
284#if defined(QNETWORKDISKCACHE_DEBUG)
285 qDebug() <<
"QNetworkDiskCache::remove()" << url;
287 Q_D(QNetworkDiskCache);
290 for (
auto it = d->inserting.cbegin(), end = d->inserting.cend(); it != end; ++it) {
291 QCacheItem *item = it.value();
292 if (item && item->metaData.url() == url) {
294 d->inserting.erase(it);
299 if (d->lastItem.metaData.url() == url)
301 return d->removeFile(d->cacheFileName(url));
345QNetworkCacheMetaData QNetworkDiskCache::fileMetaData(
const QString &fileName)
const
347#if defined(QNETWORKDISKCACHE_DEBUG)
348 qDebug() <<
"QNetworkDiskCache::fileMetaData()" << fileName;
350 Q_D(
const QNetworkDiskCache);
351 QFile file(fileName);
352 if (!file.open(QFile::ReadOnly))
353 return QNetworkCacheMetaData();
354 if (!d->lastItem.read(&file,
false)) {
356 QNetworkDiskCachePrivate *that =
const_cast<QNetworkDiskCachePrivate*>(d);
357 that->removeFile(fileName);
359 return d->lastItem.metaData;
365QIODevice *QNetworkDiskCache::data(
const QUrl &url)
367#if defined(QNETWORKDISKCACHE_DEBUG)
368 qDebug() <<
"QNetworkDiskCache::data()" << url;
370 Q_D(QNetworkDiskCache);
371 std::unique_ptr<QBuffer> buffer;
374 if (d->lastItem.metaData.url() == url && d->lastItem.data.isOpen()) {
375 buffer.reset(
new QBuffer);
376 buffer->setData(d->lastItem.data.data());
378 QFile file(d->cacheFileName(url));
379 if (!file.open(QFile::ReadOnly | QIODevice::Unbuffered))
382 if (!d->lastItem.read(&file,
true)) {
387 if (d->lastItem.data.isOpen()) {
389 buffer.reset(
new QBuffer);
390 buffer->setData(d->lastItem.data.data());
392 buffer.reset(
new QBuffer);
393 buffer->setData(file.readAll());
396 buffer->open(QBuffer::ReadOnly);
397 return buffer.release();
403void QNetworkDiskCache::updateMetaData(
const QNetworkCacheMetaData &metaData)
405#if defined(QNETWORKDISKCACHE_DEBUG)
406 qDebug() <<
"QNetworkDiskCache::updateMetaData()" << metaData.url();
408 QUrl url = metaData.url();
409 QIODevice *oldDevice = data(url);
411#if defined(QNETWORKDISKCACHE_DEBUG)
412 qDebug(
"QNetworkDiskCache::updateMetaData(), no device!");
417 QIODevice *newDevice = prepare(metaData);
419#if defined(QNETWORKDISKCACHE_DEBUG)
420 qDebug() <<
"QNetworkDiskCache::updateMetaData(), no new device!" << url;
425 while (!oldDevice->atEnd()) {
426 qint64 s = oldDevice->read(data, 1024);
427 newDevice->write(data, s);
478qint64 QNetworkDiskCache::expire()
480 Q_D(QNetworkDiskCache);
481 if (d->currentCacheSize >= 0 && d->currentCacheSize < maximumCacheSize())
482 return d->currentCacheSize;
484 if (cacheDirectory().isEmpty()) {
485 qWarning(
"QNetworkDiskCache::expire() The cache directory is not set");
494 std::chrono::milliseconds msecs;
498 std::vector<CacheItem> cacheItems;
499 qint64 totalSize = 0;
500 using F = QDirListing::IteratorFlag;
501 for (
const auto &dirEntry : QDirListing(cacheDirectory(), F::FilesOnly | F::Recursive)) {
505 const QFileInfo &info = dirEntry.fileInfo();
506 QDateTime fileTime = info.birthTime(QTimeZone::UTC);
507 if (!fileTime.isValid())
508 fileTime = info.metadataChangeTime(QTimeZone::UTC);
509 const std::chrono::milliseconds msecs{fileTime.toMSecsSinceEpoch()};
510 const qint64 size = info.size();
511 cacheItems.push_back(CacheItem{msecs, info.filePath(), size});
515 const qint64 goal = (maximumCacheSize() * 9) / 10;
516 if (totalSize < goal)
519 auto byFileTime = [&](
const auto &a,
const auto &b) {
return a.msecs < b.msecs; };
520 std::sort(cacheItems.begin(), cacheItems.end(), byFileTime);
522 [[maybe_unused]]
int removedFiles = 0;
523 for (
const CacheItem &cached : cacheItems) {
524 QFile::remove(cached.path);
526 totalSize -= cached.size;
527 if (totalSize < goal)
530#if defined(QNETWORKDISKCACHE_DEBUG)
531 if (removedFiles > 0) {
532 qDebug() <<
"QNetworkDiskCache::expire()"
533 <<
"Removed:" << removedFiles
534 <<
"Kept:" << cacheItems.count() - removedFiles;
561 cleanUrl.setPassword(QString());
562 cleanUrl.setFragment(QString());
564 const QByteArray hash = QCryptographicHash::hash(cleanUrl.toEncoded(), QCryptographicHash::Sha1);
566 const QByteArray id = QByteArray::number(*(qlonglong*)hash.data(), 36).left(8);
568 uint code = (uint)id.at(id.size()-1) % 16;
569 QString pathFragment = QString::number(code, 16) + u'/' + QLatin1StringView(id) +
CACHE_POSTFIX;
648bool QCacheItem::read(QFileDevice *device,
bool readData)
652 QDataStream in(device);
665 qint32 streamVersion;
668 if (streamVersion > in.version())
670 in.setVersion(streamVersion);
676 if (readData && compressed) {
678 data.setData(qUncompress(dataBA));
679 data.open(QBuffer::ReadOnly);
683 QString expectedFilename = QNetworkDiskCachePrivate::uniqueFileName(metaData.url());
684 if (!device->fileName().endsWith(expectedFilename))
687 return metaData.isValid() && !metaData.headers().isEmpty();