Загрузка файла с использованием метода post () QNetworkAccessManager - PullRequest
7 голосов
/ 11 мая 2010

У меня проблемы с приложением Qt; в частности, с классом QNetworkAccessManager. Я пытаюсь выполнить простую HTTP-загрузку двоичного файла, используя метод post () QNetworkAccessManager. В документации говорится, что я могу дать указатель на QIODevice для post () и что класс будет передавать данные, найденные в QIODevice. Это говорит мне о том, что я должен иметь возможность дать post () указатель на QFile. Например:

QFile compressedFile("temp");  
compressedFile.open(QIODevice::ReadOnly);  
netManager.post(QNetworkRequest(QUrl("http://mywebsite.com/upload") ), &compressedFile);  

Похоже, что происходит в системе Windows, где я разрабатываю, это то, что мое приложение Qt выталкивает данные из QFile, но затем не завершает запрос; кажется, что он сидит там и ждет, пока из файла не появится больше данных. Почтовый запрос не «закрывается» до тех пор, пока я не убью приложение вручную, после чего на моем сервере появится весь файл.

Из некоторых отладок и исследований я думаю, что это происходит потому, что операция чтения () в QFile не возвращает -1, когда вы достигаете конца файла. Я думаю, что QNetworkAccessManager пытается читать из QIODevice, пока не получит -1 от read (), и в этот момент он предполагает, что данных больше нет, и закрывает запрос. Если он продолжает получать нулевой код возврата от read (), QNetworkAccessManager предполагает, что может быть больше поступающих данных, и поэтому он продолжает ожидать эти гипотетические данные.

Я с некоторым тестовым кодом подтвердил, что операция read () в QFile просто возвращает ноль после того, как вы прочитали до конца файла. Кажется, это несовместимо с тем, как метод post () QNetworkAccessManager ожидает, что QIODevice будет вести себя. Мои вопросы:

  1. Является ли это каким-то ограничением работы QFile под Windows?
  2. Есть ли какой-то другой способ, которым я должен использовать QFile или QNetworkAccessManager для отправки файла с помощью post ()?
  3. Разве это не сработает вообще, и мне нужно будет найти какой-то другой способ загрузить мой файл?

Будем благодарны за любые предложения или советы.

Обновление: Оказывается, у меня были две разные проблемы: одна на стороне клиента и одна на стороне сервера. На стороне клиента я должен был убедиться, что мой объект QFile остался в течение всей сетевой транзакции. Метод post () QNetworkAccessManager возвращается немедленно, но на самом деле не завершается немедленно. Вам необходимо прикрепить слот к сигналу finish () QNetworkAccessManager, чтобы определить, когда фактически завершился POST. В моем случае было достаточно легко хранить QFile более или менее постоянно, но я также прикрепил слот к сигналу finish (), чтобы проверить наличие сообщений об ошибках от сервера.

Я прикрепил сигнал к слоту так:

connect(&netManager, SIGNAL(finished(QNetworkReply*) ), this, SLOT(postFinished(QNetworkReply*) ) );  

Когда пришло время отправлять мой файл, я написал такой почтовый индекс (обратите внимание, что сжатый файл является членом моего класса и поэтому не выходит за рамки этого кода):

compressedFile.open(QIODevice::ReadOnly);  
netManager.post(QNetworkRequest(QUrl(httpDestination.getCString() ) ), &compressedFile);  

Завершенный (QNetworkReply *) сигнал от QNetworkAccessManager вызывает мой метод postFinished (QNetworkReply *). Когда это происходит, для меня безопасно закрыть сжатый файл и удалить файл данных, представленный сжатым файлом. В целях отладки я также добавил несколько операторов printf (), чтобы подтвердить, что транзакция завершена:

void CL_QtLogCompressor::postFinished(QNetworkReply* reply)  
{  
    QByteArray response = reply->readAll();  
    printf("response: %s\n", response.data() );  
    printf("reply error %d\n", reply->error() );  
    reply->deleteLater();  
    compressedFile.close();  
    compressedFile.remove();  
}  

Поскольку сжатый файл не закрывается сразу и не выходит за пределы области видимости, QNetworkAccessManager может передавать мой файл столько времени, сколько ему нравится. В конце концов транзакция завершена, и мой метод postFinished () вызывается.

Моя другая проблема (которая также способствовала поведению, которое я наблюдал, когда транзакция никогда не завершалась) заключалась в том, что код Python для моего веб-сервера неправильно отображал POST, но это выходит за рамки моего исходного вопроса Qt.

Ответы [ 2 ]

8 голосов
/ 11 мая 2010

Вы создаете compressedFile в стеке и передаете указатель на него в свой QNetworkRequest (и, в конечном счете, в свой QNetworkAccessManager). Как только вы выходите из метода, в котором вы находитесь, compressedFile выходит из области видимости. Я удивлен, что это не падает на вас, хотя поведение не определено.

Вам нужно создать QFile в куче:

QFile *compressedFile = new QFile("temp"); 

Вам, конечно, нужно будет отследить его, а затем delete, когда сообщение будет завершено, или установить его в качестве дочернего элемента QNetworkReply, чтобы оно уничтожалось, когда ответ уничтожался позже: 1011 *

QFile *compressedFile = new QFile("temp"); 
compressedFile->open(QIODevice::ReadOnly);

QNetworkReply *reply = netManager.post(QNetworkRequest(QUrl("http://mywebsite.com/upload") ), compressedFile); 
compressedFile->setParent(reply);
3 голосов
/ 14 июля 2010

Вы также можете запланировать автоматическое удаление файла, выделенного в куче, используя сигналы / слоты

QFile* compressedFile = new QFile(...);
QNetworkReply* reply = Manager.post(...);
// This is where the tricks is
connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater());
connect(reply, SIGNAL(destroyed()), compressedFile, SLOT(deleteLater());

ИМХО, он гораздо более локализован и инкапсулирован, чем необходимость хранить ваш файл во внешнем классе.

Обратите внимание, что вы должны удалить первый connect(), если у вас есть слот postFinished(QNetworkReply*), в котором вы не должны забывать вызывать reply->deleteLater() внутри него, чтобы вышеописанное работало.

...