Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Console App - Connect to FTP and download csv files to local device/storage help



  • Hi there, I am really new to QT and programming in general (GIS background) so I am having trouble understanding how to use the QNetworkAccessManager to connect to our secure FTP and download a few csv files to the device. In the app that we currently have out it connects to our FTP and depending on if its Android or iOS it downloads 3 csv files (they are encoded differently for the different platforms). We also have a zip file full of photos that needs to be downloaded locally. This process occurs on the initial start up of the app after it has been downloaded so this process only occurs once.

    Right now I have some code in my main.cpp that will connect to the FTP and read a specific csv file that I have hard coded into the QNetworkRequest url but what I need it to do, at least at the moment is to download 3 specific csv files and save them locally so other pages within the app can be queried against them. Is something like this even possible in QT?

    I found this code elsewhere so I did not write it myself. I am having a really difficult time understanding and wrapping my head around slots and signals seeing that there seems to be a couple ways to write them. I believe this is why I haven't been able to make any progress with this.

    QEventLoop loop;
        QNetworkAccessManager netMan;
        QObject::connect(&netMan, &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);
        QNetworkReply* const repl = netMan.get(QNetworkRequest(QUrl::fromUserInput(R"**(ftp url to specific file location)**")));
        QObject::connect(repl,&QNetworkReply::readyRead,[repl]()->void{qDebug().noquote() << repl->readAll();});
        QObject::connect(repl,QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error),[repl]()->void{qDebug() << "Error: " + repl->errorString();});
        QObject::connect(repl,&QNetworkReply::finished,repl,&QNetworkReply::deleteLater);
        QObject::connect(&netMan,&QNetworkAccessManager::authenticationRequired,repl,[repl](QNetworkReply *reply, QAuthenticator *authenticator)->void{
            if(reply!=repl)
                return;
            authenticator->setUser("username");
            authenticator->setPassword("password");
        });
        loop.exec();
    

    any help would be greatly appreciated as I have spent the last two months off and on trying to find some sort of example that works for a console app.



  • @luckachi said in Console App - Connect to FTP and download csv files to local device/storage help:

    Download of "file name" failed: Protocol "" is unknown

    How does your URL look like? Do you use the full URI (something like: ftp://ftp.example.com/foo/bar.csv)? Otherwise you have to add QUrl::fromUserInput( ...... ) when constructing your URL. Same as in your original post. I guess, this is why it could not find any protocol, because the ftp:// was probably missing?!

    @luckachi said in Console App - Connect to FTP and download csv files to local device/storage help:

    added the following to the downloadFinished slot under the QUrl url = reply->url(); line.
    url.setUserName(" ");
    url.setPassword(" ");

    At this time the magic has happened already :)
    downloadFinished is emitted when you already got your reply, either your data or any error msg. You want to add your credentials before downloading (when the request happens).
    It would be somewhere inside the execute - function then, around line 160. If you want to do it that way (instead of using QAuthenticator).
    (https://code.qt.io/cgit/qt/qtbase.git/tree/examples/network/download/main.cpp#n160)

    https://doc.qt.io/qt-5/qurl.html#setUserInfo



  • Hi and welcome :)

    @luckachi said in Console App - Connect to FTP and download csv files to local device/storage help:

    Is something like this even possible in QT?

    Sure. Why not?

    Signals and slots are not that complicated as they look.

    There is this old (string-based) syntax which uses SIGNAL and SLOT keywords and the new one (since Qt5) which is used in your example code as well.

    (Your code makes also use of lambda functions / connections, which are just a bit more complicated)

    @luckachi said in Console App - Connect to FTP and download csv files to local device/storage help:

    I am having a really difficult time understanding and wrapping my head around slots and signals seeing that there seems to be a couple ways to write them. I believe this is why I haven't been able to make any progress with this.

    What exactly you don't understand? Does this code work for you? Any errors? Or does it not work at all?



  • @Pl45m4 Thanks and I appreciate the response ! :)

    Ahh okay, so the syntax has changed since Qt5 - thank you for these links. I believe I have read through the first one before but I have been bouncing between a few different things which are now finished so a fresh review will probably be helpful. I think what has been confusing me is the fact that I have seen examples of the old and new syntax and wasn't sure which one to use or if they were interchangeable so at least now that I am aware of the change, that clears things up a bit.

    The code does work for me, it reads the csv file from the hard coded URL just fine but I am just not sure how to proceed when it comes to multiple files and then downloading / making a copy of the file on the device.



  • @luckachi said in Console App - Connect to FTP and download csv files to local device/storage help:

    I think what has been confusing me is the fact that I have seen examples of the old and new syntax and wasn't sure which one to use or if they were interchangeable

    The "old" one is still valid and usable. In new programs it's recommended to use the new one.
    Basically they both do the same (establish a connection between a signal and a slot / function), so they are interchangeable (in most cases), but there are a couple differences
    For example the new function based connection has a parameter check on compile time, while the string-based connections are checked at run time first. So a program which uses a new style connection with a parameter mismatch, won't compile.

    https://doc.qt.io/qt-5/signalsandslots-syntaxes.html

    @luckachi said in Console App - Connect to FTP and download csv files to local device/storage help:

    I am just not sure how to proceed when it comes to multiple files and then downloading / making a copy of the file on the device.

    As you've said, your current example will download one (hard coded) file and then all instances get cleaned up.
    To download multiple files, you could make a for-loop around the download-command and get your QUrls from a QStringList or QVector <QUrl>. The netMan instance has to live until the very end (until all downloading has been done).

    There's is this offical Qt example, which fits perfectly in your case. So you might want to take a look at it :)

    https://doc.qt.io/qt-5/qtnetwork-download-example.html

    Direct link to code:

    https://code.qt.io/cgit/qt/qtbase.git/tree/examples/network/download/main.cpp



  • @Pl45m4 Thank you for the explanation on the slots/signals. I appreciate it.

    You know, I have looked at this example off an on for the last two months or so and I wasn't really understanding where my FTP connection would go along with the URLs.

    I would assume they code I have now or at least something similar would go under the section below seeing that it calls network request and reply?

    void DownloadManager::doDownload(const QUrl &url)
    
    

    It also sounds like the URLs should be placed in the command line argument box, is that correct? If so are they just separated by commas or is there another way to formation the arguments?

    Again, I appreciate your time with it - I barely have any coding background so this is all very new to me.



  • @luckachi said in Console App - Connect to FTP and download csv files to local device/storage help:

    I would assume they code I have now or at least something similar would go under the section below seeing that it calls network request and reply?

    Actually you don't need to change that much.

    execute / doDownload performs a parallel download of all files, that you've passed as argument. As far as I can see, separated by spaces. If the files, you want to download never change, you can remove that argument part and hard code those links, so that your download manager will access a pre-defined container of Urls and start downloading afterwards.

    I think you don't even have to use all the connections you're using currently. The only thing, you have to add, is your login credentials for the ftp server using QAuthenticator (the same way you do currently or something similar).

    The code also shows how to write downloaded content to a separate file.

    Go through the code to try to figure out what happens, when and why. Then it should be clear, which parts you can use and where you can add some of your stuff.



  • @Pl45m4 Thank you! Spent most of the day reading the code over and some other information and it seems like things are starting to make a bit more sense to me.

    I took the network download manager example and I ended up adding

    private slots:
        void authenticationRequired(QNetworkReply* reply, QAuthenticator* authenticator);
    
    void DownloadManager::authenticationRequired (QNetworkReply *reply, QAuthenticator *authenticator)
    {
        Q_UNUSED(reply)
        authenticator->setUser("myuser");
        authenticator->setPassword("mypass");
    }
    

    but I am receiving the following error: Download of "file name" failed: Protocol "" is unknown

    I also receive this error when I removed the above code and added the following to the downloadFinished slot under the QUrl url = reply->url(); line.

    url.setUserName(" ");
    url.setPassword(" ");
    


  • @luckachi said in Console App - Connect to FTP and download csv files to local device/storage help:

    Download of "file name" failed: Protocol "" is unknown

    How does your URL look like? Do you use the full URI (something like: ftp://ftp.example.com/foo/bar.csv)? Otherwise you have to add QUrl::fromUserInput( ...... ) when constructing your URL. Same as in your original post. I guess, this is why it could not find any protocol, because the ftp:// was probably missing?!

    @luckachi said in Console App - Connect to FTP and download csv files to local device/storage help:

    added the following to the downloadFinished slot under the QUrl url = reply->url(); line.
    url.setUserName(" ");
    url.setPassword(" ");

    At this time the magic has happened already :)
    downloadFinished is emitted when you already got your reply, either your data or any error msg. You want to add your credentials before downloading (when the request happens).
    It would be somewhere inside the execute - function then, around line 160. If you want to do it that way (instead of using QAuthenticator).
    (https://code.qt.io/cgit/qt/qtbase.git/tree/examples/network/download/main.cpp#n160)

    https://doc.qt.io/qt-5/qurl.html#setUserInfo



  • @Pl45m4 You have been a life saver! I see the 3 csv files in my build folder! :) Thank you so very much for the help and for the clear explanations.

    At least I have a bit more confidence to tackle the next few steps of reading the file, querying and displaying the data as a list (fingers crossed).

    Thanks again!!


Log in to reply