概要
源于嵌入式项目需要通过FTP上传/下载服务器的文件,使用QFtp实现,下附一个单例模式的共享库例子,有需要的伙伴可以留意一下,修修改改就能用。
创作不易,转载请注明作者,谢谢。
第一步,学习QFtp的使用函数
查阅qftp.h头文件,常用的函数都在此文件中可以看到,如下代码块所示:
int setProxy(const QString &host, quint16 port);
int connectToHost(const QString &host, quint16 port=21);
int login(const QString &user = QString(), const QString &password = QString());
int close();
int setTransferMode(TransferMode mode);
int list(const QString &dir = QString());
int cd(const QString &dir);
int get(const QString &file, QIODevice *dev=0, TransferType type = Binary);
int put(const QByteArray &data, const QString &file, TransferType type = Binary);
int put(QIODevice *dev, const QString &file, TransferType type = Binary);
int remove(const QString &file);
int mkdir(const QString &dir);
int rmdir(const QString &dir);
int rename(const QString &oldname, const QString &newname);
上述的API接口简洁明了,此处不再赘述接口功能。
第二步,废话少说,直接上代码
.pro文件
QT += core network
QT -= gui
TARGET = FtpManager
TEMPLATE = lib
DEFINES += FTP_LIBRARY
DEFINES += QT_DEPRECATED_WARNINGS
SOURCES += \
FtpManager.cpp\
qftp.cpp \
qurlinfo.cpp
HEADERS += \
FtpManager.h \
ftp_global.h \
qftp.h \
qurlinfo.h
unix {
target.path = /usr/lib
INSTALLS += target
}
CONFIG += plugin
CONFIG += debug_and_release
CONFIG(debug,debug|release){
BuildType = debug
}else{
BuildType = release
}
unix: DESTDIR = $$PWD/../../Bin
win32: DESTDIR = $$PWD/../../Bin/$$BuildType
FtpManager.h文件
#ifndef FTPMANAGER_H
#define FTPMANAGER_H
#include "ftp_global.h"
#include <QObject>
#include <QCoreApplication>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include "qftp.h"
#include "qurlinfo.h"
struct sFtpData;
class FTPSHARED_EXPORT cFtpManager : public QObject
{
Q_OBJECT
public:
explicit cFtpManager(QObject *parent = nullptr);
~cFtpManager();
private:
void downloadFile(const QString &fileName);
private:
sFtpData *d;
static cFtpManager *mFtpManager;
public:
static cFtpManager *getInstance();
void connectFtp();
void disconnectFtp();
void uploadFile(const QString &fileName);
void renameFile(const QString &oldName, const QString &newName);
void deleteFile(const QString &fileName);
bool isConnected() const;
QFtp::State ftpState() const;
signals:
void sigUpdateDataTransferProgress(qint64,qint64);
void sigUploaded(bool);
void sigDelete(bool);
void sigRename(bool);
private slots:
void ftpCommandFinished(int cmdId, bool error);
void ftpStateChanged(int state);
void addToList(const QUrlInfo &urlInfo);
void updateDataTransferProgress(qint64 readBytes, qint64 totalBytes);
};
#endif // FTPMANAGER_H
FtpManager.cpp文件
#include "FtpManager.h"
#include <QFile>
#include <QDir>
#include <QDebug>
#include <QMutex>
#include <QDirIterator>
#include <QTimer>
#include <QTime>
#define Ftp_Ip QString("192.168.11.88")
#define Ftp_Port 21
#define Ftp_Username QString("Administrator")
#define Ftp_Password QString("1")
struct sFtpData
{
QFile *downloadFile = nullptr;
QFile *uploadFile = nullptr;
QFtp *ftp = nullptr;
QTimer *timer = nullptr;
QFtp::State mState;
};
cFtpManager *cFtpManager::mFtpManager = nullptr;
cFtpManager::cFtpManager(QObject *parent) : QObject(parent)
{
d = new sFtpData;
d->timer = new QTimer(this);
connect(d->timer,&QTimer::timeout,this,[=]()
{
if(!d->ftp)
return;
if(isConnected()))
downloadFile("timer.txt"); // 间隔20s下载一次文件,保持长连接
});
d->timer->start(20000);
}
cFtpManager::~cFtpManager()
{
disconnectFtp();
delete d;
}
cFtpManager *cFtpManager::getInstance()
{
if(mFtpManager == nullptr)
{
static QMutex mutex;
mutex.lock();
mFtpManager = new cFtpManager;
mutex.unlock();
}
return mFtpManager;
}
void cFtpManager::uploadFile(const QString &fileName)
{
if(fileName.isEmpty())
return;
if(!isConnected())
{
emit sigUploaded(false);
return;
}
QString localPath = QCoreApplication::applicationDirPath() + fileName;
d->uploadFile = new QFile(localPath );
if(d->uploadFile->open(QIODevice::ReadOnly | QIODevice::Text))
{
d->ftp->put(d->uploadFile,fileName);
}
}
void cFtpManager::renameFile(const QString &oldName, const QString &newName)
{
if(oldName.isEmpty() || newName.isEmpty())
return;
if(isConnected())
d->ftp->rename(oldName,newName);
else
emit sigRename(false);
}
void cFtpManager::deleteFile(const QString &fileName)
{
if(fileName.isEmpty())
return;
if(isConnected())
d->ftp->remove(fileName);
else
emit sigDelete(false);
}
bool cFtpManager::isConnected() const
{
if(ftpState() == QFtp::Connected || ftpState() == QFtp::LoggedIn)
return true;
else
return false;
}
QFtp::State cFtpManager::ftpState() const
{
return d->mState;
}
void cFtpManager::ftpCommandFinished(int cmdId, bool error)
{
if(!d->ftp)
return;
switch (d->ftp->currentCommand()) {
case QFtp::ConnectToHost:
{
if(error)
disconnectFtp();
}break;
case QFtp::Login:
{
if(error)
return;
}break;
case QFtp::Cd:
{
if(error)
return;
}break;
case QFtp::List:
{
if(error)
return;
}break;
case QFtp::Get:
{
if(d->downloadFile && d->downloadFile->isOpen())
{
d->downloadFile->close();
delete d->downloadFile;
}
}break;
case QFtp::Put:
{
if(error)
emit sigUploaded(false);
else
emit sigUploaded(true);
if(d->uploadFile && d->uploadFile->isOpen())
{
d->uploadFile->close();
delete d->uploadFile;
}
}break;
case QFtp::Rename:
{
if(error)
emit sigRename(true);
else
emit sigRename(false);
}break;
case QFtp::Remove:
{
if(error)
emit sigDelete(true);
else
emit sigDelete(false);
}break;
default:
break;
}
}
void cFtpManager::ftpStateChanged(int state)
{
if(!d->ftp)
return;
d->mState = d->ftp->state();
switch (d->ftp->state()) {
case QFtp::Connecting:
{
qDebug() << "ftp connecting";
}break;
case QFtp::Connected:
{
qDebug() << "ftp connected";
}break;
case QFtp::Unconnected:
{
qDebug() << "ftp Unconnected";
}break;
default:
break;
}
}
void cFtpManager::addToList(const QUrlInfo &urlInfo)
{
if(urlInfo.isFile())
{
// 文件名称通过urlInfo.name()获取;
}
}
void cFtpManager::updateDataTransferProgress(qint64 readBytes, qint64 totalBytes)
{
emit sigUpdateDataTransferProgress(readBytes,totalBytes);
}
void cFtpManager::connectFtp()
{
if(!d->ftp)
{
d->ftp = new QFtp(this);
QObject::connect(d->ftp, SIGNAL(commandFinished(int,bool)),this, SLOT(ftpCommandFinished(int,bool)));
QObject::connect(d->ftp, SIGNAL(stateChanged(int)),this, SLOT(ftpStateChanged(int)));
QObject::connect(d->ftp, SIGNAL(listInfo(QUrlInfo)),this, SLOT(addToList(QUrlInfo)));
QObject::connect(d->ftp, SIGNAL(dataTransferProgress(qint64,qint64)),this, SLOT(updateDataTransferProgress(qint64,qint64)));
}
d->ftp->connectToHost(Ftp_Ip,Ftp_Port);
d->ftp->login(Ftp_Username,Ftp_Password);
}
void cFtpManager::disconnectFtp()
{
if(d->ftp)
{
d->ftp->state();
d->ftp->abort();
d->ftp->deleteLater();
d->ftp = nullptr;
return;
}
}
void cFtpManager::downloadFile(const QString &fileName)
{
QString localPath = QCoreApplication::applicationDirPath() + fileName;
d->downloadFile = new QFile(localPath );
if(d->downloadFile->open(QIODevice::WriteOnly | QIODevice::Text))
{
d->ftp->get(fileName,d->downloadFile);
}
}
关键注意事项!!!
1、保持与FTP服务器的长连接,需要进行一次FTP操作,我这里使用定时器隔20S获取一次timer.txt文件,文件内容为空。
2、QFtp重连的方式,必须先进行disconnect,调用abort()和deletelater(),然后将指针赋值为nullptr。
3、在断开QFtp连接之后,若剩余的操作未结束,返回ftpCommandFinished或ftpStateChanged,注意!!!此时QFtp指针已经被delete,需要对Qftp对象进行判空处理,若为nullptr则return,否则访问空指针导致崩溃。
4、QFtp的文件操作,是异步处理,若需要断开连接,请在创建QFtp的线程进行断开。
5、ftpCommandFinished和ftpStateChanged槽函数触发时,不要使用槽函数的形参,应使用QFtp对象的函数command()和state()来获取当前命令与当前状态。
小结
针对QFtp的操作,就到这里,大部分的FTP操作QFtp都是可以满足的,不过目前Qt官方已经废弃QFtp,使用QNetworkAccessManager来实现。以上例子仅用作分享,有建议可以留言交流,大家一起学习。