I am trying to upload a simple test text file to a FTP server. In order to achieve this I am using QNetworkAccessManager, since QFtp has been deprecated in Qt 5.1.
I created a test.txt file in the programs directory and using QFile I am opening it as QIODevice::ReadWrite | QIODevice::Text.
The problem is when I set the connection and tell the QNetworkAccessManager to upload a file the program crashes ("FTPConnectionTest does not respond"). It happens both when I am trying to use an external FTP server or a local one created with FileZilla.
I connected all signals emitted by the reply (functions: uploadFinish, uploadProgress, uploadError) however no feedback is beeing captured.
Question: Is this problem lying on the side of FTP server or am I doing something wrong in my code?
Code snipped below:
Main.cpp
#include <QCoreApplication>
#include <ftp.h>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Ftp ftp;
return a.exec();
}
ftp.cpp
#include "ftp.h"
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
#include <QFile>
#include <QUrl>
#include <QDebug>
Ftp::Ftp()
{
QFile file("test.txt");
if (file.open(QIODevice::ReadWrite | QIODevice::Text)) {
url = QUrl("ftp://127.0.0.1/test.txt");
url.setUserName("user");
url.setPassword("password");
qDebug() << "URL set" << url;
QNetworkAccessManager* nam = new QNetworkAccessManager();
qDebug() << "nam set";
QNetworkReply *rep = nam->put(QNetworkRequest(url), &file);
qDebug() << "after rep";
connect(rep, SIGNAL(finished()), this, SLOT(uploadFinish()));
connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(uploadError(QNetworkReply::NetworkError)));
connect(rep, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(uploadProgress(qint64,qint64)));
}
else qDebug() << "failed to open";
}
void Ftp::uploadFinish()
{
qDebug() << "finished uploading file";
}
void Ftp::uploadProgress(qint64 a, qint64 b)
{
qDebug() << a << "/" << b;
}
void Ftp::uploadError(QNetworkReply::NetworkError state)
{
qDebug() << "State" << state;
}
See the QNetworkAccessManager::put documentation:
data must be opened for reading when this function is called and must remain valid until the finished() signal is emitted for this reply.
Your file object falls out of scope when the constructor finishes execution, so QNetworkAccessManager probably tries to read from object that is already deleted. You need to make file a class member variable or create it using QFile* file = new QFile().
Related
I want to make simple communication example on Qt Remote Objects. I want to make the communication peer-to-peer, therefore I'm trying to merge both Source and Replica of the same remote object functionality in one application (REPC_MERGED tool used to generate Source and Replica base classes).
#include <QtCore/QCoreApplication>
#include "MyPeerHost.h"
#include "Client.h"
#include <QDebug>
static QString peer_node_name(int number)
{
QString ret = QString("peer_%1").arg(number);
return ret;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyPeerHost peerHost; // just inherits auto-generated MyPeerSimpleSource
QUrl thisAddress = "local:" + peer_node_name(0);
QRemoteObjectHost sourceNode(thisAddress);
if(sourceNode.enableRemoting(&peerHost))
{
qInfo() << "Source remoting enabled successfully" << thisAddress;
QUrl remoteAddress = "local:" + peer_node_name(1);
QSharedPointer<MyPeerReplica> replica;
QRemoteObjectNode replicaNode;
if(replicaNode.connectToNode(remoteAddress))
{
qInfo() << "Replica connected to the address" << remoteAddress << "successfully";
replica.reset(replicaNode.acquire<MyPeerReplica>());
QString sourceClassName = peerHost.staticMetaObject.className();
qDebug() << "Replica wait for Source" << sourceClassName << "...";
if(replica->waitForSource(1000))
{
qInfo() << "Replica object completely initialized";
Client client;
client.setReplicaObject(replica);
client.sendMessage("AAA");
}
else
{
qCritical() << "Replica wait for Source" << sourceClassName << "FAILED" << replicaNode.lastError();
}
}
else
{
qCritical() << "Replica connect to the address" << remoteAddress << "FAILED" << replicaNode.lastError();
}
}
else
{
qCritical() << "Source remoting enable FAILED" << sourceNode.lastError();
}
return a.exec();
}
Application output:
Source remoting enabled successfully QUrl("local:peer_0")
Replica connected to the address QUrl("local:peer_1") successfully
Replica wait for Source "MyPeerHost" ...
Replica wait for Source "MyPeerHost" FAILED QRemoteObjectNode::NoError
As you see, replicaNode successfully connected to the non-existent node QUrl("local:peer_1").
What I am doing wrong?
You don't have valid Qt code.
Qt relies on an event loop to handle asynchronous behavior, which is started by the a.exec() at the end of your main() routine. Qt Remote Objects, in turn, relies on the event loop for all of its communication.
In your code, you create your objects on the stack, but in code blocks that go out of scope before you start the event loop. They will therefore be destructed before the event loop is kicked off.
I'd recommend starting with some of the examples, make sure they work, then grow what you are trying to do from there.
I have a simple web service with the following URL:
http://localhost:8080/WebSvc1/webresources/generic/data?ctype=Ping
This returns a simple XML data:
<CALL TYPE='Ping'><IP>10.0.0.10</IP></CALL>
I'm trying to write a Qt program to call this web service.
The code that makes the call is below:
QUrl qrl("http://localhost:8080/WebSvc1/webresources/generic/data?ctype=Ping");
manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
printf ("Calling url: [%s]\n", qPrintable(url));
QNetworkReply *reply = 0;
reply = manager->get(QNetworkRequest(qrl));
qDebug() << reply->readAll();
I'm expecting/hoping the readAll will get the XML text data and print it (via qDebug).
Instead I see nothing and the program just hangs.
UPpdate, also have this:
void obj::replyFinished(QNetworkReply *reply)
{
qDebug() << reply->readAll();
}
I've included an example (forcing a synchronous request <-> reply exchange to easy the debugging process) that should work for you:
QUrl qrl("http://localhost:8080/WebSvc1/webresources/generic/data?ctype=Ping");
qDebug() << "Calling url: " << qrl.toString();
manager = new QNetworkAccessManager();
QNetworkReply* reply = manager->get(QNetworkRequest(qrl));
QEventLoop eventLoop;
connect(reply, SIGNAL(finished()), &eventLoop, SLOT(quit()));
eventLoop.exec();
if (reply->error() != QNetworkReply::NoError)
{
qDebug() << "Network error: " << reply->error();
}
else
{
qDebug() << reply->readAll();
}
Notice that the "emitter" of the finished signal is not the QNetworkAccessManager but the reply itself.
I think your error could be with your web service. I tried your code out (slightly modified) with httpbin.org, and was getting proper replies. Maybe take a look at your code with httpbin.org and then see if you can track down what's wrong with your service.
MainWindow.cpp
#include "MainWindow.hpp"
#include <QUrl>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QDebug>
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent), manager(this) {
load();
}
void MainWindow::load() {
const QUrl url(QStringLiteral("http://httpbin.org/xml"));
QNetworkReply* reply = manager.get(QNetworkRequest(url));
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
replyFinished(reply);
});
qDebug() << reply->readAll(); // The reply won't be ready by now, so
// testing here isn't very helpful.
}
void MainWindow::replyFinished(QNetworkReply* reply) {
qDebug() << reply->readAll();
reply->deleteLater();
}
MainWindow.hpp
#ifndef MAINWINDOW_HPP
#define MAINWINDOW_HPP
#include <QMainWindow>
#include <QNetworkAccessManager>
#include <QNetworkReply>
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget* parent = nullptr);
public slots:
void load();
void replyFinished(QNetworkReply* reply);
private:
QNetworkAccessManager manager;
};
#endif // MAINWINDOW_HPP
main.cpp
#include "MainWindow.hpp"
#include <QApplication>
int main(int argc, char* argv[]) {
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
network.pro
QT += core gui widgets network
TARGET = network
TEMPLATE = app
DEFINES += QT_DEPRECATED_WARNINGS
CONFIG += c++11
SOURCES += \
main.cpp \
MainWindow.cpp
HEADERS += MainWindow.hpp
I have a problem. I'm writing a small application, which will fetch an image from a website and display it in a QT GUI application.
I use QHttp to do this. The code works if I execute it in main (before GUI is shown), but when I try to implement it, so that the code will run when I click on a button, it doesn't work.
Here's some of the code:
downloader.h - The class that's responsible for creating connection and saving image
#ifndef DOWNLOADER_H
#define DOWNLOADER_H
#include <QObject>
#include <QHttp>
#include <QFile>
#include <QDebug>
#include <QDir>
class Downloader : public QObject
{
Q_OBJECT
public:
explicit Downloader(QObject *parent = 0);
void getImageFromWeb(QString host, QString append);
signals:
public slots:
void stateChanged(int state);
void responseHeaderReceived(const QHttpResponseHeader &resp);
void requestFinished(int id, bool error);
private:
QHttp *http;
};
#endif // DOWNLOADER_H
downloader.cpp - The implementation
The case switches are added for debugging
#include "downloader.h"
#include <QApplication>
Downloader::Downloader(QObject *parent) :
QObject(parent)
{
}
void Downloader::getImageFromWeb(QString host, QString append)
{
http = new QHttp(this);
connect(http, SIGNAL(stateChanged(int)), this, SLOT(stateChanged(int)));
qDebug() << "Connect 1";
connect(http, SIGNAL(responseHeaderReceived(QHttpResponseHeader)), this, SLOT(responseHeaderReceived(QHttpResponseHeader)));
qDebug() << "Connect 2";
connect(http, SIGNAL(requestFinished(int,bool)), this, SLOT(requestFinished(int,bool)));
qDebug() << "Connect 3";
http->setHost(host);
http->get(append);
}
void Downloader::stateChanged(int state)
{
switch(state)
{
case 0:
qDebug() << "Unconnected";
break;
case 1:
qDebug() << "Hhost Lookup";
break;
case 2:
qDebug() << "Connection";
break;
case 3:
qDebug() << "Sending";
break;
case 4:
qDebug() << "Reading";
break;
case 5:
qDebug() << "Connect";
break;
case 6:
qDebug() << "Closing";
break;
}
}
void Downloader::responseHeaderReceived(const QHttpResponseHeader &resp)
{
qDebug() << "Size" << resp.contentLength();
qDebug() << "Type" << resp.contentType();
qDebug() << "Status" << resp.statusCode();
}
void Downloader::requestFinished(int id, bool error)
{
if(error)
{
qDebug() << "ERROR!";
}
else
{
qDebug() << "OK";
QFile *file = new QFile(QDir::currentPath() + "/image.png");
if(file->open(QFile::Append))
{
file->write(http->readAll());
file->flush();
file->close();
}
delete file;
}
}
main.cpp - The code above works correctly if it is implemented like this
#include "mainwindow.h"
#include <QApplication>
#include <downloader.h>
int main(int argc, char *argv[])
{
Downloader getImage;
getImage.getImageFromWeb("servlet.dmi.dk", "/byvejr/servlet/byvejr?by=8000&tabel=dag3_9");
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
Instead of this, I would like the image to be fetched when I press a button in the program, so I tried this:
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "downloader.h"
#include <QApplication>
#include <QDir>
void MainWindow::on_pushButton_clicked()
{
Downloader getImage;
getImage.getImageFromWeb("www.dmi.dk/", "/uploads/tx_dmidatastore/webservice/k/d/_/n/g/femdgn_dk.png");
}
This doesn't work. From the debugger I get:
Connect 1
Connect 2
Connect 3
When it works (when it's implemented in main.cpp) the debugger gives me something like:
Connect 1
Connect 2
Connect 3
OK
Connection
Sending
Reading
Size 16282
Type "image/png"
Status 200
Connect
OK
So I guess this tells me that the connections are made, but nothing is being executed.
Any answer/suggestion is appreciated.
Thanks in advance!
Looks like your getImage object is being descoped/destructed before it can do anything. Try creating a Downloader object as a member of MainWindow instead of inside the on_pushButton_clicked() function.
I am writing a simple program. The program has 2 QStrings set with following variables: path and name of file, there is a 3rd QString which I later on use to put the result of the append of the first 2 QString together in. What I want to do is append the 2 QStrings and put them in the appendAll QString, and then send the appendAll QString to the QFile variable constructor. Now when I do that, it prints "Failed to Create File", this is the code I used:
#include <QString>
#include <QTextStream>
#include <QFile>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTextStream output(stdout);
QString location = "/home/mahmoud/Destkop";
QString name = "mahmoud.txt";
QString appendAll;
if( !location.endsWith("/") )
{
location.append("/");
}
appendAll = location.append(name);
output << appendAll << endl;
QFile myFile(appendAll);
if(myFile.open(QIODevice::WriteOnly | QIODevice::Text ))
{
output << "File Has Been Created" << endl;
}
else
{
output << "Failed to Create File" << endl;
}
QTextStream writeToFile(&myFile);
writeToFile << "Hello World" << endl;
myFile.close();
return a.exec();
}
But when I type the string directly into the QFile variable constructor in the same program it prints, "File Has Been Created" and I find it on my desktop, the below code works fine:
QFile myFile("/home/mahmoud/Desktop/mahmoud.txt");
if(myFile.open(QIODevice::WriteOnly | QIODevice::Text ))
{
output << "File Has Been Created" << endl;
}
else
{
output << "Failed to Create File" << endl;
}
I want to be able to already have QStrings and append them and send them to the QFile variable constructor, any suggestions on how to solve my problem? Thank You
Do not hard-code this filesystem location. Instead, in Qt4 you should be using QDesktopServices:
QString location =
QDesktopServices::storageLocation(QDesktopServices::DesktopLocation);
In Qt5, it's QStandardPaths:
QString location =
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
This is important because /home/username/Desktop is not guaranteed to be the user's Desktop folder.
There is typing error in your code: Destkop should be Desktop. Qt can't create file in non-existent directory.
OK, I'm a total beginner but I'm missing something here. Been all over the Qt documentation/examples and everything I can dig up through Google. All similar information is slightly different in context...
I'm just starting out with Qt SIGNALS and SLOTS, I'm successful with gui examples and within a class. Now I want to connect a SIGNAL in a child class with a SLOT in a sibling class with the Connect defined in the parent main. Ultimately my aim is to receive iamges in a class handling a QTcpSocket and emit the data as char* to be handled (saved or displayed) by another class.
For now I've just created the most basic version of the arrangement in a Console app as a learning exercise. I've got a sender class...
sender.h
#ifndef SENDER_H
#define SENDER_H
#include <QObject>
class sender : public QObject
{
Q_OBJECT
public:
sender(QObject *parent = 0);
~sender();
signals:
void output(int data);
public slots:
void test(int data);
private:
};
#endif // SENDER_H
sender.cpp
#include <iostream>
#include "sender.h"
sender::sender(QObject *parent)
: QObject(parent)
{
std::cout << "Created sender" << std::endl;
int stuff = 47;
std::cout << "stuff = " << stuff << std::endl;
connect(this, SIGNAL(output(int)), this, SLOT(test(int)));
emit output(stuff);
}
void sender::test(int data)
{
std::cout << "Got to test, data = " << data << std::endl;
}
sender::~sender()
{
std::cout << "Destroying sender" << std::endl;
}
...and a receiver class...
receiver.h
#ifndef RECEIVER_H
#define RECEIVER_H
#include <QObject>
class receiver : public QObject
{
Q_OBJECT
public:
receiver(QObject *parent = 0);
~receiver();
public slots:
void input (int data);
private:
};
#endif // RECEIVER_H
receiver.cpp
#include <iostream>
#include "receiver.h"
receiver::receiver(QObject *parent)
: QObject(parent)
{
std::cout << "Created receiver" << std::endl;
}
void receiver::input(int data)
{
std::cout << "Got data as = " << data << std::endl;
}
receiver::~receiver()
{
std::cout << "Destroying receiver" << std::endl;
}
My main looks like this...
main.cpp
#include <QtCore/QCoreApplication>
#include <iostream>
#include "sender.h"
#include "receiver.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
receiver myReceiver;
sender mySender;
if (QObject::connect(&mySender, SIGNAL(output(int)),
&myReceiver, SLOT(input(int))))
{
std::cout << "Got here so connect returned true" << std::endl;
}
return a.exec();
}
I've added the cout outputs and the sender::test function to try and figure out what's happening.
For me, this compiles cleanly and runs without any warnings or errors but while the sender::test SLOT gets called the receiver::input SLOT doesn't. The test on the connect in main returns true and neither sender or receiver are destroyed prematurely. Console output is...
Created receiver
Created sender
stuff = 47
Got to test, data = 47
Got here so connect returned true
So the SIGNAL is emitted, the SIGNAL and SLOT parameters match, I've got the Q_OBJECT macros in both sender.h and receiver.h, and both inherit from and #include QObject.
What's wrong???
P.S. I'm running 4.8.3 and IDE is VS2010 with Qt plugin.
The answer is quite simple: you are sending signal before you've connected it to the receiver and after you've connected it to itself. So your output is absolutely correct.
Do all emits only after everything has been connected. Here the instantiation of sender happens before sender and receiver are connected, and that's where the emit was done.