c++qtqt4.8qtcoreqiodevice

custom QAbstractNetworkCache implementation; QAbstractNetworkCache::insert(QIODevice *device) device has no data


I am attempting to build my own custom QAbstractNetworkCache implementation for use with QNetworkAccessManager.

I am having trouble with QAbstractNetworkCache::insert(QIODevice *device); inside this method, the device always arrives with 0 bytes to read from.

As I understand, the QIODevice* that is returned from QAbstractNetworkCache::prepare(const QNetworkCacheMetaData &metaData) is going to be filled with data and used as a parameter to QAbstractNetworkCache::insert(QIODevice *device) method once QNetworkAccessManager finishes downloading.

So I have prepared a QBuffer to be this container, but whenever QAbstractNetworkCache::insert(QIODevice *device) is getting called, it always arrives with nothing in it (device->bytesAvailable() == 0)

QIODevice* NetworkCachePrivate::prepare(const QNetworkCacheMetaData &metaData) {

        if (!metaData.isValid() || !metaData.url().isValid() || cacheDir.isEmpty()) return 0;

        QIODevice* device = 0;

        QString hash = hexMD5(metaData.url().toString());

        QScopedPointer<QBuffer> buffer(new QBuffer);

        if (buffer->open(QIODevice::ReadWrite))
        {
                qDebug() << "BUFFER READY";
                device = buffer.take();
                deviceMapping[device] = qMakePair(hash, metaData);
        }

        return device;
}

void NetworkCachePrivate::insert(QIODevice *device) {
        if (deviceMapping.contains(device))
        {
                QPair<QString, QNetworkCacheMetaData> pair = deviceMapping[device];
                QString fileName;
                fileName += cacheDir;
                fileName += QLatin1String("/");
                fileName += pair.first;

                qDebug() << "DEVICE BYTES" << device->bytesAvailable(); //ALWAYS 0!!!! :(
                QFile file(fileName);
                if (file.open(QIODevice::WriteOnly))
                {
                        qint64 size = file.write(device->readAll());
                        if (size <= 0)
                        {
                                file.remove();
                        }
                        else
                        {
                                qDebug() << "FILE WROTE " << size;
                                cacheSize += size;
                        }
                }
                deviceMapping.remove(device);
                delete device;
        }
}

QNetworkCacheMetaData NetworkCachePrivate::metaData (const QUrl &url ) {
    QString fileName;
    fileName += cacheDir; 
    fileName += QLatin1String("/"); 
    fileName += hexMD5(url.toString());

    QNetworkCacheMetaData data;

    if (!QFile::exists(fileName))
        return data;

    data.setUrl(url); 
    data.setExpirationDate(QDateTime::currentDateTime().addYears(1));
    return data;
}

Solution

  • As peppe also pointed out in the comment, you need to seek the QBuffer to the beginning after the write and before the read as per the documentation:

    QBuffer allows you to access a QByteArray using the QIODevice interface. The QByteArray is treated just as a standard random-accessed file. Example:

    QBuffer buffer;
    char ch;
    
    buffer.open(QBuffer::ReadWrite);
    buffer.write("Qt rocks!");
    *** buffer.seek(0); ***
    buffer.getChar(&ch);  // ch == 'Q'
    buffer.getChar(&ch);  // ch == 't'
    buffer.getChar(&ch);  // ch == ' '
    buffer.getChar(&ch);  // ch == 'r'
    

    To be more concrete for your code, you would need to insert the statement like this:

    ...
    device.seek(0);
    qDebug() << "DEVICE BYTES" << device->bytesAvailable(); //NOT 0 ANYMORE!!!! :)
    ...