网络篇,书上672~674页,带进度的FTP下载的的实例程序,写了两遍,才写成功,而且第二遍写的时候,对易错的地方有所感悟,写篇心得记下来。
首先上代码:
#include <curl/curl.h>
#include <iostream>
#include <fstream>
#include <sstream> //stringstreamusing 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行的内容,把下载进度通知加上。