目录
包含功能为:
1、向Ftp服务器上传文件
2、从Ftp服务器下载特定文件
3、从Ftp某个文件夹里面下载所有文件
4、远程在FTP服务器指定位置创建文件夹
5、查看FTP服务器指定目录内所有文件名
解决了FTP上传/下载文件时,文件名有中文,有特殊字符时无法上传/下载的问题。
写在前面
可以直接运行的,包含上述全部功能的代码已经上传到github上了,下载文件,配置好自己的环境即可直接使用。代码中包含详细注释以及使用方式。
github链接:https://github.com/DylanYh2/FTPClient_libcurl.git
libcurl库windows下编译配置
windows系统编译libcurl库,并在visual studio2019/2022使用(win10,win11通用)
libcurl c++常用操作
libcurl库实现FTP远程文件操作
头文件
#pragma once
#include<iostream>
#include<curl/curl.h>
#include<string>
#include<vector>
using namespace std;
class FtpManage
{
public:
FtpManage();
FtpManage(const string user, const string password, const string id);
~FtpManage();
/*
*@Upload: 向Ftp服务器上传文件
* @localFilePath:所上传的文件(精确到文件名),如"D:/ggbond/123.jpg"
* @remoteDirectory:上传至Ftp的目录,如"/shjr/"
* @remark:将123.jpg上传到Ftp服务器的"/shjr/"目录下,
*/
bool Upload(const char* localFilePath, const char* remoteDirectory);
/*
* @Download:从Ftp服务器下载特定文件
* @remoteFilePath:所下载的文件(精确到文件名)如"/shjr/333.jpg"
* @localDirectory:保存至本地目录,如"D:/ggbond/"
* @remark:将FTp服务器/shjr目录下的333.jpg下载到本地D:/ggbond目录
*/
bool DownloadFile(const char* remoteFilePath, const char* localDirectory);
/*
* @DownloadAllFiles:从Ftp某个文件夹里面下载所有文件
* @remoteFilePath:所下载的文件夹名称,如"/shjr/"
* @localDirectory:保存至本地目录,如"D:/ggbond/"
* @remark:将FTp服务器/shjr/目录里面下载所有文件到本地D:/ggbond/目录
*/
bool DownloadAllFiles(const char* remoteFilePath, const char* localDirectory);
/*
* @Createdir:创建文件夹
* @directoryname:文件夹名称,可包含上级目录名,要以“/”结尾以表示目录
* 比如:"/ggbond/"
*/
bool Createdir(const char* directoryname);
/*
* @GetFilesName:提供获取FTP文件夹内所有文件名的接口
* @filepath:文件夹名称,可包含上级目录名,要以“/”结尾以表示目录
* 比如:"/ggbond/"
*/
const std::vector<std::string>& GetFilesName(const string filepath);
private:
/*
* @SetURL:初始化URL
*/
void SetURL();
/*
* @UrlEncode:把含有特殊符号的文件名进行URL编码
* @value:文件名
* @"kunkunboy## 123"这种形式是无法传入curl的,需要转换一下
*/
std::string UrlEncode(const std::string& value);
/*
* @GEtFileNameFromPath:从路径中提取文件名并返回
* @filePath:文件完整路径
* @"D:/ggbond/123.jpg"->123.jpg
*/
std::string GetFileNameFromPath(const std::string& filePath);
/*
* @GetfilenameFromftp:获得FTP服务器上某个文件夹内所有文件名,如果文件夹内既有文件又有文件夹,则只会显示文件
* @directoryname:文件夹名称,可包含上级目录名,要以“/”结尾以表示目录
*/
bool GetfilenameFromftp(const string directoryname);
private:
std::string Ftp_ip; //Ftp服务器地址
std::string User, Password; //登录用户名及密码
std::string _URL;
std::vector<std::string> fNs; //用于记录全部文件名
CURL* curl;
};
向Ftp服务器上传文件
核心代码为:
/*
*@Upload: 向Ftp服务器上传文件
* @localFilePath:所上传的文件(精确到文件名),如"D:/ggbond/123.jpg"
* @remoteDirectory:上传至Ftp的目录,如"/shjr/"
* @remark:将123.jpg上传到Ftp服务器的"/shjr/"目录下,
*/
bool Upload(const char* localFilePath, const char* remoteDirectory);
{
SetURL();
_URL = Ftp_ip + remoteDirectory+ GetFileNameFromPath(localFilePath);
curl_easy_setopt(curl, CURLOPT_URL, _URL.c_str()); // 设置访问的FTP地址
if (curl)
{
FILE* fp = fopen(localFilePath, "rb");
curl_off_t fsize = 0;
if (fp)
{
fseek(fp, 0, SEEK_END);
fsize = ftell(fp); // 获取文件大小
fseek(fp, 0, SEEK_SET);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); // 指定为上传文件模式
curl_easy_setopt(curl, CURLOPT_READDATA, fp); // 指定上传的文件是哪个
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, fsize);// 指定上传的文件大小
CURLcode res = curl_easy_perform(curl); // 开始上传
if (res != CURLE_OK)
{
std::cout << "localFilePath: " << localFilePath << " upload failed!" << curl_easy_strerror(res) << std::endl;
curl_easy_cleanup(curl);
fclose(fp);
return false;
}
else
{
std::cout << "localFilePath: " << localFilePath << " upload successfully!" << std::endl;
}
fclose(fp);
}
else
{
std::cout << "file: " << localFilePath << " open failed!" << std::endl;
curl_easy_cleanup(curl);
return false;
}
}
curl_easy_cleanup(curl);
return true;
}
从Ftp服务器下载指定文件
核心代码为:
/*
* @Download:从Ftp服务器下载特定文件
* @remoteFilePath:所下载的文件(精确到文件名)如"/shjr/333.jpg"
* @localDirectory:保存至本地目录,如"D:/ggbond/"
* @remark:将FTp服务器/shjr目录下的333.jpg下载到本地D:/ggbond目录
*/
bool FtpManage::DownloadFile(const char* remoteFilePath, const char* localDirectory)
{
SetURL();
_URL = Ftp_ip + UrlEncode(remoteFilePath);
curl_easy_setopt(curl, CURLOPT_URL, _URL.c_str()); // 设置请求的URL
if (curl)
{
FILE* fp = fopen((localDirectory + GetFileNameFromPath(remoteFilePath)).c_str(), "wb");
if (fp)
{
// 设置文件写入地址
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
// 执行任务
CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK)
{
std::cout << "DownLoad file:" << remoteFilePath << " failed! " << curl_easy_strerror(res) << std::endl;
fclose(fp);
curl_easy_cleanup(curl);
return false;
}
else
{
std::cout << "DownLoad file:" << remoteFilePath << " successfully!" << std::endl;
fclose(fp);
}
}
else
{
std::cout << "file open failed!" << std::endl;
curl_easy_cleanup(curl); // 清除curl
return false;
}
}
curl_easy_cleanup(curl);
return true;
}
从Ftp某个文件夹里面下载所有文件
/*
* @DownloadAllFiles:从Ftp某个文件夹里面下载所有文件
* @remoteFilePath:所下载的文件夹名称,如"/shjr/"
* @localDirectory:保存至本地目录,如"D:/ggbond/"
* @remark:将FTp服务器/shjr/目录里面下载所有文件到本地D:/ggbond/目录
*/
bool FtpManage::DownloadAllFiles(const char* remoteFilePath, const char* localDirectory)
{
if (GetfilenameFromftp(remoteFilePath))
{
for (const auto& fns : fNs)
{
std::string filename = remoteFilePath + fns;
bool res = DownloadFile(filename.c_str(), localDirectory);
if (res) continue;
else
{
return false;
}
}
}
return true;
}
远程在FTP上创建文件夹
/*
* @Createdir:创建文件夹
* @directoryname:文件夹名称,可包含上级目录名,要以“/”结尾以表示目录
* 比如:"/ggbond/"
*/
bool FtpManage::Createdir(const char* directoryname)
{
if (directoryname == nullptr)
{
std::cout << "directoryname is NULL!" << std::endl;
return false;
}
SetURL();
_URL = Ftp_ip + UrlEncode(directoryname);
curl_easy_setopt(curl, CURLOPT_URL, _URL.c_str()); // 指定url
curl_easy_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS, 1L); //如果目录不存在则创建一个
CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK)
{
std::cout << "Directory:" << directoryname << "create failed!" << curl_easy_strerror(res) << std::endl;
curl_easy_cleanup(curl); // 清理curl
return false;
}
else
{
std::cout << "Create directory:" << directoryname << " successfully!" << std::endl;
}
curl_easy_cleanup(curl);
return true;
}
获取FTP文件夹内所有文件名
/*
* @GetFilesName:提供获取FTP文件夹内所有文件名的接口
* @filepath:文件夹名称,可包含上级目录名,要以“/”结尾以表示目录
* 比如:"/ggbond/"
*/
//接收消息回调函数
size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* output)
{
size_t totalSize = size * nmemb;
output->append(static_cast<char*>(contents), totalSize);
return totalSize;
}
// 获取ftp某个文件夹内文件名
bool FtpManage::GetfilenameFromftp(const std::string filePath)
{
SetURL();
std::string path = Ftp_ip + UrlEncode(filePath);
std::string fileName; // 文件名列表保存位置
if (curl)
{
curl_easy_setopt(curl, CURLOPT_URL, path.c_str()); // 设置访问URL
curl_easy_setopt(curl, CURLOPT_DIRLISTONLY, 1L); // 设置只返回文件
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &fileName); // 设置只获取文件名列表
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); // 设置get的回调函数
if (curl_easy_perform(curl) == CURLE_OK) //执行任务
{
fNs.clear();
size_t startPos = 0, endPos = 0;
while ((endPos = fileName.find('\n', startPos)) != std::string::npos )
{
std::string name = fileName.substr(startPos, endPos - startPos-1);
fNs.emplace_back(name);
startPos = endPos + 1;
}
}
}
else
{
return false;
}
curl_easy_cleanup(curl);
return true;
}
// 获取FTP文件夹内所有文件名的接口函数
const std::vector<std::string>& FtpManage::GetFilesName(const string filepath)
{
if (GetfilenameFromftp(filepath))
{
return fNs;
}
else
{
return std::vector<std::string>();
}
}
解决文件名中空格、特殊字符无法出现错误
// 对文件名进行URL编码,否则文件名中有空格,特殊符合(#^...)会出现URL错误
std::string FtpManage::UrlEncode(const std::string &value)
{
CURL* curl = curl_easy_init();
if (!curl) return "";
char* output = curl_easy_escape(curl, value.c_str(), value.length());
if (output) {
std::string encoded(output);
curl_free(output);
curl_easy_cleanup(curl);
return encoded;
}
curl_easy_cleanup(curl);
return "";
}
其他相关函数
// 把带有路径的文件转换为文件名 D:/ggbond/123.jpg->123.jpg
std::string FtpManage::GetFileNameFromPath(const std::string& filePath)
{
if (filePath == "") return filePath;
//去除路径最后的空格
string retPath;
size_t lastBlank = filePath.find_last_not_of(' ');
if (lastBlank != std::string::npos)
{
retPath = filePath.substr(0, lastBlank+1);
}
else
{
return "";
}
size_t lastSlashPos = retPath.find_last_of("/\\");
if (lastSlashPos != std::string::npos)
{
return retPath.substr(lastSlashPos + 1);
}
else
{
// 如果没有找到斜杠或反斜杠,整个路径就是文件名
return retPath;
}
}