I am using the following code to download files from the internet:
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
size_t written;
written = fwrite(ptr, size, nmemb, stream);
return written;
}
int main(int argc, char** argv)
{
FILE *downloaded_file;
if ( (downloaded_file = fopen (download_path , "w" ) ) != NULL )
{
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl)
{
curl_easy_setopt(curl, CURLOPT_URL, "www.asd.com/files/file_to_download.rar");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, downloaded_file);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if (res == CURLE_OK)
{
printf("Download complete!\n");
}
}
fclose(downloaded_file);
}
}
How can I measure the current download speed (e.g. every second) and the remaining time to complete the download?
You can use CURLOPT_PROGRESSFUNCTION. curl will pass 5 arguments to your callback function, clientp, dltotal, dlnow, ultotal, and ulnow. clientp is a pointer you provide with CURLOPT_PROGRESSDATA. The total parameters are the total amounts that need to be downloaded; the now ones are the amounts so far. Unknown values are 0.
To use this, you must set CURLOPT_NOPROGRESS to 0.
CURLOPT_PROGRESSFUNCTION has been deprecated since v7.32.0, instead you can use CURLOPT_XFERINFOFUNCTION, the usage and callback structure (all the arguments dltotal, dlnow ...etc) are almost the same as CURLOPT_PROGRESSFUNCTION
Related
I am downloading file quite commonly with curl. However, the server does a tricky thing: it return non-200 code and still sends some data. The problem is that I have the HTTP code after the data are written, but I do not want to write anything if it is non-200. Does anyone know a way to do that without storing data on disk or memory?
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteHandler);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, ptr);
res = curl_easy_perform(curl);
if (res == CURLE_OK) {
return 0;
}
long response_code;
curl_easy_getinfo(curl_.get(), CURLINFO_RESPONSE_CODE, &response_code);
if (response_code != 200) {
return 0;
}
size_t curlWriteHandler(char* chars, size_t size, size_t nmemb, void* userp) {
// write to file
return size * nmemb;
}
Setting CURLOPT_FAILONERROR should do it for 4xx and 5xx errors.
When this option is used and an error is detected, it will cause the connection to get closed and CURLE_HTTP_RETURNED_ERROR is returned.
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
Closing connection is not good for me, it is important to reuse one. Can you think about anything else?
Unfortunately I can't find a way to make CURLOPT_FAILONERROR not close the connection.
The other option is to make the write function aware of the response. Unfortunately the curl handle is not passed into the callback.
We could make the curl variable global. Or we can take advantage of the void *userdata option to the write callback and pass in a struct containing both the curl handle and the buffer.
Here's a rough sketch demonstrating how the write callback can get access to the response code and also save the response body.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <curl/curl.h>
typedef struct {
CURL *curl;
char *buf;
} curl_write_data;
size_t curlWriteHandler(char* chars, size_t size, size_t nmemb, void* userp) {
curl_write_data *curl_data = (curl_write_data*)userp;
long response_code;
curl_easy_getinfo(curl_data->curl, CURLINFO_RESPONSE_CODE, &response_code);
printf("Response: %ld\n", response_code);
// Now we can save if we like.
if( response_code < 300 ) {
curl_data->buf = malloc(size*(nmemb+1));
strcpy(curl_data->buf, chars);
strcat(curl_data->buf, "\0");
return size * nmemb;
}
else {
return 0;
}
}
int main() {
CURL *curl = curl_easy_init();
if(!curl) {
perror("Cant' init curl");
}
curl_write_data curl_data = { .curl = curl, .buf = NULL };
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com/alsdfjalj");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteHandler);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &curl_data);
curl_easy_perform(curl);
if( curl_data.buf ) {
puts(curl_data.buf);
}
}
I'm not sure if this is the best idea, its what I came up with.
I want to play mp3 files on the internet without downloading them. So, I use libcurl to get it as a stream in memory, like this:
static size_t use_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {
/* stream is NULL */
/* What to do with the stream of data ? */
}
CURLcode download_file(const char *url, const char *path, curl_progress_callback progress) {
CURL *curl;
CURLcode res = 0;
FILE *fp;
if ((curl = curl_easy_init())) {
if (progress) {
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress);
}
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, use_data);
res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
fclose(fp);
}
return res;
}
How can I parse the stream in memory to play sounds ?
The easiest way for you IMHO would be using lightweight MP3 decoding library. For example, minimp3 does it's job and consists of only 2 files.
http://keyj.emphy.de/minimp3
The API is very simple and a usage example can be found here: https://github.com/corporateshark/PortAMP/tree/master/src/Decoders/MP3
#define CURL_STATICLIB
#include <stdio.h>
#include <curl/curl.h>
#include <curl/types.h>
#include <curl/easy.h>
#include <string>
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {
size_t written;
written = fwrite(ptr, size, nmemb, stream);
return written;
}
int main(void) {
CURL *curl;
FILE *fp;
CURLcode res;
char *url = "http://localhost/aaa.txt";
char outfilename[FILENAME_MAX] = "C:\\bbb.txt";
curl = curl_easy_init();
if (curl) {
fp = fopen(outfilename,"wb");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
fclose(fp);
}
return 0;
}
Some one has posted this code for a question. But on execution of this file the downloaded file is not saving in C drive rather a new txt file will be generating with name "C:\cat.txt"... I want the downloaded file will be stored in my desired location in hard drive.. can any one help me...
First, the default CURLOPT_WRITEFUNCTION accepts a FILE * and uses fwrite using whatever's been set with CURLOPT_WRITEDATA, so you don't need to override the function unless you're using curl as a Win32 DLL.
Also, you aren't checking the return from fopen, which may fail.
I suspect in this case that you either aren't recompiling your code, or are running a different binary from the one you built
I'm currently using this C code:
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "http://my-domain.org/");
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
It prints the output on the console. How can I get the same output, but read it into, say, a string? (This is a probably a basic question, but I do not yet understand the libcurl API...)
Thanks for any help!
Mike
You need to pass a function and buffer to write it to buffer.
/* setting a callback function to return the data */
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_callback_func);
/* passing the pointer to the response as the callback parameter */
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &response);
/* the function to invoke as the data recieved */
size_t static write_callback_func(void *buffer,
size_t size,
size_t nmemb,
void *userp)
{
char **response_ptr = (char**)userp;
/* assuming the response is a string */
*response_ptr = strndup(buffer, (size_t)(size *nmemb));
}
Please take a look more info here.
you need a write callback function. I use this kind of function to read the response, error and be able to supply my own headers:
size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream)
{
std::string buf = std::string(static_cast<char *>(ptr), size * nmemb);
std::stringstream *response = static_cast<std::stringstream *>(stream);
response->write(buf.c_str(), (std::streamsize)buf.size());
return size * nmemb;
}
bool CurlGet(
const std::string &url,
const std::vector<std::string> &headers,
std::stringstream &response,
std::string &error)
{
curl_global_init(CURL_GLOBAL_ALL);
curl_slist *headerlist = NULL;
std::vector<std::string>::const_iterator it;
for (it = headers.begin(); it < headers.end(); it++) {
headerlist = curl_slist_append(headerlist, it->c_str());
}
CURL *curl = curl_easy_init();
char ebuf[CURL_ERROR_SIZE];
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, ebuf);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
CURLcode res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
curl_slist_free_all(headerlist);
if (res != CURLE_OK)
error = ebuf;
else
error.clear();
return res == CURLE_OK;
}
This can be done using
curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, write_data);
which sets a callback function write_data which is a function with the signature
size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp);
If you want userp be some internal struct you are using in your program, call
curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, &internal_struct);
to get the pointer to internal_struct passed to every call of write_data.
Hi i solve issue of return code 23 from call back function to return size from call back function.
see below code:
/* setting a callback function to return the data */
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_callback_func);
/* passing the pointer to the response as the callback parameter */
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &response);
/* the function to invoke as the data recieved */
size_t static write_callback_func(void *buffer,
size_t size,
size_t nmemb,
void *userp)
{
char **response_ptr = (char**)userp;
/* assuming the response is a string */
*response_ptr = strndup(buffer, (size_t)(size *nmemb));
return ((size_t)(size *nmemb));
//if you not send return value of size it will show you ERROR CODE 23return curl_easy_perform();
}
None of the other examples worked for me.
Here's what I eventually ended up doing:
size_t static curl_write(void *buffer, size_t size, size_t nmemb, void *userp)
{
userp += strlen(userp); // Skipping to first unpopulated char
memcpy(userp, buffer, nmemb); // Populating it.
return nmemb;
}
int GetCurl()
{
CURL *curl;
CURLcode res;
char *s = (char *) malloc(512);
curl = curl_easy_init();
if (curl)
{
curl_easy_setopt(curl, CURLOPT_URL, "http://www.google.com");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, s);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
printf("GREAT SUCCESS!! Your string is %s\n", s);
}
I am building an application (on windows using Dev-C++) and I want it to download a file. I am doing this using libcurl (I have already installed the source code using packman). I found a working example (http://siddhantahuja.wordpress.com/2009/04/12/how-to-download-a-file-from-a-url-and-save-onto-local-directory-in-c-using-libcurl/) but it doesn't close the file after download is complete. I would like an example of how to download a file in C.
The example you are using is wrong. See the man page for easy_setopt. In the example write_data uses its own FILE, *outfile, and not the fp that was specified in CURLOPT_WRITEDATA. That's why closing fp causes problems - it's not even opened.
This is more or less what it should look like (no libcurl available here to test)
#include <stdio.h>
#include <curl/curl.h>
/* For older cURL versions you will also need
#include <curl/types.h>
#include <curl/easy.h>
*/
#include <string>
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {
size_t written = fwrite(ptr, size, nmemb, stream);
return written;
}
int main(void) {
CURL *curl;
FILE *fp;
CURLcode res;
char *url = "http://localhost/aaa.txt";
char outfilename[FILENAME_MAX] = "C:\\bbb.txt";
curl = curl_easy_init();
if (curl) {
fp = fopen(outfilename,"wb");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
fclose(fp);
}
return 0;
}
Updated: as suggested by #rsethc types.h and easy.h aren't present in current cURL versions anymore.
Just for those interested you can avoid writing custom function by passing NULL as last parameter (if you do not intend to do extra processing of returned data).
In this case default internal function is used.
Details
http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTWRITEDATA
Example
#include <stdio.h>
#include <curl/curl.h>
int main(void)
{
CURL *curl;
FILE *fp;
CURLcode res;
char *url = "http://stackoverflow.com";
char outfilename[FILENAME_MAX] = "page.html";
curl = curl_easy_init();
if (curl)
{
fp = fopen(outfilename,"wb");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
fclose(fp);
}
return 0;
}