实现FTP Client功能
ubuntu搭建ftp服务器
1、选用vsftpd为我们的ftp服务器,端口号默认为21
安装:~$ sudo apt‐get install vsftpd
2、配置:~$ sudo gedit /etc/vsftpd.conf
3、 几个重要的参数:
anonymous_enable=NO 是否支持匿名访问
local_enable=YES 是否允许本地用户登录
write_enable=YES 是否给客户端写的权限 (默认只读)
4、几个重要的命令
~$ sudo /etc/init.d/vsftpd restart 重启vsftp服务 (ubuntu 18)
~$ ps ‐ef | grep ftp 查看ftp服务器是否启动
~$ sudo netstat ‐utlpn | grep vsftp 查看vsftp状态及端口号
PS:如果想修改端口号(建议不要修改)
1、编辑/etc/vsftpd/vsftpd.conf 文件,在该配置文件中添加此行:listen_port=6666
2、编辑/etc/services 文件,将其中的 ftp 21/tcp 改为 ftp 21/tcp 改为 ftp 6666/tcp
QT实现ftp的客户端![](https://i-blog.csdnimg.cn/blog_migrate/36e15624892f3089759d3db5b6f4ef8e.png)
通过该项目学会
QNetworkAccessManager上传和下载,对N多协议都支持;
QNetworkAccessManager 自己会新建线程 不用使用QThread
如果不需要遍历FtpServer目录可以直接使用QNetworkAccesssManager
- QUrl
- QNetworkAccessManager
- QNetworkReply
完整的ftp地址
完整代码
- ftpAccessManager.h
#ifndef FTPACCESSMANAGER_H
#define FTPACCESSMANAGER_H
#include <QObject>
#include <QUrl>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QFile>
#include <QFileInfo>
#include <QDebug>
#include <QEvent>
class ftpAccessManager : public QObject
{
Q_OBJECT
public:
explicit ftpAccessManager(QObject *parent = nullptr);
virtual bool event(QEvent *event);
public:
// 设置端口
void setHostPortInfo(const QString &host, quint16 prot =21);
void setUserPwdInfo(const QString &userName, const QString &pwd);
// 下载路径
void putFileFromClent(const QString &fileName, const QString &path);
void getFileFromServer(const QString &fileName, const QString &path);
signals:
/* 以下信号上传给界面 */
void uploadProgressSignal(qint64, qint64);
void downloadProgressSignal(qint64, qint64);
void netErrorSignal(QNetworkReply::NetworkError);
private slots:
//上传时的槽
void uploadProgressSlot(qint64, qint64);
void uploadFinishedSlot();
void uploadErrorSlot(QNetworkReply::NetworkError);
//下载时的槽
void downloadProgressSlot(qint64, qint64);
void downloadFinishedSlot();
void downloadErrorSlot(QNetworkReply::NetworkError);
private:
QUrl murl;
QNetworkAccessManager mNetAccMgr;
QFile *putFileData = nullptr; //上传数据的文件指针
QFile *getFileData = nullptr;
QNetworkReply *putNetReply; //上传的网络应答 QNetworkRequest请求
QNetworkReply *getNetReply; //下载数据的网络
};
#endif // FTPACCESSMANAGER_H
- ftpAccessManager.cpp
#include "ftpAccessManager.h"
ftpAccessManager::ftpAccessManager(QObject *parent)
: QObject{parent}
{
murl.setScheme("ftp"); // 设置协议
}
/*
* 设置FTP主机地址信息,包括Ip地址和端口号
*/
void ftpAccessManager::setHostPortInfo(const QString &host, quint16 prot)
{
murl.setHost(host);
murl.setPort(prot);
}
/*
* 设置FTP用户密码信息
*/
void ftpAccessManager::setUserPwdInfo(const QString &userName, const QString &pwd)
{
murl.setUserName(userName);
murl.setPassword(pwd);
}
/*
* 上传文件
* 参数1:本地文件(带路径) 参数2:服务器端文件(带路径) <服务器路径必须带上文件名,不然服务器不知道写什么样的文件>
*/
void ftpAccessManager::putFileFromClent(const QString &clientFile, const QString &serverPath)
{
putFileData = new QFile(clientFile, this);
QString serverFileNamePath = serverPath + "/" +
QFileInfo(clientFile).fileName(); //路径+文件拼接,保证服务器端写入的文件和本地文件同名
//step1 :: setUrl
murl.setPath(serverFileNamePath);
//step2:打开文件 把文件读入到网络流发出去
if(putFileData->open(QIODevice::ReadOnly))
{
//step3:upload 上传
putNetReply = mNetAccMgr.put(QNetworkRequest(murl), putFileData);
//step4:关联信号与槽
connect(putNetReply, SIGNAL(uploadProgress(qint64, qint64)), this, SLOT(uploadProgressSlot(qint64, qint64)));
connect(putNetReply, SIGNAL(finished()), this, SLOT(uploadFinishedSlot()));
connect(putNetReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(uploadErrorSlot(QNetworkReply::NetworkError)));
}
else
{
delete putFileData;
putFileData = nullptr;
}
}
/*
* 下载文件
* 参数1:需要写入的本地文件(带路径 不带路径则为当前目录) 参数2:服务器端的路径
*/
void ftpAccessManager::getFileFromServer(const QString &saveNameWithPath, const QString &downloadPath)
{
getFileData = new QFile(saveNameWithPath, this);
//step1 :: setUrl
murl.setPath(downloadPath);
if(getFileData->open(QIODevice::WriteOnly | QFile::Truncate))
{
getNetReply = mNetAccMgr.get(QNetworkRequest(murl));
//step4 :
connect(getNetReply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(downloadProgressSlot(qint64, qint64)));
connect(getNetReply, SIGNAL(finished()), this, SLOT(downloadFinishedSlot()));
connect(getNetReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadErrorSlot(QNetworkReply::NetworkError)));
}
else
{
delete getFileData;
getFileData = nullptr;
}
}
void ftpAccessManager::uploadProgressSlot(qint64 bytesSent, qint64 bytesTotal)
{
emit uploadProgressSignal(bytesSent, bytesTotal);
//qDebug() << "uploadProgressSlot" << bytesSent << bytesTotal;
}
void ftpAccessManager::uploadFinishedSlot()
{
qDebug() << "uploadFinishedSlot" ;
putNetReply->deleteLater();
delete putFileData;
putFileData = nullptr;
}
void ftpAccessManager::uploadErrorSlot(QNetworkReply::NetworkError errorCode)
{
putNetReply->deleteLater();
delete putFileData;
putFileData = nullptr;
switch (errorCode)
{
case QNetworkReply::HostNotFoundError :
qDebug() << "HostNotFoundError";
break;
case QNetworkReply::ConnectionRefusedError :
qDebug() << "ConnectionRefusedError";
break;
default:
qDebug() << "orther error" << errorCode;
break;
}
emit netErrorSignal(errorCode);
}
void ftpAccessManager::downloadProgressSlot(qint64 bytesSent, qint64 bytesTotal)
{
emit downloadProgressSignal(bytesSent, bytesTotal);
getFileData->write(getNetReply->readAll()); //写文件到磁盘
}
void ftpAccessManager::downloadFinishedSlot()
{
qDebug() << "downloadFinishedSlot";
getNetReply->deleteLater();
delete getFileData;
getFileData = nullptr;
}
void ftpAccessManager::downloadErrorSlot(QNetworkReply::NetworkError errorCode)
{
qDebug() << "downloadErrorSlot" << errorCode;
getNetReply->deleteLater();
delete getFileData;
getFileData = nullptr;
emit netErrorSignal(errorCode);
}
/* 演示deleteLater什么时候生效 */
bool ftpAccessManager::event(QEvent *event)
{
qDebug()<<"event" << event->isAccepted();
return true;
}
- widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QMessageBox>
#include <QFileDialog>
#include "ftpAccessManager.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_pushButtonDownload_clicked();
void on_pushButtonUpload_clicked();
void uploadProgressSlot(qint64, qint64);
void downloadProgressSlot(qint64, qint64);
void netErrorSlot(QNetworkReply::NetworkError);
private:
bool getHostInfoFromUi();
private:
Ui::Widget *ui;
ftpAccessManager mFtpAccMgr;
QString ftpServerHost;
qint16 ftpServerPort;
QString ftpServerUsr;
QString ftpServerPasswd;
};
#endif // WIDGET_H
- widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
connect(&mFtpAccMgr, SIGNAL(uploadProgressSignal(qint64,qint64)),
this, SLOT(uploadProgressSlot(qint64,qint64)));
connect(&mFtpAccMgr, SIGNAL(downloadProgressSignal(qint64,qint64)),
this, SLOT(downloadProgressSlot(qint64,qint64)));
connect(&mFtpAccMgr, SIGNAL(netErrorSignal(QNetworkReply::NetworkError)),
this, SLOT(netErrorSlot(QNetworkReply::NetworkError)));
}
Widget::~Widget()
{
delete ui;
}
bool Widget::getHostInfoFromUi()
{
ftpServerHost = ui->lineEditHostAddress->text();
ftpServerPort = ui->lineEditPort->text().toInt();
ftpServerUsr = ui->lineEditUsrName->text();
ftpServerPasswd = ui->lineEditPassWord->text();
if(ftpServerHost.isEmpty() || ftpServerUsr.isEmpty()
|| ftpServerPasswd.isEmpty())
{
QMessageBox::warning(this, "Host Error", "Please input Host Info:Host Usr PWD");
return false;
}
return true;
}
void Widget::on_pushButtonUpload_clicked()
{
if(! getHostInfoFromUi())
return;
mFtpAccMgr.setHostPortInfo(ftpServerHost, ftpServerPort);
mFtpAccMgr.setUserPwdInfo(ftpServerUsr, ftpServerPasswd);
QString clientFile = QFileDialog::getOpenFileName(this,
QString::fromLocal8Bit("选择需要上传的文件"));
if(clientFile.isEmpty())
{
QMessageBox::warning(this, "upload error", "please select One file");
return;
}
QString serverPath = ui->lineEditUploadPath->text();
if(serverPath.isEmpty())
{
QMessageBox::warning(this, "upload error", "please select correct ftp path");
return;
}
mFtpAccMgr.putFileFromClent(clientFile, serverPath);
}
void Widget::on_pushButtonDownload_clicked()
{
if(! getHostInfoFromUi())
return;
QString downloadPath = ui->lineEditDownloadPath->text();
mFtpAccMgr.setHostPortInfo(ftpServerHost, ftpServerPort);
mFtpAccMgr.setUserPwdInfo(ftpServerUsr, ftpServerPasswd);
QString targetFileName = QFileInfo(downloadPath).fileName();
if(targetFileName.isEmpty())
return;
QString saveNameWithPath = QFileDialog::getSaveFileName(this,
QString::fromLocal8Bit("保存为..."),
targetFileName,
"");
mFtpAccMgr.getFileFromServer(saveNameWithPath, downloadPath);
}
void Widget::uploadProgressSlot(qint64 bytesReceived, qint64 bytesTotal)
{
// ui->progressBarUpload->setMaximum(bytesTotal);
// ui->progressBarUpload->setValue(bytesReceived);
//解决文件大小大于0x7fffffff情况 > int
qint32 fileSize = (qint32)(bytesReceived / (1024 * 1024)); //换成M单位
if(bytesReceived > 0 && fileSize == 0)
{
fileSize = 1;
}
qint32 totalSize = (qint32)(bytesTotal / (1024 * 1024));
if(bytesTotal > 0 && totalSize == 0)
{
totalSize = 1;
}
ui->progressBarUpload->setMaximum(totalSize); //这句话一定要写在setValue前面
ui->progressBarUpload->setValue(fileSize);
}
void Widget::downloadProgressSlot(qint64 bytesReceived, qint64 bytesTotal)
{
// ui->progressBarDownload->setMaximum(bytesTotal);
// ui->progressBarDownload->setValue(bytesReceived);
qint32 fileSize = (qint32)(bytesReceived / (1024 * 1024));
if(bytesReceived > 0 && fileSize == 0)
{
fileSize = 1;
}
qint32 totalSize = (qint32)(bytesTotal / (1024 * 1024));
if(bytesTotal > 0 && totalSize == 0)
{
totalSize = 1;
}
ui->progressBarDownload->setMaximum(totalSize);
ui->progressBarDownload->setValue(fileSize);
}
void Widget::netErrorSlot(QNetworkReply::NetworkError errorCode)
{
//显示在状态栏里
}