Downloading files and showing the download progress in Python is simple. However, sometimes we would like to do the same thing in a C++ program.
In this blog post, I would like to show how to download files using the LibCurl library and the indicators progress bar library in C++.
We have to install the following dependencies.
$ sudo apt-get update
$ sudo apt-get install build-essential wget libcurl4-openssl-dev ca-certificates cmake
Alternatively, we could also use Docker container created from the following Dockerfile.
FROM ubuntu:20.04
ENV DEBIAN_FRONTEND noninteractive
# Install package dependencies
RUN apt-get update
RUN apt-get install -y --no-install-recommends \
build-essential \
wget \
libcurl4-openssl-dev \
ca-certificates \
cmake
RUN apt-get clean
To display a progress bar for downloading the file, we could use the indicators header-only library. Just download the header file and compile the download application with the header file.
$ wget https://raw.githubusercontent.com/p-ranav/indicators/v2.2/single_include/indicators/indicators.hpp
The simple download application was implemented as follows.
#include <curl/curl.h>
#include <curl/easy.h>
#include <cstdio>
#include <iostream>
#include <stdexcept>
#include <string>
#include "indicators.hpp"
// https://curl.se/libcurl/c/CURLOPT_XFERINFOFUNCTION.html
int download_progress_callback(void* clientp, curl_off_t dltotal,
curl_off_t dlnow, curl_off_t ultotal,
curl_off_t ulnow)
{
indicators::ProgressBar* progress_bar =
static_cast<indicators::ProgressBar*>(clientp);
if (progress_bar->is_completed())
{
;
}
else if (dltotal == 0)
{
progress_bar->set_progress(0);
}
else
{
int percentage =
static_cast<float>(dlnow) / static_cast<float>(dltotal) * 100;
progress_bar->set_progress(percentage);
}
// If your callback function returns CURL_PROGRESSFUNC_CONTINUE it will
// cause libcurl to continue executing the default progress function. return
// CURL_PROGRESSFUNC_CONTINUE;
return 0;
}
int download_progress_default_callback(void* clientp, curl_off_t dltotal,
curl_off_t dlnow, curl_off_t ultotal,
curl_off_t ulnow)
{
return CURL_PROGRESSFUNC_CONTINUE;
}
std::string extract_file_name(const std::string& url)
{
int i = url.size();
for (; i >= 0; i--)
{
if (url[i] == '/')
{
break;
}
}
return url.substr(i + 1, url.size() - 1);
}
bool download(const std::string& url, const std::string& file_path)
{
const std::string file_name = extract_file_name(url);
// Hide cursor
indicators::show_console_cursor(false);
indicators::ProgressBar progress_bar{
indicators::option::BarWidth{30}, indicators::option::Start{" ["},
indicators::option::Fill{"█"}, indicators::option::Lead{"█"},
indicators::option::Remainder{"-"}, indicators::option::End{"]"},
indicators::option::PrefixText{file_name},
// indicators::option::ForegroundColor{indicators::Color::yellow},
indicators::option::ShowElapsedTime{true},
indicators::option::ShowRemainingTime{true},
// indicators::option::FontStyles{
// std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}
};
CURL* curl;
FILE* fp;
CURLcode res;
curl = curl_easy_init();
if (curl)
{
fp = fopen(file_path.c_str(), "wb");
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION,
download_progress_callback);
curl_easy_setopt(curl, CURLOPT_XFERINFODATA,
static_cast<void*>(&progress_bar));
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
// Perform a file transfer synchronously.
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
fclose(fp);
}
// Show cursor
indicators::show_console_cursor(true);
if (res == CURLE_OK)
{
return true;
}
else
{
return false;
}
}
int main(int argc, char* argv[])
{
std::string url;
std::string file_path;
if (argc == 2)
{
url = std::string{argv[1]};
file_path = extract_file_name(url);
}
else if (argc == 3)
{
url = std::string{argv[1]};
file_path = std::string{argv[2]};
}
else
{
throw std::invalid_argument{
"Insufficient arguments. Please provide URL or "
"URL and destination file path."};
}
download(url, file_path);
}
$ g++ curl_download.cpp indicators.hpp -o curl_download -lcurl
We could use the download application to download a common machine learning dataset.
$ ./curl_download https://www.cs.toronto.edu/~kriz/cifar-10-binary.tar.gz
cifar-10-binary.tar.gz [██████████████████████████████] [00m:23s<00m:00s]
Download Files in C++ Using LibCurl and Indicators Progress Bars was originally published by Lei Mao at Lei Mao's Log Book on July 06, 2021.