1

My goal is to get a CSV or XLS from a specific URL using Cpp.

When opening the following link

http://www.centrodeinformacao.ren.pt/userControls/GetExcel.aspx?T=CRG&P=01-01-2007&variation=PT

, can see in Browser tools

Browser redirect

a 302 redirect and the file being actually downloaded from the following URL

http://www.centrodeinformacao.ren.pt/_layouts/CI.GetExcel/SafeGetExcel.aspx?T=CRG&P=02-01-2007&variation=PT

as shown in the next image (Request URL)

Request URL

If I go to any of the two links manually, the a .xls file downloads just fine so we might as well use the one after redirection.


I've decided to move on an use libcurl with Visual Studio 2017 on my W10 machine. The recommended way to include libcurl in a Visual Studio 2017 project is to use vcpkg and that's what I used.


1. Install vcpkg

  1. Open Git Bash, cd C:/Program Files/ and cloned this repo.

Clone repo

  1. Open Command Prompt, cd C:/Program Files/vcpkg, run bootstrap-vcpkg.bat

bootstrap-vcpkg.bat

and after run vcpkg integrate install

vcpkg integrate install


2. Install libcurl

  1. Run vcpkg install curl

vcpkg install curl


3. Create a new project

  1. Simply create Visual C++ > Windows Desktop > Windows Console Application

Windows Console Application

to be able to use #include <curl/curl.h> right away

use #include <curl/curl.h>


4. Current Result

Then, inspired in the following answers

And using the following code

#include "pch.h"
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <iostream>
#include <stdio.h>
#include <curl/curl.h>
#include <string.h>

size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {
    size_t written = fwrite(ptr, size, nmemb, stream);
    return written;
}

void downloadFile(const char* url, const char* fname) {
    CURL *curl;
    FILE *fp;
    CURLcode res;
    curl = curl_easy_init();
    if (curl) {
        fp = fopen(fname, "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);
    }
}

int main(void) {

    downloadFile("http://www.centrodeinformacao.ren.pt/_layouts/CI.GetExcel/SafeGetExcel.aspx?T=CRG&P=01-01-2007&variation=PT", "C:\\Users\\molecoder\\Desktop\\test.csv");

}

I can see a test.csv in the desired folder but it's an empty file.

empty CSV

molecoder
  • 423
  • 1
  • 7
  • 24
  • Try adding quotes around the URL. – HolyBlackCat Jul 11 '20 at 15:20
  • @HolyBlackCat currently using `std::string url = date.strftime("http://www.centrodeinformacao.ren.pt/_layouts/CI.GetExcel/SafeGetExcel.aspx?T=CRG&P=%d-%m-%Y&variation=PT");`. Is that what you're suggesting? – molecoder Jul 11 '20 at 15:23
  • 1
    No, I'm saying `start` probably needs additional quotes around the URL. `std::string op = "start \"" + url + "\"";`. – HolyBlackCat Jul 11 '20 at 15:25
  • @HolyBlackCat that solved the problem of the 'P' and 'variation' not being recognized yet it didn't download the file. – molecoder Jul 11 '20 at 15:33
  • Why you do this in C++ and not C#/.Net? – i486 Jul 11 '20 at 15:39
  • @i486 i have used before Cpp to download from other sources and thought doing so would be another good opportunity to develop in the language. – molecoder Jul 11 '20 at 15:43

2 Answers2

1

Once going to that specific URL, a .xls file downloads. I don't mind getting a XLS instead of a CSV and so changing it to I was able to get the file as expected.

downloadFile("http://www.centrodeinformacao.ren.pt/_layouts/CI.GetExcel/SafeGetExcel.aspx?T=CRG&P=01-01-2007&variation=PT", "C:\\Users\\molecoder\\Desktop\\test.xls");

It works

This is the final code

#include "pch.h"
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <iostream>
#include <stdio.h>
#include <curl/curl.h>
#include <string.h>

size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {
    size_t written = fwrite(ptr, size, nmemb, stream);
    return written;
}

void downloadFile(const char* url, const char* fname) {
    CURL *curl;
    FILE *fp;
    CURLcode res;
    curl = curl_easy_init();
    if (curl) {
        fp = fopen(fname, "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);
    }
}

int main(void) {

    downloadFile("http://www.centrodeinformacao.ren.pt/_layouts/CI.GetExcel/SafeGetExcel.aspx?T=CRG&P=01-01-2007&variation=PT", "C:\\Users\\molecoder\\Desktop\\test.xls");

}
molecoder
  • 423
  • 1
  • 7
  • 24
0

You have to understand how your shell works.

Note: When you use system() it is the shell that executes the command.

Your command:

http://www.centrodeinformacao.ren.pt/_layouts/CI.GetExcel/SafeGetExcel.aspx?T=CRG&P=01-01-2007&variation=PT

Your shell is treating the "&" character as the "and" operator. Execute the left and right side of the "and" as commands. So it is treating the above as three commands:

http://www.centrodeinformacao.ren.pt/_layouts/CI.GetExcel/SafeGetExcel.aspx?T=CRG
P=01-01-2007
variation=PT

It fails on the second command P=01-01-2007

To fix this you may want to quote the string.

"http://www.centrodeinformacao.ren.pt/_layouts/CI.GetExcel/SafeGetExcel.aspx?T=CRG&P=01-01-2007&variation=PT"

 op.insert(0, 1, '"');
 op.append(1, '"');
 system(op.c_str());
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • Just updated the question after feedback from @HolyBlackCat. – molecoder Jul 11 '20 at 15:36
  • @molecoder You just executed the `start` command: https://ss64.com/nt/start.html Setting the title to the url you specified. – Martin York Jul 11 '20 at 16:22
  • I've used it before to download from a location where a file resided (https://location.com/file.csv) – molecoder Jul 11 '20 at 16:29
  • @molecoder Read the doc I linked. "Start" is a command that runs a command in a window. **Your have simply set the title and not run a command**. So you don't need "start" or set the title and use the url as the command for "start" – Martin York Jul 11 '20 at 16:31
  • Just read the doc you shared and I'm not understanding yet what you're hinting at... – molecoder Jul 11 '20 at 17:22