QT实现FTP下载文件功能

本文详细介绍了如何使用QT的QNetworkAccessManager和TCP方式实现FTP文件下载功能。首先展示了利用QNetworkAccessManager的代码实现,包括设置进度对话框、错误处理等。接着,介绍了使用TCP的被动模式下载文件,包括发送FTP命令、解析服务器响应、建立数据连接等步骤。整个过程涵盖了FTP的登录、PASV模式、数据传输等关键环节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用QT的QNetworkAccessManager下载文件

代码实现

{
    // /mnt/nas03/product/tools/RF-MFbox/2.txt
    QString folderPath;
    folderPath = QFileDialog::getExistingDirectory(this, tr("选择文件"), "", QFileDialog::ShowDirsOnly);
    file = new QFile(folderPath + "/2.txt");
    file->open(QIODevice::ReadWrite);
    ui->lineEdit->setText(folderPath + "/2.txt");
    qDebug() << folderPath;
    //创建一个进度对话框
    progressDialog = new QProgressDialog(this);
    //设置进度对话框采用模态方式进行,即显示进度的同时,其他窗口将不响应输入信号
    progressDialog->setWindowModality(Qt::WindowModal);
    //设置进度对话框的窗体标题
    progressDialog->setWindowTitle(tr("Please Wait"));
    //设置进度对话框的显示文字信息
    progressDialog->setLabelText(tr("正在下载..."));
    //设置进度对话框的“取消”按钮的显示文字
    progressDialog->setCancelButtonText(tr("取消"));
    
   QUrl m_pUrl("ftp://192.168.20.119/home/zhanglf412/linux/2.txt");
   m_pUrl.setUserName("zhanglf412");
   m_pUrl.setPassword("zhang412lf");
   qDebug()<< m_pUrl;

    accessManager = new QNetworkAccessManager(this);
    accessManager->setNetworkAccessible(QNetworkAccessManager::Accessible);

    reply = accessManager->get(QNetworkRequest(m_pUrl));

    connect(reply, SIGNAL(readyRead()), this, SLOT(readContent()));
    connect(accessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
    connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),SLOT(loadError(QNetworkReply::NetworkError)));
    connect(reply, SIGNAL(downloadProgress(qint64 ,qint64)), this, SLOT(loadProgress(qint64 ,qint64)));

}

void MainWindow::readContent()    //下载时向本地文件中写入数据
{
    file->write(reply->readAll());
}

void MainWindow::replyFinished(QNetworkReply *)
{
    qDebug() << "NO ERROR";
    if (reply->error() == QNetworkReply::NoError)
    {
        file->close();
    }
    else
    {
        qDebug() << " ERROR";
        QMessageBox::critical(NULL, tr("Error"), "Failed!!!");
    }
}

void MainWindow::loadProgress(qint64 bytesSent, qint64 bytesTotal)
{
    qDebug() << "loaded" << bytesSent << "of" << bytesTotal;
    progressDialog->setRange(0,bytesTotal);
    progressDialog->setValue(bytesSent);  //当前值
}
// 错误处理
void MainWindow::loadError(QNetworkReply::NetworkError error)
{
    switch (error) {
    case QNetworkReply::HostNotFoundError :
        qDebug() << QString::fromLocal8Bit("主机没有找到");
        break;
        // 其他错误处理
    default:
        break;
    }
}

使用QT的TCP下载文件

被动模式(常用)

根据上文所述,既然都“被动”了,那我们客户端就等着服务器给我们分配端口号吧。

(1)首先我们任意打开一个端口N连接FTP服务器,并在Socket中写“PASV”命令。这里的N必须大于1024;

(2)然后服务器会返回个信息,类似“227 Entering Passive Mode(47,94,99,120,39,18)”这样。前四个是服务器IP地址,后两个是和分配的端口号相关的数。从这条信息我们就知道了服务器给我们分配的用于数据传输的端口号是:39*256+18;

(3)然后我们就打开第二个端口N+1和服务器的这个39*256+18端口进行连接,然后数据传输。
FTP命令

命令字符串结尾要加'\n' 

ABOR:中断数据连接程序
ACCT <account>:系统特权帐号
ALLO <bytes>:为服务器上的文件存储器分配字节
APPE <filename>:添加文件到服务器同名文件
CDUP <dir path>:改变服务器上的父目录
CWD <dir path>:改变服务器上的工作目录
DELE <filename>:删除服务器上的指定文件
HELP <command>:返回指定命令信息
LIST <name>:如果是文件名列出文件信息,如果是目录则列出文件列表
MODE <mode>:传输模式(S=流模式,B=块模式,C=压缩模式)
MKD <directory>:在服务器上建立指定目录
NLST <directory>:列出指定目录内容
NOOP:无动作,除了来自服务器上的承认
PASS <password>:系统登录密码
PASV:请求服务器等待数据连接
PORT <address>:IP 地址和两字节的端口 ID
PWD:显示当前工作目录
QUIT:从 FTP 服务器上退出登录
REIN:重新初始化登录状态连接
REST <offset>:由特定偏移量重启文件传递
RETR <filename>:从服务器上找回(复制)文件
RMD <directory>:在服务器上删除指定目录
RNFR <old path>:对旧路径重命名
RNTO <new path>:对新路径重命名
SITE <params>:由服务器提供的站点特殊参数
SMNT <pathname>:挂载指定文件结构
STAT <directory>:在当前程序或目录上返回信息
STOR <filename>:储存(复制)文件到服务器上
STOU <filename>:储存文件到服务器名称上
STRU <type>:数据结构(F=文件,R=记录,P=页面)
SYST:返回服务器使用的操作系统
TYPE <data type>:数据类型(A=ASCII,E=EBCDIC,I=binary)
USER <username>:系统登录的用户名

FTP响应码

110:新文件指示器上的重启标记
120:服务器准备就绪的时间(分钟数)
125:打开数据连接,开始传输
150:打开连接
200:成功
202:命令没有执行
211:系统状态回复
212:目录状态回复
213:文件状态回复
214:帮助信息回复
215:系统类型回复
220:服务就绪
221:退出网络
225:打开数据连接
226:结束数据连接
227:进入被动模式(IP 地址、ID 端口)
230:登录因特网
250:文件行为完成
257:路径名建立
331:要求密码
332:要求帐号
350:文件行为暂停
421:服务关闭
425:无法打开数据连接
426:结束连接
450:文件不可用
451:遇到本地错误
452:磁盘空间不足
500:无效命令
501:错误参数
502:命令没有执行
503:错误指令序列
504:无效命令参数
530:未登录网络
532:存储文件需要帐号
550:文件不可用
551:不知道的页类型
552:超过存储分配
553:文件名不允许

代码实现

void MainWindow::on_pushButton_2_clicked()
{
    QString folderPath;
    folderPath = QFileDialog::getExistingDirectory(this, tr("选择文件"), "", QFileDialog::ShowDirsOnly);
    file = new QFile(folderPath + "/XXX.hex");
    file->open(QIODevice::ReadWrite);
    ui->lineEdit->setText(folderPath + "/XXX.hex");
    qDebug() << folderPath;

    /* 构造Socket,并连接 */
    ftpsocket = new QTcpSocket(this);
    ftpsocket->connectToHost("XXX.XXX.XXX.XXX",21);
    connect(ftpsocket,&QTcpSocket::connected,this,&MainWindow::slotConnected);
    connect(ftpsocket,&QTcpSocket::readyRead,this,&MainWindow::dataReceived);
    
}
void MainWindow::slotConnected()
{
    qDebug() << "已建立Socket连接";
}
void MainWindow::dataReceived()
{
    while (ftpsocket->bytesAvailable())
    {
        /* 读取Socket并存入datagram */
        QByteArray datagram = ftpsocket->readAll();

        QString str = QString::fromLocal8Bit(datagram);

        qDebug() << str;
        ui->send_text->append(str);
        //220:服务就绪
        if(str.contains("220",Qt::CaseSensitive))
        {
            QByteArray command = QString("USER product\n").toUtf8();
            ftpsocket->write(command);
        }
        //要求密码
        else if(str.contains("331",Qt::CaseSensitive))
        {
            QByteArray command = QString("PASS product!Q2w3e\n").toUtf8();
            ftpsocket->write(command);
        }
        //登陆成功
        else if(str.contains("230",Qt::CaseSensitive))
        {
            QByteArray command = QString("PASV\n").toUtf8();
            ftpsocket->write(command);
        }
        //要求密码
        else if(str.contains("227",Qt::CaseSensitive))
        {
            int pos1,pos2,port;
            QString ip;
            QString ip_addr;

            pos1 = str.indexOf("(");
            str.remove(0,pos1+1);
            pos2 = str.indexOf(",");  //查找第一个','
            ip = str.left(pos2);
            ip_addr.append(ip);

            str.remove(0,pos2+1);
            pos2 = str.indexOf(",");  //查找第一个','
            ip = str.left(pos2);
            ip_addr.append("."+ip);


            str.remove(0,pos2+1);
            pos2 = str.indexOf(",");  //查找第一个','
            ip = str.left(pos2);
            ip_addr.append("."+ip);

            str.remove(0,pos2+1);
            pos2 = str.indexOf(",");  //查找第一个','
            ip = str.left(pos2);
            ip_addr.append("."+ip);

            str.remove(0,pos2+1);
            pos2 = str.indexOf(",");  //查找第一个','
            ip = str.left(pos2);
            port = ip.toInt()*256;

            str.remove(0,pos2+1);
            pos2 = str.indexOf(")");  //查找第一个','
            ip = str.left(pos2);
            port += ip.toInt();

            //qDebug() << ip_addr;
            //qDebug() << QString::number(port);

            /* 构造Socket,并连接 */
            ftp_data_socket = new QTcpSocket(this);
            ftp_data_socket->connectToHost("XXX.XXX.XXX.XXX",port);
            connect(ftp_data_socket,&QTcpSocket::connected,this,&MainWindow::ftpConnected);
            connect(ftp_data_socket,&QTcpSocket::readyRead,this,&MainWindow::ftpdataReceived);
        }
        else if(str.contains("226",Qt::CaseSensitive))
        {
            QByteArray command = QString("QUIT\n").toUtf8();
            ftpsocket->write(command);
        }
        else if(str.contains("221",Qt::CaseSensitive))
        {
            qDebug() << "下载完毕";
            ui->send_text->append("下载完毕");
            file->close();
        }
    }
}

void MainWindow::ftpConnected()
{
    qDebug() << "2 已建立Socket连接";
    QByteArray command = QString("RETR XXXXX\n").toUtf8();
    ftpsocket->write(command);
}

void MainWindow::ftpdataReceived()
{

    if (!ftp_data_socket->bytesAvailable())
        return;

    /* 读取Socket并存入datagram */
    QByteArray datagram = ftp_data_socket->readAll();
    file->write(datagram);

}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值