第13章网络,Page672~674页,带进度的FTP下载的易错点及程序书写顺序

发布时间:2024年01月21日

网络篇,书上672~674页,带进度的FTP下载的的实例程序,写了两遍,才写成功,而且第二遍写的时候,对易错的地方有所感悟,写篇心得记下来。
首先上代码:

#include <curl/curl.h>
#include <iostream>
#include <fstream>
#include <sstream> //stringstream

using namespace std;

int to_size(char* data, size_t size, size_t nmemb, void* userdata)
{
    int result_code = 0;

    string s(data, size*nmemb);
    stringstream ss(s);
    ss >> result_code;

    if(!ss.bad() && result_code == 213)
    {
        int* pcode = static_cast <int*> (userdata);
        ss >> *pcode;
    }

    return nmemb*size;
}

int to_stream(char* data, size_t size, size_t nmemb, void* userdata)
{
    ostream& os = *static_cast <ostream*> (userdata);
    std::string line(data, size*nmemb);
    os << line;
    return size*nmemb;
}

//当需要通知进度时,回调
int down_progress(void*
                  , curl_off_t dltotal, curl_off_t dlnow
                  , curl_off_t ultotal, curl_off_t ulnow)
{
    if(dltotal == 0)
        return 0;
    int count = (dlnow * 1.0 / dltotal) * 50; //确定需要画多少个等号
    cout << (dlnow * 100 / dltotal) << "%"; //确定百分比

    for(int i = 0; i < count; ++i)
    {
        cout << '=';
    }

    cout << endl;
    return 0;
}

//取FTP服务器指定文件的大小
int get_server_file_size(string const& server_url
                         , string const& username
                         , string const& password
                         , string const& pathfile)
{
    CURL* handle = curl_easy_init();

    curl_easy_setopt(handle, CURLOPT_URL, server_url.c_str());
    //username和password也需要C形式的字符串
    curl_easy_setopt(handle, CURLOPT_USERNAME, username.c_str());
    curl_easy_setopt(handle, CURLOPT_PASSWORD, password.c_str());

    string cmd = "SIZE " + pathfile; //SIZE后面要有个分号
    curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, cmd.c_str());

    int filesize = 0;
    curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, to_size);
    curl_easy_setopt(handle, CURLOPT_HEADERDATA, static_cast <void*> (&filesize));
    curl_easy_perform(handle);
    cout << "filesize = " << filesize << endl;

    curl_easy_cleanup(handle);
    return filesize;
}

int main()
{
    curl_global_init(CURL_GLOBAL_DEFAULT);
    CURL* handle = curl_easy_init();

    ofstream ofs("a.zip", ios_base::out | ios_base::binary);
    if(!ofs)
    {
        cerr << "无法打开本地文件a.zip。" << endl;
        return -1;
    }

    string server_url = "ftp://127.0.0.1:21/";
    string pathfile = "fengjie/meili/2.zip";
    string username = "d2school";
    string password = "123456";

    //取服务端指定文件大小
    size_t file_size = get_server_file_size(server_url, username, password, pathfile);
    //告诉libcurl待下载文件的总大小
    curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, static_cast <curl_off_t> (file_size));
    //开启进度通知
    curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 0L);
    curl_easy_setopt(handle, CURLOPT_XFERINFOFUNCTION, down_progress);

    //设置如何处理下载的数据
    string url = server_url + pathfile;
    curl_easy_setopt(handle, CURLOPT_URL, url.c_str());
//    //username和password也需要C形式的字符串
    curl_easy_setopt(handle, CURLOPT_USERNAME, username.c_str());
    curl_easy_setopt(handle, CURLOPT_PASSWORD, password.c_str());

    //本次下载采用直接定位到文件的方式,类似于http协议的下载,不需要使用ftp命令: RETR 文件名
    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, to_stream);
    curl_easy_setopt(handle, CURLOPT_WRITEDATA, static_cast <void*> (&ofs));

    curl_easy_perform(handle);//启动下载

    ofs.close();//关闭流

    curl_easy_cleanup(handle);
    curl_global_cleanup();

    return 0;
}


易错点分析:

65行,要注意SIZE后面要有个分号
62,63, 107,108行,要注意username和password不要忘了使用c_str()转化成“C”形式的字符串
110行,本次下载采用直接定位到文件的方式,类似于http协议的下载,不需要使用ftp命令: RETR 文件名
116行,关闭流,这个容易遗忘。不过这一行即使遗忘了,应该也不会有问题,因为ofs是栈变量,会自动回收内存。
写的过程要注意:不要一口气把整个程序写完,否则出了错,会花费老大劲去寻找错误。
首先,main函数中,写到96行,要测试一下,看看能不能得到文件的大小,如果不能得到,则停下来,排查错误。把错误解决完,再继续往下写。

接下来,先写104-112行的内容,看看能否把文件下载下来,若不能,则同样排查错误。把错误解决完,再继续写98-101行的内容,把下载进度通知加上。

文章来源:https://blog.csdn.net/yanzhenxi/article/details/135724753
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。