When working with C programs that need to interact with the internet, cURL is a powerful and flexible library to consider. Whether you’re fetching data from a remote server or uploading files, cURL provides a wide range of functionality. In this blog post, we’ll focus on how to download a file to a specific directory using cURL in a C program.
Why Use cURL in C?
cURL (Client URL) is a command-line tool and library for transferring data with URLs. It supports various protocols like HTTP, HTTPS, FTP, and more. In C programming, cURL is a go-to choice because of its simplicity and powerful capabilities.
Setting Up cURL in Your C Program
Before diving into code, make sure you have cURL installed and the necessary development libraries. On Ubuntu, you can install these with:
sudo apt-get install libcurl4-openssl-dev
Now, let’s move on to writing the code.
Code Example: Downloading a File Using cURL
Below is a simple C program that uses cURL to download a file from a specified URL and saves it to a directory.
$ vim download_file_to_directory.c
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/statvfs.h>
#include <sys/mount.h>
#include <openssl/md5.h>
#include <curl/curl.h>
struct fileptr {
const char *filename;
FILE *stream;
};
static size_t file_fwrite(void *buffer, size_t size, size_t nmemb, void *stream) {
struct fileptr *out=(struct fileptr *)stream;
if(out && !out->stream) {
/* open file for writing */
out->stream = fopen(out->filename, "wb");
if(!out->stream)
return -1; /* failure, can't open file to write */
}
return fwrite(buffer, size, nmemb, out->stream);
}
int curl_download_file_to_directory(char *url, char *filename) {
CURL *curl = NULL;
CURLcode res;
struct fileptr file;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl) {
printf("url is %s, filename:%s\n", url, filename);
curl_easy_setopt(curl, CURLOPT_URL, url);
file.filename = filename;
file.stream = NULL;
/* Define our callback to get called when there's data to be written */
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, file_fwrite);
/* Set a pointer to our struct to pass to the callback */
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &file);
/* Switch on full protocol/debug output */
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
if(CURLE_OK != res) {
/* we failed */
fprintf(stderr, "curl told us %d\n", res);
if(file.stream)
fclose(file.stream); /* close the local file */
return -1;
}
}
if(file.stream)
fclose(file.stream); /* close the local file */
curl_global_cleanup();
return 0;
}
int main(int argc, char **argv) {
curl_download_file_to_directory("https://curl.haxx.se/download/curl-7.69.1.tar.gz","/tmp/curl-7.69.1.tar.gz");
return 0;
}
Explanation of the Code
- write_data Function: This function handles writing data to a file. It is passed as a callback to
curl_easy_setopt
to manage how cURL writes the incoming data. - download_file Function: This is the main function that performs the download. It initializes cURL, sets the URL, and specifies the file to which the data should be written. If any errors occur during the process, they are handled gracefully.
- main Function: This is where the URL and output path are defined. The
download_file
function is called with these parameters.
$ gcc -o download_file_to_directory download_file_to_directory.c -lcurl
$ ./download_file_to_directory
url is https://curl.haxx.se/download/curl-7.69.1.tar.gz, filename:/tmp/curl-7.69.1.tar.gz
* Trying 151.101.2.49...
* TCP_NODELAY set
* Connected to curl.haxx.se (151.101.2.49) port 443 (#0)
* found 133 certificates in /etc/ssl/certs/ca-certificates.crt
* found 402 certificates in /etc/ssl/certs
* ALPN, offering http/1.1
* SSL connection using TLS1.2 / ECDHE_RSA_AES_128_GCM_SHA256
* server certificate verification OK
* server certificate status verification SKIPPED
* common name: j2.shared.global.fastly.net (matched)
* server certificate expiration date OK
* server certificate activation date OK
* certificate public key: RSA
* certificate version: #3
* subject: C=US,ST=California,L=San Francisco,O=Fastly\, Inc.,CN=j2.shared.global.fastly.net
* start date: Thu, 12 Mar 2020 19:08:12 GMT
* expire date: Sun, 07 Mar 2021 17:48:32 GMT
* issuer: C=BE,O=GlobalSign nv-sa,CN=GlobalSign CloudSSL CA - SHA256 - G3
* compression: NULL
* ALPN, server accepted to use http/1.1
> GET /download/curl-7.69.1.tar.gz HTTP/1.1
Host: curl.haxx.se
Accept: */*
< HTTP/1.1 200 OK
< Connection: keep-alive
< Content-Length: 4131634
< Server: Apache
< X-Frame-Options: SAMEORIGIN
< Last-Modified: Wed, 11 Mar 2020 06:39:38 GMT
< ETag: "3f0b32-5a08e8020433f"
< Cache-Control: max-age=31536000
< Expires: Thu, 11 Mar 2021 06:42:23 GMT
< X-Content-Type-Options: nosniff
< Content-Security-Policy: default-src 'self' www.fastly-insights.com; style-src 'unsafe-inline' 'self'
< Strict-Transport-Security: max-age=31536000; includeSubDomains;
< Content-Type: application/x-gzip
< Via: 1.1 varnish
< Accept-Ranges: bytes
< Date: Fri, 10 Apr 2020 17:00:45 GMT
< Via: 1.1 varnish
< Age: 2629102
< X-Served-By: cache-bma1630-BMA, cache-bom18223-BOM
< X-Cache: HIT, HIT
< X-Cache-Hits: 1, 1
< X-Timer: S1586538045.387868,VS0,VE8
<
* Connection #0 to host curl.haxx.se left intact
Once the program execution is completed, i.e. download from remote location to local file is done, we will the file got downloaded at /tmp as,
$ file /tmp/curl-7.69.1.tar.gz
/tmp/curl-7.69.1.tar.gz: gzip compressed data, max compression, from Unix
Key Points to Remember
- Error Handling: Always check for errors, especially when dealing with file operations and network requests.
- Flexibility: You can easily modify this code to download different file types, save them to different directories, or handle multiple downloads concurrently.