I use libcurl to download file, url may be like this below
url = "https://***.com/**/***/abc"
url = "http://***:8000/**/test.txt?***2484d197c16b2e"
url = "http://***:8000/**/test.txt"
It is troublesome to get its file name, so is there a way to get file name using libcurl when downloading it? My download code is below.
size_t writeData(void *ptr, size_t size, size_t nmemb, FILE *stream) {
size_t written = fwrite(ptr, size, nmemb, stream);
return written;
}
int downloadFile(const char *url, const char *saveFile)
{
CURL *curl;
FILE *saveFd;
CURLcode res;
curl = curl_easy_init();
if (curl) {
saveFd = fopen(saveFile,"wb");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeData);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, saveFd);
std::string strurl = string(url);
if (strurl.find("https") != string::npos) {
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
}
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
fclose(saveFd);
}
return 0;
}
Use CURLOPT_HEADERFUNCTION to get a callback for each of the HTTP headers. You are looking for the header called Content-Disposition.
Related
I have a curl script:
curl -k -H "Content-Type: application/txt" -X POST --data-binary "#name_file.txt" -g "https://ya.com/file.txt" > .\out.txt
I can use it via console ant it works good.
I implement this script in visual studio on C++:
auto curl = curl_easy_init();
if (curl)
{
FILE* fp = fopen(path_to_file.c_str(), "wb");
curl_easy_setopt(curl, CURLOPT_URL, "https://ya.com/file.txt");
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
struct curl_slist *list = NULL;
list = curl_slist_append(list, "Content-Type: application/txt");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); // - H
std::string data_for_send = data_for_send_arr.c_str();
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, data_for_send.size());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data_for_send.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callbackfunction);
res = curl_easy_perform(curl);
if (res != CURLE_OK) AfxMessageBox(_T("error"));
curl_easy_cleanup(curl);
curl_global_cleanup();
fclose(fp);
}
size_t callbackfunction(void *ptr, size_t size, size_t nmemb, void* userdata)
{
FILE* stream = (FILE*)userdata;
if (!stream)
{
printf("!!! No stream\n");
return 0;
}
size_t written = fwrite((FILE*)ptr, size, nmemb, stream);
return written;
}
i also i need to tell that at one moment i use async call:
async(func_curl_1, data_1);
async(func_curl_2, data_2);
each function create curl and do request for server at the same time.
but this script do not work. it work one time from seven request
server returned for me empty file or broken file.
i do not know why...
try to add in your settings CURLOPT_VERBOSE
i.e.
/* ask libcurl to show us the verbose output */
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
and investigate output
I tried compiling both the examples on this question: Download file using libcurl in C/C++
Here's one of them:
#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;
}
the problem is that this example, when run, immediately returns and I get a blank file. Why? I modified to
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);
} else {
printf("error\n");
}
but I see no error. I tried compiling in both C++ and C, I get the same result on both.
I had the same issue and I fixed it by:
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true);
According to https://curl.se/libcurl/c/CURLOPT_FOLLOWLOCATION.html, true tells the library to follow HTTP location.
Get remote file size without content length.
size_t readHeader(char* header, size_t size, size_t nitems, void *userdata) {
// this works I get the file size when Content-length is available
if (startsWith(header, "Content-Length:")) {
std::string header_in_string = replaceAll(header, "Content-Length:", "");
long size = atol(header_in_string.c_str());
*file_size = size;
}
else { // do something else }
return size * nitems;
}
// main function
if (curl) {
fp = fopen(temp_file_name, "wb");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_CAINFO, "./ca-bundle.crt");
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
curl_easy_setopt(curl, CURLOPT_HEADERDATA, this);
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, readHeader);
// writing the downloading content to file
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
fclose(fp);
}
This provide the solution in C# via GET
Getting remote file size without content-length but I want to know the solution in C++ with libcurl.
I'm working upon a program in C++, where I have to download a file using cURL, and further open it, but the problem is that when I try to open the file after downloading, it isn't opening. I'm trying to open an .exe file. Here's the portion of code, which is responsible for file download
curl = curl_easy_init();
if (curl) {
fp = fopen(outfilename.c_str(), "w");
curl_easy_setopt(curl, CURLOPT_URL, links[index]);
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);
}
and the line which should open the downloaded file :
ShellExecute(NULL, "open", fileToLaunch.c_str() , NULL, NULL, SW_HIDE);
When I try to launch manually the file (by clicking on it), Windows returns me an error message which says that respective app isn't a Win32 app. I'm using Visual Studio 2017.
Here's the whole chunk of code :
#include <stdio.h>
#include <curl/curl.h>
#include <curl/easy.h>
#include <string>
#include <iostream>
using namespace std;
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;
string url = "Here goes the url for file download";
string outfilename = "C:\\1.exe";
curl = curl_easy_init();
if (curl) {
fp = fopen(outfilename.c_str(), "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;
ShellExecute(NULL, "open", outfilename.c_str(), NULL, NULL, SW_HIDE);
}
Change
fp = fopen(outfilename.c_str(), "w");
to
fp = fopen(outfilename.c_str(), "wb");
You're writing it to disk as text with newline translations by default. You need to write it as binary.
See https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfopen?view=vs-2017 for a fuller explanation.
First of all this line should be deleted :
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
because write_data function has the same functionality, as cURL's CURLOPT_WRITEDATA, and so, the line below the one which should be deleted will be enough, and secondly, on the line where curl_easy_setopt() is firstly met:
curl_easy_setopt(curl, CURLOPT_URL, url);
you should add to the url parameter .c_str(), so it will look like this :
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
because this function is unable to operate with string type of data...
And as Rob K mentioned, you have to change 'w' to 'wb' in fopen() function, because you are working with a binary stream of data.
i got a small program from http://curl.haxx.se/ and while i run it always prints the webpage how can i disable the printing function
#include <iostream>
#include <curl/curl.h>
using namespace std;
int main() {
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "http://google.com");
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION,1);
res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
}
return 0;
}
You need to set up a CURLOPT_WRITEFUNCTION to make it not use stdout.
There is an explanation here (under CURLOPT_WRITEFUNCTION):
http://curl.haxx.se/libcurl/c/curl_easy_setopt.html
and here (Under "Handling the Easy libcurl):
http://curl.haxx.se/libcurl/c/libcurl-tutorial.html
Basically adding the function:
size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp)
{
return size * nmemb;
}
and calling
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
Should do it.
You can still get diagnostic messages. To stop these either change or add the following line:
curl_easy_setopt (curl, CURLOPT_VERBOSE, 0L); //0 disable messages
To write data into file instead of printing, give a file descriptor as:
FILE *wfd = fopen("foo.txt", "w");
...
curl_easy_setopt(curl, CURLOPT_WRITEDATA, wfd);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
What worked for me was using the CURLOPT_NOBODY option in the code, referenced here: http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTNOBODY
#include <iostream>
#include <curl/curl.h>
using namespace std;
int main() {
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "http://google.com");
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION,1);
//USING CURLOPT NOBODY
curl_easy_setopt(curl, CURLOPT_NOBODY,1);
res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
}
return 0;
}