QT中使用libcurl库实现到ftp服务器的上传和下载

发布于:2025-06-10 ⋅ 阅读:(18) ⋅ 点赞:(0)

最近新开了一个项目需要用到ftp服务器,之前没接触过这方面,刚开始觉得很简单,一两天应该就可以搞好,谁知道花了我接近一周时间,此时此刻!!!我实现了,赶紧记录一下哈哈

  1. libcurl库的下载,网上很多都需要下载之后编译,我嫌浪费时间就找了一个版本的用,(夸克网盘)
    链接:https://pan.quark.cn/s/64d2067a6657
    提取码:pVU5
  2. 首先就是加入库和include包含文件,这些不赘述了,有问题可以问我,看到会回的,知无不言;
  3. 先说上传本地文件到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,暂时分享到这,代码还是有点冗余,需要优化,后续会在读取和写入使用定时器确保不卡顿,还要完成实时更新服务器上的文件以及下载,同步问题都需要解决,但好在第一步终于踏出去了!