一点多发FTP客户端设计

时间:2022-07-23
本文章向大家介绍一点多发FTP客户端设计,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

项目背景

FileZilla FTP Client这款软件只能手动上传文件到FTP,貌似我还没有找到定时扫描某个本地目录,然后执行定时上传的功能。最近遇到一个问题就是:在服务器上部署到很多个FTP客户端定时程序,每个FTP客户端exe可执行程序功能都是类似的,都是将本地服务器中的某个文件夹下的符合文件规则(如*.json,*.xml)文件通过FTP上传到指定FTP服务器上面的某个目录下。但是开的程序太多了,这样如果需要上传多个比如说雷达文件到多个FTP服务器上时,就会开启多个FTP推送客户端程序,这样服务器上面就产生多个exe可执行程序。 如下图所示:

这样看起来很不好,而且很难区分对应的是哪个FTP程序,为了解决这个问题,最好将FTP推送客户端做成一点多发的形式。

FTP File Upload Version1.0版

关于只上传一个FTP服务器地址的MFC程序可以参考我之前写的博客:VC++ libcurl FTP上传客户端程序

源代码我已经上传到Github和Gitee上面了,使用的C/C++库有libcurlboostpugixml

FTPUpload-Github地址 FTPUpload-Gitee地址 FTPUpload是一款基于MFC的FTP推送客户端程序,使用了libcurl实现FTP推送,使用pugixml实现xml配置文件的读写,还使用了Boost库用于目录规则的转换(涉及到日期的)。程序执行流程图如下图所示:

程序如下图所示:

FTP File Upload Version2.0版

在第一个版本的基础上做了另外一个版本,增加了Windows服务程序

FTP File Upload Version3.0版

功能描述:

  • FTP多数据源多FTP目标源推送客户端的实现(支持一点多发):
  • 1.从数据库中获取任务信息,分别获取需要上传的本地文件源的信息(包括需要扫描的本地目录、目录规则、文件规则等)以及
  • FTP目的地信息(包括FTP的远程URL地址、FTP用户名、密码、远程的目录地址等)
  • 2、遍历任务列表
  • 3、针对每个任务,遍历文件源(可能有多个)
  • 4、对于每个文件源,遍历需要上传的FTP地址(可能有多个)
  • 5、针对每个文件源以及某个FTP目的信息的数据上传通道,创建一个FTP上传线程
  • 6、对于单个的FTP上传线程,执行FTP上传,并将上传成功或失败的写入到数据库中,以便多次重复传输(这里要注意剔除哪些在本地目录已经不存在但是在数据库中还有上传记录的文件上传记录信息)

程序执行流程图

程序流程图如下图所示:

数据库设计

我使用的数据库是Sqlite3书库,选择它主要是因为它轻便而且无需提前安装。主要涉及到三张表:t_task任务表、t_source数据源表、t_dest即FTP目的地表,后续根据需要最好再加一张记录已经上传的文件列表信息,以防止重复上传相同的文件。这三张表的结构如下图示:

  • 表1 t_task
  • 表2 t_source
  • 表3 t_dest

当然,可以选择Sqlite3数据库作为数据源,也可以使用xml配置文件作为数据源。

为了方便我使用的是Sqlite3数据库,简单方便,使用了CodeProject上面一个关于Windows系统下SQLite的C++封装类CppSQLite - C++ Wrapper for SQLite,基于此类简单封装了一下基于Sqlite3的操作。核心代码如下:

// FtpUploadMulti.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include "TaskDB.h"
#include "FTPUpload.h"

/**
 * 功能描述:
 * FTP多数据源多FTP目标源推送客户端的实现(支持一点多发):
 * 1.从数据库中获取任务信息,分别获取需要上传的本地文件源的信息(包括需要扫描的本地目录、目录规则、文件规则等)以及
 * FTP目的地信息(包括FTP的远程URL地址、FTP用户名、密码、远程的目录地址等)
 * 2、遍历任务列表
 * 3、针对每个任务,遍历文件源(可能有多个)
 * 4、对于每个文件源,遍历需要上传的FTP地址(可能有多个)
 * 5、针对每个文件源以及某个FTP目的信息的数据上传通道,创建一个FTP上传线程
 * 6、对于单个的FTP上传线程,执行FTP上传,并将上传成功或失败的写入到数据库中,以便多次重复传输(这里要注意剔除哪些在本地目录已经不存在但是在数据库中还有上传记录的文件上传记录信息)
 */
int main()
{
	// 创建数据库类对象
	signal_log lg;
	TaskDB taskDB(lg);

	TaskItemArray taskList;
	taskDB.init();
	// 获取任务列表
	taskList = taskDB.getTaskList();
	// 遍历任务列表
	// 遍历任务列表,获取文件源和FTP目的地
	for (auto taskIter = taskList.begin(); taskIter != taskList.end(); taskIter++)
	{
		int srcId = taskIter->sourceId;
		int destId = taskIter->destId;
		SourceItemArray srcList;
		DestItemArray destList;
		// 获取源文件列表
		srcList = taskDB.getSourceInfoBySId(srcId);
		// 获取FTP目的地信息
		destList = taskDB.getDestInfoByDId(destId);
		for (auto srcIter = srcList.begin(); srcIter != srcList.end(); srcIter++)
		{
			SourceItem srcItem = *srcIter;
			for (auto destIter = destList.begin(); destIter != destList.end(); destIter++)
			{
				DestItem destItem = *destIter;
				// 执行FTP上传任务(此处最好做成一个线程,针对每个任务或者FTP通道开辟一个FTP上传线程)
				FTPUpload ftp;
				// 设置FTP本地目录、扫描的目录规则、文件规则
				ftp.set_local_path(srcItem.localPath.c_str(), srcItem.folderRule.c_str(), srcItem.fileRule.c_str());
				// 设置FTP服务器的远程URL地址、FTP用户名和密码
				ftp.set_remote_path(destItem.ftpUrl.c_str(), destItem.ftpUser.c_str(), destItem.ftpPwd.c_str());
				// 执行FTP上传
				ftp.upload();
			}
		}
	}
   

	return 0;
}