首先明确一点:实现ftp断点续传,需要服务器支持,我使用的是CentOS 下的vsftpd作为ftp服务器。
FTP连接一般是有两个连接,一种连接时客户端C与服务器端S传送命令,一种连接是用于数据的传输。而FTP连接支持两种模式
Port模式(主动模式):涉及到的端口号有21和20,当客户端C向服务器端S通过端口21发送请求链接时,服务器端接收连接,并打开一条命令链路。当客户端需要传输数据时,便会通过命令链路向服务器端发送PORT命令请求:我打开了xxx端口,你连接我吧。服务器端接受请求,通过端口号为21向客户端xxx端口建立一条数据传输链路发送数据。
passiv模式(被动模式):客户端C向服务器端(端口21)发送请求连接,服务器端接收并打开一条命令链路。当客户端需要传输数据时,会通过命令链路像服务器端发送passiv命令:我打开了XXX端口,你连接我吧,服务器端接受请求,从端口1024-5000中随机选择一个端口与客户端建立链接,并发送命令:我打开了XXX端口,你过来链接吧,客户端接受后就向该端口发送数据。
由此可知,port模式是客户端打开一个本地端口,等待服务器端进行数据连接,而passiv模式是由服务器打开一个端口,等待客户端进行数据连接。
QFtp具有文件上传以及下载功能,但是对于文件的续传支持上不好,因此 我使用Qt5.2.1编译器,从网络上下载了QFtp的实现,对其进行修改。目前为止,可以实现文件的续传功能。
QFtp默认采用(PASV)被动模式进行文件传输
QFtp续传:http://download.csdn.net/detail/jiezhj/7528025
以下是我封装的ftpManager
//头文件
#ifndef FTPMANAGER_H
#define FTPMANAGER_H
#include <QObject>
#include <QDebug>
#include <QThread>
#include <QFile>
#include <QTcpSocket>
#include "qftp.h"
class FtpManager : public QObject
{
Q_OBJECT
public:
explicit FtpManager(QString _host, QString userName = "", QString passWd = "",qint16 _port = 21,QObject *parent = 0);
~FtpManager();
signals:
void G_getProgressVal(int);
public slots:
void S_commandFinish(int,bool);
void S_upDateProgress(qint64,qint64);
void S_abort();
void S_dloadFile(QString _remoteFile,QString _localFile,bool isRese = false);
void S_uloadFile(QString _localFile,QString _remoteFile,bool isRese = false);
private:
QString m_userName;
QString m_passwd;
QFtp *myFtp;
QFile *m_File;
bool m_IsOpenFile;
};
#endif // FTPMANAGER_H//实现
#include "ftpmanager.h"
class FtpManager;
FtpManager::FtpManager(QString _host, QString userName, QString passWd, qint16 _port, QObject *parent):
QObject(parent),
m_userName(userName),
m_passwd(passWd),
m_File(0),
m_IsOpenFile(false)
{
//构建ftp对象
myFtp = new QFtp(this);
//连接ftp服务器
myFtp->connectToHost(_host,_port);
//进度条显示
connect(myFtp,SIGNAL(dataTransferProgress(qint64,qint64)),
SLOT(S_upDateProgress(qint64,qint64)));
//状态显示
connect(myFtp,SIGNAL(commandFinished(int,bool)),
SLOT(S_commandFinish(int,bool)));
}
FtpManager::~FtpManager()
{
delete myFtp;
}
//停止Ftp动作
void FtpManager::S_abort()
{
myFtp->abort();
}
//下载文件(当isRese==true为续传下载)
void FtpManager::S_dloadFile(QString _remoteFile,QString _localFile,bool isRese)
{
m_File = new QFile(_localFile);
if(!isRese)
{
qDebug() << tr("文件%1的普通下载... ...").arg(_remoteFile);
if(m_File->open(QIODevice::WriteOnly))
{
m_IsOpenFile = true;
myFtp->get(_remoteFile,m_File);
}
else
{
delete m_File;
m_File = NULL;
qDebug() << tr("本地文件%1打开失败!").arg(_localFile);
}
}
else
{
qDebug() << tr("文件%1的续传下载... ...").arg(_remoteFile);
if(m_File->open(QIODevice::Append))
{
m_IsOpenFile = true;
myFtp->rawCommand(tr("REST %1").arg(m_File->size()));
myFtp->m_isConLoad = true; //设置当前现在为续传
myFtp->get(_remoteFile,m_File);
}
else
{
delete m_File;
m_File = NULL;
qDebug() << tr("本地文件%1打开失败!").arg(_localFile);
}
}
}
//上传文件(当isRese==true为续传上传)
void FtpManager::S_uloadFile(QString _localFile,QString _remoteFile,bool isRese)
{
m_File = new QFile(_localFile);
if(m_File->open(QIODevice::ReadOnly))
{
m_IsOpenFile = true;
if(!isRese)
{
qDebug() << tr("文件%1的普通上传... ...").arg(_localFile);
myFtp->put(m_File,_remoteFile); //上传
}
else
{
qDebug() << tr("文件%1的续传... ...").arg(_localFile);
//在QFtp中定义续传方法
myFtp->conPut(m_File,_remoteFile); //续传
}
}
else
{
delete m_File;
m_File = NULL;
qDebug() << tr("本地文件%1打开失败!").arg(_localFile);
}
}
//更新进度条
void FtpManager::S_upDateProgress(qint64 _used, qint64 _total)
{
int tmpVal = _used / (double)_total * 100;
emit G_getProgressVal(tmpVal);
}
//ftp服务提示信息
void FtpManager::S_commandFinish(int tmp, bool en)
{
Q_UNUSED(tmp);
if(myFtp->currentCommand() == QFtp::ConnectToHost){
if(en)
qDebug() << (tr("连接服务器出现错误:%1").arg(myFtp->errorString()));
else
{
qDebug() << (tr("连接到服务器成功"));
myFtp->login(m_userName,m_passwd); //登陆服务器
}
}
if (myFtp->currentCommand() == QFtp::Login){
if(en)
qDebug() << (tr("登录出现错误:%1").arg(myFtp->errorString()));
else
qDebug() << (tr("登录服务器成功"));
}
if (myFtp->currentCommand() == QFtp::Get)
{
if(en)
{
qDebug() << (tr("下载出现错误:%1").arg(myFtp->errorString()));
}
else
{
qDebug() << (tr("已经完成下载"));
m_File->flush();
}
m_File->close();
m_IsOpenFile = false;
delete m_File;
m_File = NULL;
}
else if(myFtp->currentCommand() == QFtp::Put)
{
if(en)
{
qDebug() << (tr("上传出现错误:%1").arg(myFtp->errorString()));
}
else
{
qDebug() << (tr("已经完成文件上传"));
}
m_File->close();
m_IsOpenFile = false;
delete m_File;
m_File = NULL;
}
else if (myFtp->currentCommand() == QFtp::Close)
{
qDebug() << (tr("已经关闭连接"));
if(m_IsOpenFile)
{
m_File->close();
delete m_File;
m_File = NULL;
}
}
}//测试
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
manager = new FtpManager("192.168.136.144","zhj","123456",21,this);
manager2 = new FtpManager("192.168.136.144","zhj","123456",21,this);
connect(manager,SIGNAL(G_getProgressVal(int)),SLOT(S_updateProgess(int)));
connect(manager2,SIGNAL(G_getProgressVal(int)),SLOT(S_updateProgess2(int)));
}
MainWindow::~MainWindow()
{
delete ui;
}
//更新进度条
void MainWindow::S_updateProgess(int _val)
{
ui->progressBar->setValue(_val);
}
//更新进度条
void MainWindow::S_updateProgess2(int _val)
{
ui->progressBar_2->setValue(_val);
}
//普通下载
void MainWindow::on_downloadBn_clicked()
{
manager->S_dloadFile("VisualStdio.Net2005_En.iso","G:/VisualStdio.Net2005_En.iso");
}
//普通上传
void MainWindow::on_uploadBn_clicked()
{
manager->S_uloadFile("D:/QTWorkspace.zip","QTWorkspace.zip");
}
//下载(续传)
void MainWindow::on_downloadBn_2_clicked()
{
manager2->S_dloadFile("VisualStdio.Net2005_En.iso","G:/VisualStdio.Net2005_En.iso",true);
}
//停止
void MainWindow::on_abort_clicked()
{
manager2->S_abort();
}
//上传(续传)
void MainWindow::on_abort_2_clicked()
{
manager2->S_uloadFile("D:/QTWorkspace.zip","QTWorkspace.zip",true);
}
//停止
void MainWindow::on_abort_3_clicked()
{
manager->S_abort();
}
//
//普通文件上传
//普通文件下载
QFtp具有文件上传以及下载功能,
但是对于文件的续传支持上不好,因此 我使用Qt5.2.1编译器,从网络上下载了QFtp的实现,对其进行修改。目前为止,可以实现文件的续传功能。
作者:jhe_zhang