Russian Qt Forum

Qt => Вопросы новичков => Тема начата: Алёна_4_12_1989 от Март 19, 2013, 13:15



Название: QNetworkReply::readyRead()
Отправлено: Алёна_4_12_1989 от Март 19, 2013, 13:15
Здравствуйте. У меня проблема. Я скачиваю большой файл на компьютер. Использую для этого сигнал QNetworkReply::finished(). Но при этом постоянно съедается оперативная память, так как насколько я поняла, она используется в этом классе. Как я могу сохранять файл по частям, используя readyRead() сигнал? Я не совсем это понимаю. Открыть файл, запустить QNetworkReply::get и ждать readyRead слот ? Но что делать в нем? Как сохранять и освобождать оперативку? Спасибо.


Название: Re: QNetworkReply::readyRead()
Отправлено: mutineer от Март 19, 2013, 13:19
Цитировать
QNetworkReply is a sequential-access QIODevice, which means that once data is read from the object, it no longer kept by the device.

То бишь после прочтения из reply данных они больше не будут занимать память внутри reply


Название: Re: QNetworkReply::readyRead()
Отправлено: Алёна_4_12_1989 от Март 19, 2013, 13:29
т.е. при выходе из слота readyRead() память должна очищатся? или надо как-то считать эти данные? Я просто записываю их в файл, но в итоге программа делает crash из-за отсутствия памяти.


Название: Re: QNetworkReply::readyRead()
Отправлено: Bepec от Март 19, 2013, 13:32
Кооод... нужен кооод... Покажи коооддд...


Название: Re: QNetworkReply::readyRead()
Отправлено: mutineer от Март 19, 2013, 13:33
Не при выходе из слота, а при прочтении данных


Название: Re: QNetworkReply::readyRead()
Отправлено: Алёна_4_12_1989 от Март 19, 2013, 14:13
А как их прочесть?


Название: Re: QNetworkReply::readyRead()
Отправлено: Alex Custov от Март 19, 2013, 14:32
reply->readAll()


Название: Re: QNetworkReply::readyRead()
Отправлено: Алёна_4_12_1989 от Март 19, 2013, 14:35
я использую reply->readAll() функцию класса QNetworkReply. Это правильно? Но при этом всё равно не работает.

А код вот:

reply = network_manager->get(QNetworkRequest(url));
connect(reply, SIGNAL(readyRead()), this, SLOT(ready_read_slot()));

void ready_read_slot()
{
     reply->readAll(); // эти данные записываю в файл
}



Название: Re: QNetworkReply::readyRead()
Отправлено: mutineer от Март 19, 2013, 14:38
"не работает" это очень распространенная проблема, да. Только универсального решения не придумали. Что и как не работает?


Название: Re: QNetworkReply::readyRead()
Отправлено: Алёна_4_12_1989 от Март 19, 2013, 14:41
ок, попробую конкретизировать. Спасибо.


Название: Re: QNetworkReply::readyRead()
Отправлено: Алёна_4_12_1989 от Март 19, 2013, 16:43
Программа падает на функции reply->readAll()
Первые два раза в слот readyRead программа заходит ( причем во втором случае количество bytesAvailible() больше, чем в первом - возможно это не новые данные, а все скачанные данные), а на третьем падает.


Название: Re: QNetworkReply::readyRead()
Отправлено: mutineer от Март 19, 2013, 17:13
Покажи больше кода


Название: Re: QNetworkReply::readyRead()
Отправлено: Bepec от Март 19, 2013, 17:16
Кооод... нужен кооод... Покажи коооддд...


Название: Re: QNetworkReply::readyRead()
Отправлено: Алёна_4_12_1989 от Март 19, 2013, 17:34
Вот мой код.

void DownloadManager::startDownload()
{
    if (network_manager)
    {
        m_reply = network_manager->get(QNetworkRequest(url));
        connect(m_reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(download_progress(qint64, qint64)));
        connect(m_reply, SIGNAL(readyRead()), this, SLOT(ready_read()));
        connect(m_reply, SIGNAL(finished()), this, SLOT(finish_download()));
        if (m_reply)
        {
            if (m_reply->error() == QNetworkReply::NoError)
            {
                file = new QFile();
                if (file)
                {
                    file->setFileName(fileName);
                    if (file->open(QIODevice::WriteOnly))
                    {
                        QByteArray my_array = m_reply->readAll();
                        int number = my_array.size();
                        m_reply->seek(0);
                        ::write(file->handle(), my_array.data(), number);
                        http.setHost(url.host(), url.port(80));
                        http.get(url.toEncoded(QUrl::RemoveScheme | QUrl::RemoveAuthority));
                    }
                }
            }
        }
    }
}

void DownloadManager::ready_read()
{
    if (m_reply)
    {
        qint64 value = m_reply->bytesAvailable();
        if (file)
        {
            QByteArray my_array = m_reply->readAll();
            int number = my_array.size();
            ::write(file->handle(), my_array.data(), number);
        }
    }
}

void DownloadManager::finish_download()
{
    if (file)
    {
        ::close(file->handle());
        m_reply->deleteLater();
        delete file;
    }
}


Название: Re: QNetworkReply::readyRead()
Отправлено: mutineer от Март 19, 2013, 17:41
А для чего делается m_reply->seek(0); ? Он же бессмысленен для последовательных девайсов


Название: Re: QNetworkReply::readyRead()
Отправлено: Алёна_4_12_1989 от Март 19, 2013, 17:44
понятно. спасибо, уберу.


Название: Re: QNetworkReply::readyRead()
Отправлено: Алёна_4_12_1989 от Март 19, 2013, 17:46
но это не решило проблему. не могу пока понять в чем дело.


Название: Re: QNetworkReply::readyRead()
Отправлено: mutineer от Март 19, 2013, 17:46
И еще вопрос: почему используются ::write и ::close вместо функций QFile?


Название: Re: QNetworkReply::readyRead()
Отправлено: mutineer от Март 19, 2013, 17:47
но это не решило проблему. не могу пока понять в чем дело.
Я и не говорил что это решит проблему, просто странности в коде, интересно откуда у них ноги растут. Может наведет на мысли


Название: Re: QNetworkReply::readyRead()
Отправлено: Алёна_4_12_1989 от Март 19, 2013, 17:51
раньше не разобралась почему, но QFile не работал. Поэтому заменила на сишные функции. Но сейчас вижу, вроде QFile тоже работает. Но вот почему-то reply->readAll() падает((


Название: Re: QNetworkReply::readyRead()
Отправлено: Alex Custov от Март 19, 2013, 18:16
почему ты после вызова network_manager->get(QNetworkRequest(url)); сразу же проверяешь m_reply->error()? Скачивание будет происходить асинхронно, и завершится по сигналу finished(). Во-вторых, все точки назначения, куда нужно записывать полученные данные (типа переменной file), должны быть созданы до того, как ты делаешь network_manager->get(QNetworkRequest(url)), потому что данные могут прийти сразу же, а переменная file ещё не создана, и я даже подозреваю, что не занулена в конструкторе DownloadManager.


Название: Re: QNetworkReply::readyRead()
Отправлено: Алёна_4_12_1989 от Март 20, 2013, 09:49
Я обнуляю указатель на файл в конструкторе. Спасибо за замечания. Я исправила эти моменты. К сожалению, программа всё еще падает. Я стараюсь найти ошибку.


Название: Re: QNetworkReply::readyRead()
Отправлено: Bepec от Март 20, 2013, 09:52
Кооод... нужен кооод... Покажи коооддд...

PS выложи весь проект.


Название: Re: QNetworkReply::readyRead()
Отправлено: Алёна_4_12_1989 от Март 20, 2013, 10:13
Вот код всего donwloadera.




h file

#ifndef DOWNLOADMANAGER_H
#define DOWNLOADMANAGER_H

#include <QObject>
#include <QUrl>
#include <QHttp>

class QNetworkAccessManager;
class QNetworkReply;
class QFile;

class DownloadManager : public QObject
{
    Q_OBJECT
public:
    DownloadManager(QObject *parent = 0);
    ~DownloadManager();
    void download(QUrl &url, QString fileName);
    void download(QString fileName);
    void setUrl(QUrl &url);

private:
    void startDownload();

    QUrl url;
    QNetworkAccessManager* network_manager;
    QNetworkReply *m_reply;
    QHttp http;
    QString fileName;
    QFile *file;

signals:

private slots:
    void onNetworkReply(QNetworkReply*);
    void quit(bool);
    void download_progress(qint64,qint64);
    void ready_read();
    void finish_download();

};

#endif // DOWNLOADMANAGER_H











cpp file

#include "downloadmanager.h"
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>

DownloadManager::DownloadManager(QObject *parent) :
    QObject(parent)
{
    network_manager = 0;
    network_manager = new QNetworkAccessManager(this);
    if (network_manager)
        connect(network_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(onNetworkReply(QNetworkReply*)));
    connect(&http, SIGNAL(done(bool)), this, SLOT(quit(bool)));
    file = 0;
}

DownloadManager::~DownloadManager()
{
    delete network_manager;
}

void DownloadManager::download(QUrl &url, QString fileName)
{
    this->url = url;
    this->fileName = fileName;
    startDownload();
}

void DownloadManager::download(QString fileName)
{
    this->fileName = fileName;
    startDownload();
}

void DownloadManager::setUrl(QUrl &url)
{
    this->url = url;
}

void DownloadManager::startDownload()
{
    if (network_manager)
    {
        file = new QFile();
        m_reply = network_manager->get(QNetworkRequest(url));
        connect(m_reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(download_progress(qint64, qint64)));
        connect(m_reply, SIGNAL(readyRead()), this, SLOT(ready_read()));
        connect(m_reply, SIGNAL(finished()), this, SLOT(finish_download()));
        if (m_reply)
        {
            if (file)
            {
                file->setFileName(fileName);
                if (file->open(QIODevice::WriteOnly))
                {
                    http.setHost(url.host(), url.port(80));
                    http.get(url.toEncoded(QUrl::RemoveScheme | QUrl::RemoveAuthority));
                }
            }
        }
    }
}

void DownloadManager::onNetworkReply(QNetworkReply *reply)
{

}

void DownloadManager::quit(bool)
{

}

void DownloadManager::download_progress(qint64 bytesReceived, qint64 bytesTotal)
{

}

void DownloadManager::ready_read()
{
    if (m_reply)
    {
        if (m_reply->error() == QNetworkReply::NoError)
        {
            qint64 value = m_reply->bytesAvailable();
            if (file)
                file->write(m_reply->readAll());
        }
    }
}

void DownloadManager::finish_download()
{
    if (file && m_reply)
    {
        file->close();
        m_reply->deleteLater();
        delete file;
        file = 0;
    }
}


Название: Re: QNetworkReply::readyRead()
Отправлено: mutineer от Март 20, 2013, 11:04
Код
C++ (Qt)
delete network_manager;
Не надо этого делать - будет двойное удаление. network_manager будет разрушен своим родителем

Кроме того не надо сразу после get что-то читать из reply, читай по сигналу ready_read. И для чего тут протухший QHttp?


Название: Re: QNetworkReply::readyRead()
Отправлено: Old от Март 20, 2013, 14:36
Код
C++ (Qt)
delete network_manager;
Не надо этого делать - будет двойное удаление.
Да ну перестаньте. Никакого двойного удаления не будет. ;)
Сколько лет это обсуждается...


Название: Re: QNetworkReply::readyRead()
Отправлено: Алёна_4_12_1989 от Март 20, 2013, 14:38
Спасибо. Я снова поправила. По-прежнему падает. Адрес m_reply остается постоянным всегда, почему падает не понимаю.


Название: Re: QNetworkReply::readyRead()
Отправлено: mutineer от Март 20, 2013, 14:45
Код
C++ (Qt)
delete network_manager;
Не надо этого делать - будет двойное удаление.
Да ну перестаньте. Никакого двойного удаления не будет. ;)
Сколько лет это обсуждается...

Это не повод писать ненужный код


Название: Re: QNetworkReply::readyRead()
Отправлено: Bepec от Март 20, 2013, 15:18
А давайте зададим вопрос - зачем вы используете QHttp?

И зачем вызываете у него метод get?

И... И... И зачем вам стока пустых слотов :D

Код:
void deleteTmpNetwar::startDownload()
{
if (network_manager)
{
file = new QFile();
file->setFileName(fileName);
file->open(QIODevice::WriteOnly);
m_reply = network_manager->get(QNetworkRequest(url));
connect(m_reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(download_progress(qint64, qint64)));
connect(m_reply, SIGNAL(readyRead()), this, SLOT(ready_read()));
connect(m_reply, SIGNAL(finished()), this, SLOT(finish_download()));
}
}

И закачка 70 мегов прошла спокойно без эксцессов. Память съедалась на целых + 5 кб больше, чем при простое.

PS а ответ прост, класс QHttp у вас качал тот же файл. А вы с него не читали. И он качал в память. И его опять не читали... И всё :D Дальше он не выдерживал и умирал наверно )


Название: Re: QNetworkReply::readyRead()
Отправлено: Алёна_4_12_1989 от Март 21, 2013, 12:39
Большое спасибо. Да, наверное, http лишнее будет. Вообще, я посмотрела, что установка размера буфера в 10 Мегабайт позволила решить проблему. Но я не уверена, что она не вылезет в будущем.


Название: Re: QNetworkReply::readyRead()
Отправлено: Bepec от Март 21, 2013, 13:02
буфер? Где? Какие 10 мб? Омфг...

Девушка, почитайте мой пост выше. Всё нормально работает, память не жрёт, нулевая активность.