QT中使用libcurl库实现到ftp服务器的上传和下载
最近新开了一个项目需要用到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); // 初始化 libcurlcurl = 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;// 设置 URLcurl_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); // 初始化 libcurlcurl = 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,暂时分享到这,代码还是有点冗余,需要优化,后续会在读取和写入使用定时器确保不卡顿,还要完成实时更新服务器上的文件以及下载,同步问题都需要解决,但好在第一步终于踏出去了!