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

Download multiple files from Ftp issue



  • Hi! I want to download some files from my ftp server. The problem is, that only the last one has data, others 0 sized or it crashes when close QFile as a pointer.

    My code:

    QFtp *ftp = new QFtp(this);
    ftp->connectToHost(FTP_HOST, FTP_PORT);
    ftp->login(FTP_USERNAME, FTP_PASSWORD);
    QFile *reportFile = nullptr;
    
    connect(ftp, &QFtp::listInfo, [this](const QUrlInfo &ftpUrlInfo) {
            if (ftpUrlInfo.isFile()) {
                reportFile = new QFile("some local path" + ftpUrlInfo.name());
                reportFile->open(QIODevice::WriteOnly);
                ftp->get("some ftp path" + ftpUrlInfo.name(), reportFile, QFtp::Binary);
            }
        });
    connect(ftp, &QFtp::done, [this]() {
            qDebug() << "DONE!";
            ftp->close();
            ftp->deleteLater();
        });
        connect(ftp, &QFtp::commandFinished, [this]() {
            qDebug() << "COMMAND FINISHED!";
    
                if (reportFile != nullptr) {
                    reportFile.close();
                    reportFile->deleteLater();
                }
        });
    
    ftp->list("ftp path to dir");
    

    So, it should download the file, close it and deleteLater for all files in the ftp directory. Any ideas how to do it? Thanks.



  • @Cobra91151
    How are you waiting for one file's get() to finish, before you set off the next one? You are reusing the same reportFile variable over & over, while it is still servicing previous file get()s? Hence you only get the final file, and the crashes when one file's reportFile.close(); closes/deletes the reportFile from a different file!? (Put a qDebug() in the first connect(), not just the other two.)

    Either wait if you want to do one-at-a-time synchronously, or you need to maintain multiple different reportFiles to do them all asynchronously.



  • @JonB

    Ok. I will change code and reply. Thanks.



  • I have tried to use QList<QFile*> and append the files when they are listing, and then loop it, and when command get() finished, close and deleteLater() the file, now it doesn't crash when use QFile as a pointer but all files are 0 sized.

    if (ftp->currentCommand() == QFtp::Get) {
         qDebug() << "GET FINISHED!";
         reportFile->close();
         reportFile->deleteLater();
    }
    

    I think that here is better to use QQueue and proceed the file one by one.


  • Lifetime Qt Champion

    Hi
    Did you replace reportFile with access to the QList<QFile*>
    so each invocation works on the right QFile?
    Should work just as fine as one.
    But yeah if 1 download works, than using a queue should be ok easy.



  • @mrjj

    Yes, 1 file downloads works. The problem with collection of files. I'm currently working to fix it.


  • Lifetime Qt Champion

    @Cobra91151
    Ok if concurrent download is not a must have, one by one should be fairly
    easy to implement if 1 download already works.

    It sounds like you did it correctly with QList<QFile*> but you never showed
    full code after the change so its unclear if just a normal bug-ish or
    something with QFtp .
    Also the shown connect(ftp, &QFtp::done ) would delete the ftp on first file finished
    but assume you fixed that also ?



  • @mrjj

    I will post more code soon. Yes, I have fixed it by creating closeFtp function and connect to it when it finished, so I just need to read more docs on the QQueue and fix the issue with the files.

    void Test::closeFtp()
    {
        qDebug() << "FTP CLOSED";
        ftp->close();
        ftp->deleteLater();
        emit finished();
    }
    

    I will reply later. Thanks.



  • @mrjj

    Hi! I have finally fixed it!

    My code:

    QQueue<QFile*> reportQueue; //initialize the queue
    
    connect(ftp, &QFtp::listInfo, [this](const QUrlInfo &ftpUrlInfo) {
            if (ftpUrlInfo.isFile()) {
                reportQueue.append(new QFile("local path" + "\\" + ftpUrlInfo.name()));
            }
        });
        connect(ftp, &QFtp::done, [this]() {
            emit reportsDataFinished();
        });
        connect(ftp, &QFtp::commandFinished, [this]() {
            if (ftp->currentCommand() == QFtp::List) {
                proceedDownload();
            } else if (ftp->currentCommand() == QFtp::Get) {
                reportFile->close();
                reportFile->deleteLater();
                proceedDownload();
            }
        });
    
        if (ftp->error() == QFtp::NotConnected) {
            emit ftpReportError(ftp->error());
        } else {
            ftp->list("ftp path to the dir");
        }
    
    void Test::proceedDownload()
    {
        if (!reportQueue.isEmpty()) {
            reportFile = reportQueue.dequeue();
            reportFile->open(QIODevice::WriteOnly);
            QFileInfo ftpFileInfo(reportFile->fileName());
            ftp->get("ftp path to file" + "/" + ftpFileInfo.fileName(), reportFile, QFtp::Binary);
        }
    }
    

    I added the files to the QQueue, when ftp list command has been finished, I use function proceedDownload(). In the function, I dequeue() the queue to the reportFile and proceed with ftp get() function. When get ftp command finishes, I close and delete the file from memory, and again call the proceedDownload(). So the the whole process goes again until the queue is empty. I use emit reportsDataFinished(); signal in the connection to the slot closeFtp() where ftp closes and deleteLater() frees the resources. All files downloads well. Thank you.


  • Lifetime Qt Champion

    Super :)
    Seems pretty compact.


Log in to reply