I use libcurl in my C code to download files given their urls. My code looks similar to this:
#include <stdio.h>
#include <curl.h>
#include <pthread.h>
static size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream)
{
size_t written = fwrite(ptr, size, nmemb, (FILE *)stream);
return written;
}
int progress_func(void *ptr, double TotalToDownload, double NowDownloaded,
double TotalToUpload, double NowUploaded)
{
struct my_custom_struct *my_dummy_data = (struct my_custom_struct *) data;
//do some stuffs here
return 0;
}
void *download_with_curl(void *data)
{
char *url = (char *) data;
int res = 0;
// My custom struct to store data
struct my_custom_struct my_dummy_data;
char errbuff[CURL_ERROR_SIZE] = {0};
CURL *curl_handle;
/* init the curl session */
curl_handle = curl_easy_init();
/* set URL to get here */
curl_easy_setopt(curl_handle, CURLOPT_URL, url);
/* disable progress meter, set to 0L to enable*/
curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 0L);
/* send all data to this function*/
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl_handle, CURLOPT_LOW_SPEED_TIME, RESPOND_TIME);
curl_easy_setopt(curl_handle, CURLOPT_LOW_SPEED_LIMIT, 30L);
/* set the progress function */
curl_easy_setopt(curl_handle, CURLOPT_PROGRESSFUNCTION, progress_func);
/* set the progress data */
curl_easy_setopt(curl_handle, CURLOPT_PROGRESSDATA, &my_dummy_data);
/* provide a buffer to store errors in */
curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errbuff);
FILE *pagefile = fopen(path_to_where_I_want_to_store_the_file, "wb");
/* write the page body to this file handle */
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, pagefile);
/* get the file*/
int status = curl_easy_perform(curl_handle);
res = 0;
int response_code;
curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &response_code);
fclose(pagefile);
if (status != 0) {
log_warn("CURL ERROR %d: %s", status, errbuff);
response_code = -status;
}
/* cleanup curl stuff */
curl_easy_cleanup(curl_handle);
return NULL;
}
int main()
{
// sockfd = create a sockfd
// bind, listen
do {
// accept new connection
char *url;
// receive the url from client
pthread_t tid;
pthread_create(&tid, NULL, download_with_curl, url);
} while (1);
}
When I send a single download request, the code works fine. "Works fine" means that the md5sum values of the original file and the downloaded file are equal. However, when I send multiple requests to download multiple files, only the first file that is downloaded has the correct md5sum value. To be clear, if I send requests to download files A (200MB), B (5MB) and C (50MB) in that order, only file B is correctly downloaded because it is finished first. Files A and C will have incorrect md5sum values. Moreover, when I check the content of files A and C, it looks like curl just inserts random segments of data into them. If the original file content is
This is the content of a file
then the downloaded file is like
This is the #$%!##%#% content of $%(#(!)$()$%||##$%*&) a file
After spending two days of debugging, I finally solved the problem (I hope so). All I did was just flushing the data after calling fwrite. The function write_data now looks like this:
static size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream)
{
size_t written = fwrite(ptr, size, nmemb, (FILE *)stream);
fflush((FILE *) stream);
return written;
}
I do not know if it completely solves the problem or not. Could anyone explain why it behaves that way and give my a solution to this?
UPDATE 1
It seems that there is something to do with fwrite()'s internal buffer. Changing from fwrite(ptr, size, nmemb, stream) to write(fileno(stream), ptr, size * nmemb) seems to give the same result as using fflush().
UPDATE 2
Using the default write function (remove the option CURLOPT_WRITEFUNCTION) of libcurl gives the same problem.
Related
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
How to not show all this info?
All I do is using little edited FTP example and i dont want that info to be shown.
edit: added full code from main.c
image in link: http://www.mediafire.com/convkey/71e9/oyhctzcdjxakzxzfg.jpg
#include <stdio.h>
#include <stdlib.h>
#include <curl/curl.h>
struct FtpFile {
const char *filename;
FILE *stream;
};
static size_t my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream)
{
struct FtpFile *out=(struct FtpFile *)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 main()
{
CURL *curl;
CURLcode res;
struct FtpFile version={"version.txt", /* name to store the file as if succesful */NULL};
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
FILE *file_verzija;
int trenutna_verzija;
int nova_verzija;
char pitanje_za_update;
file_verzija=fopen("version.txt","r");
fscanf(file_verzija,"%i",&trenutna_verzija);
fclose(file_verzija);
printf("Current version %i",trenutna_verzija);
printf("\nChecking for updates...\n");
if(curl)
{
/*You better replace the URL with one that works!*/curl_easy_setopt(curl, CURLOPT_URL,"http://elektro-srb.com/site/muffin_finder_files/version.txt");
/* Define our callback to get called when there's data to be written */
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
/* Set a pointer to our struct to pass to the callback */
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &version);
/* 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 */
printf("\nerror");
}
}
if(version.stream)
fclose(version.stream); /* close the local file */
file_verzija=fopen("version.txt","r");
fscanf(file_verzija,"%i",&nova_verzija);
fclose(file_verzija);
if(trenutna_verzija != nova_verzija)
{
printf("\nUpdate found! New version is %i",nova_verzija);
}
else
{
printf("You are running latest version of Muffin Finder!");
}
if(trenutna_verzija != nova_verzija)
{
printf("\nUpdate? y/n");
scanf("\n%c",&pitanje_za_update);
if((pitanje_za_update == 'y') || (pitanje_za_update == 'Y'))
{
//UPDATE
}
else if((pitanje_za_update == 'n') || (pitanje_za_update == 'N'))
{
//pokretanje stare
}
}
curl_global_cleanup();
return 0;
}
You should construct a WRITEFUNCTION option, to prevent it from using stdout for printing.
See here: http://curl.haxx.se/libcurl/c/curl_easy_setopt.html.
Search for "WRITEFUNCTION". You should implement the function (and I assume you would like to leave it empty).
EDIT: As the manual states, you should do the following:
Implement a function to replace the default stdout:
size_t outputFunction(char *ptr, size_t size, size_t nmemb, void *userdata) {}
When you initialize the CURL structure, use this option:
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, outputFunction);
comment following line:
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
I have code to save/read an http header, but in my application I want to set and send an http response header to my client. So how can I set an http header response for any request sent by the client?
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <curl/curl.h>
static size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream)
{
int written = fwrite(ptr, size, nmemb, (FILE *)stream);
return written;
}
int main(void)
{
CURL *curl_handle;
static const char *headerfilename = "head.txt";
FILE *headerfile;
static const char *bodyfilename = "body.txt";
FILE *bodyfile;
curl_global_init(CURL_GLOBAL_ALL);
/* init the curl session */
curl_handle = curl_easy_init();
/* set URL to get */
curl_easy_setopt(curl_handle, CURLOPT_URL, "http://google.co.in");
/* no progress meter please */
curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L);
/* send all data to this function */
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_data);
/* open the files */
headerfile = fopen(headerfilename,"wb");
if (headerfile == NULL) {
curl_easy_cleanup(curl_handle);
return -1;
}
bodyfile = fopen(bodyfilename,"wb");
if (bodyfile == NULL) {
curl_easy_cleanup(curl_handle);
return -1;
}
/* we want the headers be written to this file handle */
curl_easy_setopt(curl_handle, CURLOPT_WRITEHEADER, headerfile);
/* we want the body be written to this file handle instead of stdout */
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, bodyfile);
/* get it! */
curl_easy_perform(curl_handle);
/* close the header file */
fclose(headerfile);
/* close the body file */
fclose(bodyfile);
/* cleanup curl stuff */
curl_easy_cleanup(curl_handle);
return 0;
}
As has been said in comments:
libcurl is a client-side library for doing the HTTP request. It will not help you implement the server response.
The server response will have to be sent by the server-side implementation and you haven't shown that or even explained how that is implemented. You cannot implement the server-side using libcurl.
Here I implemented code for file download from server. its working fine.
Now I want to make my own progress bar function which calculates some data like remaining seconds data Rate per second etc.
So from here I found one way to use curl progress bar option. how we can enable this option.
I completely done with this.
I put my code below. here in this code my_progress_func calls frequently as per curl library time interval. I want to change this interval time and make it to 1 second. is it possible in curl library using to set some options for curl library?
I want to call this my_progress_func function after every 1 second.
Code :
#include <stdio.h>
#include <curl/curl.h>
long test =0;
struct FtpFile {
const char *filename;
FILE *stream;
long iAppend;
};
static size_t my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream)
{
struct FtpFile *out=(struct FtpFile *)stream;
if(out && !out->stream) {
/* open file for writing */
out->stream=fopen(out->filename, out->iAppend ? "ab":"wb");
if(!out->stream)
return -1; /* failure, can't open file to write */
}
out->iAppend += nmemb;
return fwrite(buffer, size, nmemb, out->stream);
}
int my_progress_func(void *bar,
double t, /* dltotal */
double d, /* dlnow */
double ultotal,
double ulnow)
{
printf("%f : %f \n", d, t);
return 0;
}
int main(void)
{
CURL *curl;
CURLcode res;
int c;
struct FtpFile ftpfile={
"dev.zip", /* name to store the file as if succesful */
NULL,
};
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL,
"sftp://root:xyz_#192.170.10.1/mnt/xyz.tar");
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 120L);
/* Define our callback to get called when there's data to be written */
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
/* Set a pointer to our struct to pass to the callback */
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ftpfile);
curl_easy_setopt(curl, CURLOPT_FTPPORT, "-");
/* Switch on full protocol/debug output */
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, my_progress_func);
res = curl_easy_perform(curl);
printf("res is %d\n, data get %ld\n", res, ftpfile.iAppend);
///Retry upto 100 times it timeout or connection drop occur
for (c = 0; (res != CURLE_OK) && (c < 100); c++) {
curl_easy_setopt(curl, CURLOPT_RESUME_FROM , ftpfile.iAppend);
res = curl_easy_perform(curl);
if(res == CURLE_OK) c =0;
printf("%d res is %d\n, data get %ld\n",c, res, ftpfile.iAppend);
}
/* always cleanup */
curl_easy_cleanup(curl);
}
if(ftpfile.stream)
fclose(ftpfile.stream); /* close the local file */
curl_global_cleanup();
return 0;
}
According to the curl documentation:
http://curl.haxx.se/libcurl/c/curl_easy_setopt.html
Function pointer that should match the curl_progress_callback
prototype found in . This function gets called by libcurl
instead of its internal equivalent with a frequent interval during
operation (roughly once per second or sooner) no matter if data is
being transfered or not. Unknown/unused argument values passed to the
callback will be set to zero (like if you only download data, the
upload size will remain 0). Returning a non-zero value from this
callback will cause libcurl to abort the transfer and return
CURLE_ABORTED_BY_CALLBACK.
If it's calling too frequently then you can use time() and a static var to limit this, something like this:
static time_t prevtime;
time_t currtime;
double dif;
static int first = 1;
if(first) {
time(&prevtime);
first = 0;
}
time(&currtime);
dif = difftime(currtime, prevtime);
if(dif < 1.0)
return;
prevtime = currtime;
Obviously, you run the risk that curl might not call this function again for fully another second.
#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