Uploading a file onto a FTP server using QNetworkAccessManager is not working, now small problems mit QtFTP ;-) [SOLVED]



  • Hello,

    I am trying to upload a file onto a ftp server.
    As I have seen QFtp is deprecated and so I tried to use QNetworkAccessManager.

    I have now searched several hours on the internet to get to know how I can achieve my goal.
    Sadly I was not successfull and I found only one good example here in the forum written from "ChrisW67".

    This is the code:

    #ifndef UPLOADER_H
    #define UPLOADER_H
    
    #include <QtCore>
    #include <QtNetwork>
    #include <QDebug>
    
    class Uploader: public QObject
    {
        Q_OBJECT
    public:
        Uploader(QObject *p = 0): QObject(p)     {  }
    
        void start(const QString &file)     {
            QUrl url("ftp://ftp.abc.com/site/");
            url.setUserName("myname");
            url.setPassword("12345");
            url.setPort(21);
          
            data = new QFile(file, this);
            if (data->open(QIODevice::ReadOnly)) {
                reply = nam.put(QNetworkRequest(url), data);
                connect(reply, SIGNAL(uploadProgress(qint64, qint64)), SLOT(uploadProgress(qint64, qint64)));
                connect(reply, SIGNAL(finished()), SLOT(uploadDone()));
            }
            else
                qDebug() << "Oops";
        }
    
    public slots:
        void uploadProgress(qint64 bytesSent, qint64 bytesTotal)    {
            qDebug() << "Uploaded" << bytesSent << "of" << bytesTotal;
        }
    
        void uploadDone()     {
            qDebug() << "Finished" << reply->error();
            data->deleteLater();
            reply->deleteLater();
        }
    
    private:
        QNetworkAccessManager nam;
        QFile *data;
        QNetworkReply *reply;
    };
    #endif // UPLOADER_H
    
    

    The corresponding cpp file is empty.

    If I now run the code in my main function like this :

    Uploader u;
        u.start("/home/test.dat");
    

    I get the following console output:
    qt.network.ssl: QSslSocket: cannot resolve SSLv2_client_method
    qt.network.ssl: QSslSocket: cannot resolve SSLv2_server_method
    Uploaded 0 of 0
    Finished 202

    I have tried several things, but I always get the same output.
    The connection details are working fine.
    As I understood the ssl messages are just warnings, but I have no idea why there is written "Uploaded 0 of 0".

    Has anyone an idea what is wrong?

    Thank you very much :-)





  • Thank you very much :-)
    I managed to install QFtp and uploading is working fine.

    Now I also tried to download a file, but this is not working.
    A file with the right name is created on the harddrive, but is it empty.
    The console tells me "QIODevice::write: device not open" and I have no idea why :-/

    Later on I would like to download several images from a ftp server, but first I need to manage to download at least one test file ;-)

    This is the code I use:

    int main(int argc, char* argv[])
    {
        QApplication app(argc, argv);
        MainWindow w;
        w.show();
    
        QFile *file;
            file = new QFile("test.dat");
    
        myFtp* ftp = new myFtp();
            ftp->connectToHost("ftp.ne.com");
            ftp->login("name","12345");
            ftp->cd("test");
            ftp->list();
    
            if (file->open(QIODevice::WriteOnly)){
            ftp->get("test.dat", file);
            }
            ftp->list();
            file->close();
        return app.exec();
    }
    

    Could you please help me?

    Thank you very much :-)



  • You are running the complete stack of commands outside of teh event loop. Since you are downloading you sent a commend and have to wait for response. Probably you have closed already the file file->close()before the file download ftp->get("test.dat", file)has been finished or even started.
    You need to use event loop and slignal-slot mechanism for download.
    IIRC the Qt5 compatibility code has also an example. Otherwise check out the Qt4 QFtp example. This shall give you the basic handling structure as a good start.



  • Actually I understand what you mean, but I do not really understand what to connect. :-(

    This is how the ftp class I use looks:

    #include <QtNetwork>
    #include <QtFtp>
    #include <iostream>
    using namespace std;
    class myFtp:public QFtp
    {
      Q_OBJECT
    public:
      myFtp():QFtp()
      {
        m_MStateMap.insert(pair<int,QString>(0,"Unconnected"));
        m_MStateMap.insert(pair<int,QString>(1,"Host Lookup"));
        m_MStateMap.insert(pair<int,QString>(2,"Connecting"));
        m_MStateMap.insert(pair<int,QString>(3,"Connected"));
        m_MStateMap.insert(pair<int,QString>(4,"Login"));
        m_MStateMap.insert(pair<int,QString>(5,"Closing"));
    
        connect(this,SIGNAL(stateChanged(int)),this,SLOT(connectionStateChanged(int)));
        connect(this,SIGNAL(listInfo(QUrlInfo)),this,SLOT(listInfo2(QUrlInfo)));
        connect(this,SIGNAL(readyRead()),this,SLOT(readData()));
        connect(this,SIGNAL(commandFinished(int,bool)),this,SLOT(commandFinished(int,bool)));
        connect(this,SIGNAL(dataTransferProgress(qint64,qint64))
                ,this,SLOT(viewProgress(qint64,qint64)));
      };
      ~myFtp()
      {};
    
    private slots:
      void connectionStateChanged(int state)
      {
        cout<<"Connection State Changed->"
             << state<<" Status: "
             <<m_MStateMap.find(state)->second.toStdString()
             <<endl;
      };
    
      void viewProgress(qint64 done ,qint64 total)
      {
        cout<<"Transferred Data: "<<done
        <<endl<<"Remaining: "<<total<<endl;
      };
    
      void listInfo2(QUrlInfo info)
      {
        cout<<"Listing URL Info:"<<info.name().toStdString()<<endl;
      };
    
      void commandFinished(int id,bool error)
      {
        cout<<"Command Finished! Id:"<<id<<"-Status:"<<error<<endl;
      };
    
      void readData()
      {
        cout<<"Read File Content-->"<<endl
        <<QString(readAll()).toStdString()
        <<endl
        <<"Read File END "<<endl;
      };
    
    private:
      map<int,QString> m_MStateMap;
    
    };
    

    Maybe you could give me a hint what to do ;-)
    Thanks a lot :-)



  • where is the instance of your ftp object?
    Respectively is the event loop running when the download is started?
    BTW the event loop is started when the statement return app.exec(), is executed.
    Are the slots being called?



  • Ok, I now made a minimal example.
    In my main here I am not using Signals and Slots.
    I just placed the code in the main for testing. Later on I will use Signals and Slots.
    At the moment I check a variable of my ftp object to see whether transfer is completed.

    Sadly the variable never becomes true.

    I have acutally no idea why :-(

    ftp.h:

    #ifndef FTP_H
    #define FTP_H
    
    #include <QtNetwork>
    #include <QtFtp>
    #include <iostream>
    using namespace std;
    class myFtp:public QFtp
    {
      Q_OBJECT
    public:
      myFtp():QFtp()
      {
        connect(this,SIGNAL(done(bool)),this,SLOT(finishedTransfer(bool)));
      };
      ~myFtp()
      {};
    
    private slots:
      void finishedTransfer(bool status)
      {
          cout<<"Transfer complete: "<< status << endl;
          finished=status;
      };
    
    public:
     bool finished=false;
    
    };
    
    
    #endif
    

    main.cpp

    #include "mainwindow.h"
    #include <QApplication>
    #include <QFile>
    #include <QIODevice>
    #include "ftp.h"
    
    
    int main(int argc, char* argv[])
    {
        QApplication app(argc, argv);
        MainWindow w;
        w.show();
    
        QFile *file;
            file = new QFile("test.dat");
    
        myFtp* ftp = new myFtp();
            ftp->connectToHost("ftp.abcd.com");
            ftp->login("bla.com","12345");
            ftp->cd("test");
    
            if (file->open(QIODevice::WriteOnly)){
            ftp->get("test.dat", file);
            while(ftp->finished==false)  {  }
            }
    
            ftp->list();
            file->close();
    
        return app.exec();
    }
    
    

    Do you know whats wrong?
    I dont get it :-/



  • #include "mainwindow.h"
    #include <QApplication>
    #include <QFile>
    #include <QIODevice>
    #include "ftp.h"
     
    int main(int argc, char* argv[])
    {
        QApplication app(argc, argv);
        MainWindow w;
        w.show();
    ////   >>>>>>>>>>>>>>>>>>>>>>>begin section move to separate class
        QFile *file;
        file = new QFile("test.dat");
     
         myFtp* ftp = new myFtp();
         ftp->connectToHost("ftp.abcd.com");
         ftp->login("bla.com","12345");
         ftp->cd("test");
    
         if (file->open(QIODevice::WriteOnly)){
            ftp->get("test.dat", file);
            while(ftp->finished==false)  {  }
            }
    
            ftp->list();
            file->close();
    //// >>>> end of section for moving
        return app.exec();  // <<<<<<<<<<<<<<<<< this starts the event loop 
    }
    

    You are trying to do all before the event loop is started, but you need the event loop for downloading.

    Assuming that you have downloaded the complete compatibility code QtFtp. There is also an example. Compile and try to run.
    Alternatively, there is also the Qt4.8 example. I believe it should also work with the compatibility code.



  • I now understood that FTP runs somehow in the background and also that I have to wait until it is completed.

    As you said I moved the code to a new class and I also included an eventloop waiting for the FTP to finish.

    Still it is not working. :-(
    I also had a look on the included example and the one you linked, but I really do not understand whats wrong with my code.

    Sadly I just don´t see it :-(

    This is now my code:

    ftp.h:

    #ifndef FTP_H
    #define FTP_H
    
    #include <QtNetwork>
    #include <QtFtp>
    #include <iostream>
    using namespace std;
    class FTP:public QFtp
    {
      Q_OBJECT
    public:
      FTP():QFtp()
      {
        connect(this,SIGNAL(done(bool)),this,SLOT(finishedTransfer(bool)));
      };
      ~FTP()
      {};
    
    private slots:
      void finishedTransfer(bool status)
      {
          cout<<"Transfer complete: "<< status << endl;
      };
    };
    
    
    #endif
    

    main.cpp

    #include "mainwindow.h"
    #include <QApplication>
    #include "myftp.h"
    
    int main(int argc, char* argv[])
    {
        QApplication app(argc, argv);
        MainWindow w;
        w.show();
    
        MyFTP myFtp;
        myFtp.useFTP();
    
        return app.exec();
    }
    
    
    

    myFTP.cpp

    #include "myftp.h"
    
    MyFTP::MyFTP(QObject *parent) : QObject(parent)
    {
    
    }
    
    void MyFTP::useFTP()
    {
        file = new QFile("test.dat");
    
        ftp = new FTP();
        QEventLoop eventLoop;
        connect(ftp, SIGNAL(commandFinished(int,bool)), &eventLoop, SLOT(quit()));
    
        ftp->connectToHost("ftp.bla.com");
        ftp->login("blub","1²345");
        ftp->cd("test");
    
        if (file->open(QIODevice::WriteOnly)){
        ftp->get("test.dat", file);
        eventLoop.exec();
        }
    
        ftp->list();
        file->close();
    }
    

    MyFTP.h

    #ifndef MYFTP_H
    #define MYFTP_H
    
    #include <QObject>
    #include <QFile>
    #include <QIODevice>
    #include "ftp.h"
    
    class MyFTP : public QObject
    {
        Q_OBJECT
    public:
        explicit MyFTP(QObject *parent = 0);
    
        void useFTP();
    
    signals:
    
    public slots:
    
    
    private:
        QFile *file;
        FTP* ftp;
    
    };
    
    #endif // MYFTP_H
    
    


  • I have quickly changed these three modules. Just brain to keyboard no testing.

    The last statementreturn app.exec();in main.cpp starts the event loop I meant. This needs to be started for your signal to slot mechanism to work at all.
    I have added a QTimer with a single shot. This delays the execution of useFTP enough to allow the event loop to start.
    I have commented out closing of file, because you have to do it it somewhere else after the download is done. If you do it in the slot function, you close the file before something could be stored. Basically commands are put into a queue and sent, but the responses may even take a couple of seconds or much longer (depending on file size). Therefore, you will have to wait for closing of the file until you know that the command has been ececuted. The command has been executed when you receive the signal commandFinished for the download. It is a matter of taste if you are making some book-keeping that you know when the command has been executed. IIRC the int gives you the sequence number of the actual command which has been finished.

    main.cpp

    #include "mainwindow.h"
    #include <QApplication>
    #include <QTimer>
    #include "myftp.h"
    
    int main(int argc, char* argv[])
    {
        QApplication app(argc, argv);
        MainWindow w;
        w.show();
    
        MyFTP myFtp;
        // myFtp.useFTP();  This would start the same as before, but in an object. Still 
        
        QTimer::singleShot(1000, &myFtp, SLOT ( useFTP()) ); // shoots after 1 sec. This should be enough to start the event loop in next line
    
        return app.exec(); // this start the event loop you need.
    }
    

    myFTP.cpp

    #include "myftp.h"
    
    MyFTP::MyFTP(QObject *parent) : QObject(parent)
    {
    
    }
    
    void MyFTP::useFTP()
    {
        file = new QFile("test.dat");
    
        ftp = new FTP();
        // QEventLoop eventLoop;  
        connect(ftp, SIGNAL(commandFinished(int,bool)), &eventLoop, SLOT(quit()));
    
        ftp->connectToHost("ftp.bla.com");
        ftp->login("blub","1²345");
        ftp->cd("test");
    
        if (file->open(QIODevice::WriteOnly)){
        ftp->get("test.dat", file);
        // eventLoop.exec();  this wopuld start the event loop, but basicvally the same issue as in main.cpp
        }
    
        ftp->list();
    }
    

    MyFTP.h

    #ifndef MYFTP_H
    #define MYFTP_H
    
    #include <QObject>
    #include <QFile>
    #include <QIODevice>
    #include "ftp.h"
    
    class MyFTP : public QObject
    {
        Q_OBJECT
    public:
        explicit MyFTP(QObject *parent = 0);
    signals:
    
    public slots:
        void useFTP();   // moved from above 
    private:
        QFile *file;
        FTP* ftp;
    };
    
    #endif // MYFTP_H
    
    


  • Thank you very much for your patience and the nice explanation :-)

    Now I understood that I better should move my code into the MainWindowClass as the eventloop is then already running.
    In the main here my QApplication was namend app and this app is also the instance I have to connect e.g. like this connect(ftp, SIGNAL(commandFinished(int,bool)), &app, SLOT(quit())); , right?

    I also now understood that I have to wait for the commandFinished(int,bool) signal with the ID 9 as it means that the transfer is complete.

    Sadly the code is still not working. At least removing the close command for the file removes the error that the file is not open.
    The problem is this: connect(ftp, SIGNAL(commandFinished(int,bool)), &eventLoop, SLOT(quit()));
    I do not know the right slot for the eventLoop. Furthermore it is not really clear for me where and how to wait for the signal with the ID 9.
    I also tried again to create my own eventLoop but then still everything freezes :-/

    Thank you again :-)



  • @RolBri said:

    Now I understood that I better should move my code into the MainWindowClass as the eventloop is then already running.
    In the main here my QApplication was namend app and this app is also the instance I have to connect e.g. like this connect(ftp, SIGNAL(commandFinished(int,bool)), &app, SLOT(quit())); , right?

    The event loop does not have a signal or slot. It is merely required for processing of events as the name already suggests. Signals and slots are part of objects. While the signals are more an abtract indirect way of calling somewhere a function is a slot such a function. Emitting a signal is placing an event into a queue. The event loop is basically an endless loop waiting for events to process in sequence. Therefore when you have an event taking very long, your event loop is blocked until the slot routine has been finished.
    Summarizing the event handling is basically a delayed call to a member of an object.
    Certainly you can connect the commandFinished signal to QApplication's slot quit. However, this will quit your whole application right with the first occurence of signal commandFinished. I do not think that this is a good strategy. If I recall correctly, the signal is emitted after each successful completion of an FTP command. However, you have to execute a series of FTP commands.
    Just for your understanding it might better to connect the signal to a slot and when you receive the proper command id of your get, you can detect if the dowload was successful and close the file. When this is the case, you may call either through another signal or even through direct call quit.

    I also now understood that I have to wait for the commandFinished(int,bool) signal with the ID 9 as it means that the transfer is complete.

    Sadly the code is still not working. At least removing the close command for the file removes the error that the file is not open.
    The problem is this: connect(ftp, SIGNAL(commandFinished(int,bool)), &eventLoop, SLOT(quit()));
    I do not know the right slot for the eventLoop. Furthermore it is not really clear for me where and how to wait for the signal with the ID 9.
    I also tried again to create my own eventLoop but then still everything freezes :-/

    Your own event loop will not help you here at all.

    Unfortunately, my FTP dowload is not separate from my application and I could not find my initial trials anymore.

    Once again I like to bring the Network examples to your attention. There is a simple file download example and one a bit more complex download manager.. Checking those out may help you to gain the basics behind. Personally I have found that such examples debugged with the debugger as very helpful. For instance you can set break points in the slots and verify the individual calls.



  • Thank you very much :-)

    I tried the whole morning to fix it or at least to understand what to do.
    I also now check all status id´s in the console from the signals commandStarted and commandFinished.

    I again tried also to upload a file using this code:

    ftp = new FTP();
    
        ftp->connectToHost("ftp.bla.com");
        ftp->login("name","pw");
        ftp->cd("test");
        ftp->put("hallo","rttt.dat");
    

    It get the following console output where you never find the ID for uploading but the file was successfully uploaded! Also I have not even the smallest idea why here is an QIODevice error.

    Command Started! Id: 1 Set transfer mode
    Command Finished! Id: 1 Set transfer mode Error: 0
    Command Started! Id: 2 Set proxy
    Command Finished! Id: 2 Set proxy Error: 0
    Command Started! Id: 3 Connect to host
    Command Finished! Id: 3 Connect to host Error: 0
    Command Started! Id: 4 Execute login
    Transferred Data: 0
    Remaining: 5
    Transferred Data: 5
    Remaining: 5
    QIODevice::read: device not open
    Command Finished! Id: 4 Execute login Error: 0
    Transfer complete: 0
    

    The behavior of Qt looks strange to me.

    All I tried was to write a simple code for down/ and uploading a file but this seems not to be possible.
    I have really no idea to fix all this and for the moment I give up.... :-(



  • The main difference I see is that you are using an FTP account with password, while my application downloads through an anonymuous account. You may be something you like to test.
    Strangely, I am facing also problems with maintenance tool and an FTP account with password for update.



  • I tried again to fix everything and now it is working.

    I made two big mistakes.

    1. I was not closing the files correctly. No I am waiting for the done()-signal and close the file.
    2. The documentation of QtFtp is a little bit confusing. I first understood that e.g. commandFinished() would always return the same number for a certain command like put or get. But this is not the case. With every scheduled and finished command you just get the number of the place in the schedule ;-)

    Thank you again for your help :-)


Log in to reply