最近新开了一个项目需要用到ftp服务器,之前没接触过这方面,刚开始觉得很简单,一两天应该就可以搞好,谁知道花了我接近一周时间,此时此刻!!!我实现了,赶紧记录一下哈哈
- libcurl库的下载,网上很多都需要下载之后编译,我嫌浪费时间就找了一个版本的用,(夸克网盘)
链接:https://pan.quark.cn/s/64d2067a6657
提取码:pVU5 - 首先就是加入库和include包含文件,这些不赘述了,有问题可以问我,看到会回的,知无不言;
- 先说上传本地文件到ftp服务器
//上传
void connectANDUploadFile(const QString& ftpUrl, const QString& username, const QString& password, const QString& localFilePath)
{
CURL* curl;
CURLcode res;
FILE* file = fopen(localFilePath.toStdString().c_str(), "rb"); // 打开本地文件
if (!file)
{
qDebug() << "Error opening file for downloading!";
return;
}
curl_global_init(CURL_GLOBAL_DEFAULT); // 初始化 libcurl
curl = curl_easy_init(); // 创建一个 curl 会话
if (!curl)
{
qDebug() << "Failed to create CURL session";
fclose(file);
curl_global_cleanup();
return;
}
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
// 设置 FTP 服务器地址
curl_easy_setopt(curl, CURLOPT_URL, ftpUrl.toStdString().c_str());
// 设置 FTP 服务器的用户名和密码
curl_easy_setopt(curl, CURLOPT_USERPWD, (username + ":" + password).toStdString().c_str());
// 设置回调函数来处理下载数据
//curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, discardfunc);
// 使用 FTP 的目录列表命令
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "LIST");
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "STOR");//显示创建ftp上不存在的文件
curl_easy_setopt(curl, CURLOPT_READFUNCTION, readfunc);
curl_easy_setopt(curl, CURLOPT_READDATA, file); // 文件指针作为目标
// 设置超时时间(例如 30 秒)
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
// 使用被动模式(通常需要)
curl_easy_setopt(curl, CURLOPT_FTP_USE_EPSV, 1L);
//curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); // 要求服务器返回200 OK才认为成功
// 执行 FTP 连接并上传文件
res = curl_easy_perform(curl);
long Ftp_code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &Ftp_code);
if (Ftp_code != 200) {
qDebug() << "FTP 错误码: " << Ftp_code;
}
// 检查连接和下载是否成功
if (res != CURLE_OK)
{
qDebug() << "FTP connection or download failed: " << curl_easy_strerror(res);
}
else
{
qDebug() << "File downloaded successfully!";
}
// 关闭文件
fclose(file);
// 清理
curl_easy_cleanup(curl);
curl_global_cleanup(); // 清理 libcurl
}
size_t readfunc(void *ptr, size_t size, size_t nmemb, void *stream)
{
FILE *f = (FILE*)stream;
size_t n;
if (ferror(f))
{
return CURL_READFUNC_ABORT;
}
n = fread(ptr, size, nmemb, f) * size;
return n;
}
//调用方法
QString localFilePath = "E:/LocalFTP"; // 本地上传路径,你自己的文件目录
QString ftpUrl = "ftp://你的服务器/upload/"; // FTP 服务器文件路径,upload要确保服务器上有这个文件夹
QString username = "user"; // FTP 服务器用户名
QString password = "118811"; // FTP 服务器密码
QList<QString> UpFileNameList = QFileDialog::getOpenFileNames(this, "OpenFiles", "", "");//因为我要上传多个文件,所以直接用了选择哪些上传哪些
foreach(const QString & FileName, UpFileNameList)
{
QFileInfo FileInfo(FileName);
connectANDUploadFile(ftpUrl+ "/" + FileInfo.fileName(), username, password, localFilePath + "/" + FileInfo.fileName());
}
注意的地方:首先就是设置那个上传CURLOPT_UPLOAD必不可少,其次是LIST命令,网上搜了说是LIST和STOR命令可在远程文件夹下没有上传的文件名时自动创建该文件,所以我加了。
很多时候可能都是由于上传路径的问题导致出错,因为上传路径必须也要有相应的文件名才行,只有文件夹不行
下载:由于也是下载远程文件夹下的很多文件,所以我这里首先要获取到远程文件夹下的文件名,然后再写入数据,实现如下:
//获取ftp服务器目录下的所有文件名并写入list中
size_t writeCallback(void* contents, size_t size, size_t nmemb, void* userp)
{
std::string* data = static_cast<std::string*>(userp);
size_t totalSize = size * nmemb;
data->append(static_cast<char*>(contents), totalSize);
return totalSize;
}
QList<QString> parseFileList(const std::string& rawData)
{
QList<QString> fileList;
std::istringstream stream(rawData);
std::string line;
while (std::getline(stream, line))
{
if (!line.empty())
{
std::istringstream lineStream(line);
std::string segment;
std::string fileName;
while (lineStream >> segment)
{
fileName = segment; // 获取最后一个部分作为文件名
}
fileList.append(QString::fromStdString(fileName)); // 添加文件名到列表
}
}
return fileList;
}
QList<QString> getDirectoryFiles(const QString& url, const QString& username, const QString& password)
{
QList<QString> fileList;
CURL* curl;
CURLcode res;
curl = curl_easy_init();
if (curl)
{
std::string readBuffer;
// 设置 URL
curl_easy_setopt(curl, CURLOPT_URL, url.toStdString().c_str());
// 设置用户名和密码
std::string userPwd = username.toStdString() + ":" + password.toStdString();
curl_easy_setopt(curl, CURLOPT_USERPWD, userPwd.c_str());
// 使用 FTP 的目录列表命令
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "LIST");
// 设置写回调函数
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
// 执行请求
res = curl_easy_perform(curl);
if (res != CURLE_OK)
{
qDebug() << "curl_easy_perform() failed:" << curl_easy_strerror(res);
}
else
{
// 解析返回的目录列表
fileList = parseFileList(readBuffer);
}
// 清理
curl_easy_cleanup(curl);
}
return fileList;
}
//将对应文件名下的数据写入对应文件的回调函数
size_t write_callback(void* ptr, size_t size, size_t nmemb, FILE* stream)
{
size_t written = fwrite(ptr, size, nmemb, stream); // 将数据写入文件
return written;
}
void connectANDdownloadFile(const QString& ftpUrl, const QString& username, const QString& password, const QString& localFilePath)
{
CURL* curl;
CURLcode res;
FILE* file = fopen(localFilePath.toStdString().c_str(), "wb"); // 打开本地文件
if (!file)
{
qDebug() << "Error opening file for downloading!";
return;
}
curl_global_init(CURL_GLOBAL_DEFAULT); // 初始化 libcurl
curl = curl_easy_init(); // 创建一个 curl 会话
if (!curl)
{
qDebug() << "Failed to create CURL session";
fclose(file);
curl_global_cleanup();
return;
}
// 设置 FTP 服务器地址
curl_easy_setopt(curl, CURLOPT_URL, ftpUrl.toStdString().c_str());
// 设置 FTP 服务器的用户名和密码
curl_easy_setopt(curl, CURLOPT_USERPWD, (username + ":" + password).toStdString().c_str());
// 设置回调函数来处理下载数据
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, file); // 文件指针作为目标
// 设置超时时间(例如 30 秒)
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
// 执行 FTP 连接并下载文件
res = curl_easy_perform(curl);
// 检查连接和下载是否成功
if (res != CURLE_OK)
{
qDebug() << "FTP connection or download failed: " << curl_easy_strerror(res);
}
else
{
qDebug() << "File downloaded successfully!";
}
// 关闭文件
fclose(file);
// 清理
curl_easy_cleanup(curl);
curl_global_cleanup(); // 清理 libcurl
}
//调用方法
QString localFilePath = "E:/DownLoadFile/upload/"; // 本地保存路径
QString ftpUrl = "ftp://你的ftp服务器/uplaod/"; // FTP 服务器文件路径
QString username = "user"; // FTP 服务器用户名
QString password = "118811"; // FTP 服务器密码
QList<QString> files = getDirectoryFiles(ftpUrl, username, password);
foreach(const QString & FileName, files)
{
connectANDdownloadFile(ftpUrl+ FileName, username, password, localFilePath + FileName);
}
注意:这里比较麻烦的就是先要获取远程目录下的文件,再根据文件名下载同名文件到本地文件中
OK,暂时分享到这,代码还是有点冗余,需要优化,后续会在读取和写入使用定时器确保不卡顿,还要完成实时更新服务器上的文件以及下载,同步问题都需要解决,但好在第一步终于踏出去了!