qt 实现 ftp 上传与下载

发布时间:2023年12月18日

????????最近接个需求,需要实现 ftp 的上传与下载,开始也是参考网上的一些写法,大概如下:

// 创建网络访问管理器
QNetworkAccessManager manager;

// 创建网络请求对象
QNetworkRequest request(QUrl(ftpUrl));

// 设置 FTP 用户名和密码
request.setRawHeader("Authorization", "Basic " + QByteArray("username:password").toBase64());

// 发送 PUT 请求,将文件上传到 FTP 服务器
QNetworkReply *reply = manager.put(request, &file);

????????虽然 reply->error() 返回的是?QNetworkReply::NoError,但传输文件就是失败,而?reply->errorString() 返回的也只是?unknown error,无从下手。

????????在此基础上尝试了多种方法,包括设置代理、设置被动模式等,均无效。后来只好尝试 QFtp 类,尽管这个类已被?qt5.0 版本废弃。

????????一开始实现的基本这样:

ftp.connectToHost(host);
ftp.login(user, password);
ftp.put(&file, remoteFileName);
file.close();

????????依然会报错,经一番研究后才发现每个 QFtp 类的每个接口都是异步的,在调用各方法时不能按顺序依次调用,需要有信号机制,这点从诸多方法的注释中也能了解:

The function does not block and returns immediately. The command?is scheduled, and its execution is performed asynchronously. The function returns a unique identifier which is passed by commandStarted() and commandFinished().

When the command is started the commandStarted() signal is emitted. When it is finished the commandFinished() signal is emitted.

????????所以可以利用 qt 的信号机制来实现这点:

connect(&ftp, SIGNAL(commandFinished(int, bool)), this, SLOT(uploadHandleFinish(int, bool)));

????????其中 uploadHandleFinish() 是根据业务需要自定义的方法:

void Class::uploadHandleFinish(int id, bool error)
{
    qDebug() << "id:" << id << ", error:" << error << ", msg:" << ftp.errorString();
    switch (id) {
        case 1:
            qDebug() << "ftp is connected to " << host;
            signal_id = ftp.login(USER_NAME, PASSWORD);
            break;
        case 2:
            qDebug() << "ftp is logined to " << host;
            signal_id = ftp.put(file, remote_path);
            break;
        case 3:
            qDebug() << "end upload to remote host" << host;
            upload_download = 0;
            if (file->isOpen()) {
                file->close();
                delete file;
            }
            ftp.disconnect();
            ftp.close();
            break;
        default:
            break;
    }
}

????????当 connectToHost() 方法执行结束时会返回唯一标识1,信号器 handleFinish 收到1后可调用 login() 方法进行登录;

????????待 login() 方法执行结束后返回唯一标识2,信号器?handleFinish 收到2后可调用 put() 方法进行上传,以此类推。

????????这样就实现了 ftp 的上传功能,如果想看实时进度,可以绑定信号器?dataTransferProgress

connect(&ftp, SIGNAL(dataTransferProgress(qint64, qint64)), this, SLOT(ftpProgress(qint64, qint64)));

????????ftpProgress() 也是自定义的方法,可实现如下:

int progress = 0;
void Class::ftpProgress(qint64 read_bytes, qint64 total_bytes)
{
    int percent = (int)((qreal) read_bytes / total_bytes * 100);
    if (percent < progress + 10) {
        return;
    }
    progress = percent;
    qDebug() << "upload to remote host" << host << "progress:" << progress << "%";
}

????????这是并没有直接输出进度,而是做了些简单的处理。如果直接输出,那么该函数打印的会非常频繁,一秒钟就会触发多次,这里是设置了只当进度达到 10% 的整数倍时才记录。

????????其实我一开始想的是设置信号器触发的频率,从源头解决,减少不必要的调用,但是搜了一圈,也没看到好的办法,只好在达到规定的进度时才打印出来,但实际上每次都还是有调用的。哪位小伙伴这块有更好的办法还请赐教哈!

????????下载和上传差不多,主方法如下:

signal_id = ftp.connectToHost(host, FTP_PORT);

connect(&ftp, SIGNAL(commandFinished(int, bool)), this, SLOT(downloadHandleFinish(int,bool)));
connect(&ftp, SIGNAL(dataTransferProgress(qint64, qint64)), this, SLOT(ftpProgress(qint64, qint64)));

????????自定义的?downloadHandleFinish()ftpProgress() 的实现与上传类似,就不粘贴了。

文章来源:https://blog.csdn.net/yang1018679/article/details/134580505
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。