Как скачать сжатые файлы с помощью API curl C? - PullRequest
0 голосов
/ 12 апреля 2020

Я хочу скачать сжатый файл с URL, используя libcurl C API. У меня есть следующий код:

// CurlGet.h

#include <cstddef>
#include <cstdlib>
#include <cstring>
#include <curl/curl.h>


struct memory {
    char *response;
    size_t size;
};

size_t callBackWrite(void *data, size_t size, size_t nmemb, void *userp) {
    size_t written = fwrite(data, size, nmemb, (FILE *) userp);
    return written;
}

int curlGetC(const char *url, const char* output_filename) {
    CURL *curl_handle;

    curl_global_init(CURL_GLOBAL_ALL);

    /* init the curl session */
    curl_handle = curl_easy_init();
    if (!curl_handle) {
        throw std::logic_error("You no curl");
    }

    /* set URL to get here */
    curl_easy_setopt(curl_handle, CURLOPT_URL, url);

    /* Switch on full protocol/debug output while testing */
    curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);

    /* disable progress meter, set to 0L to enable it */
    curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 0L);

    /* send all data to this function  */
    curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, callBackWrite);

    /* open the file */
    FILE *f = fopen(output_filename, "wb");
    if (!f) {
        throw std::invalid_argument("You no got file");
    }

    /* write the page body to this file handle */
    curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, f);

    /* get it! */
    curl_easy_perform(curl_handle);

    /* close the header file */
    fclose(f);

    /* cleanup curl stuff */
    curl_easy_cleanup(curl_handle);

    curl_global_cleanup();
    return 0;
}

Затем использование этого кода для загрузки веб-страницы работает, как и ожидалось, но загрузка файла omex (который на самом деле является просто zip файлом с расширением omex) не:


#include "CurlGet.h"
#include <iostream>

// works as expected
std::string url1 = "https://isocpp.org/wiki/faq/mixing-c-and-cpp";
std::string output_filename1 = "/mnt/d/libsemsim/semsim/example.html";
curlGetC(url1_.c_str(), output_filename1_.c_str());

// downloaded file is 0 bytes.
std::string url2 = "https://auckland.figshare.com/ndownloader/files/17432333";
std::string output_filename2 = "/mnt/d/libsemsim/semsim/example.omex";
curlGetC(url2_.c_str(), output_filename2_.c_str());

Может кто-нибудь предложить, как изменить мой код, чтобы он загружал сжатый файл?

edit: отображение подробных следов:

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying 52.48.88.255...
* TCP_NODELAY set
* Connected to auckland.figshare.com (52.48.88.255) port 443 (#0)
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: C=GB; L=London; O=figshare LLP; CN=*.figshare.com
*  start date: Mar 20 00:00:00 2019 GMT
*  expire date: Jul  9 12:00:00 2020 GMT
*  subjectAltName: host "auckland.figshare.com" matched cert's "*.figshare.com"
*  issuer: C=US; O=DigiCert Inc; CN=DigiCert SHA2 Secure Server CA
*  SSL certificate verify ok.
> GET /ndownloader/files/17432333 HTTP/1.1
Host: auckland.figshare.com
Accept: */*

< HTTP/1.1 302 Found
< Date: Sun, 12 Apr 2020 10:43:10 GMT
< Content-Type: application/octet-stream
< Content-Length: 0
< Connection: keep-alive
< Server: nginx
< X-Storage-Protocol: https
< X-Filename: BIOMD0000000204_new.omex
< Location: https://objectext.auckland.ac.nz/figshare/17432333/BIOMD0000000204_new.omex
< X-Storage-Host: objectext.auckland.ac.nz
< X-Storage-File: 17432333/BIOMD0000000204_new.omex
< X-Storage-Bucket: figshare
< Content-Disposition: attachment;filename=BIOMD0000000204_new.omex
< Cache-Control: no-cache, no-store
< Set-Cookie: fig_tracker_client=0975a192-4ec5-4a63-a800-c598eb7ca6b5; Max-Age=31536000; Path=/; expires=Mon, 12-Apr-2021 10:43:10 GMT; secure; HttpOnly
< X-Robots-Tag: noindex
< X-Frame-Options: SAMEORIGIN
< X-XSS-Protection: 1; mode=block
< Strict-Transport-Security: max-age=31536000; includeSubDomains;
< Cache-Control: public, must-revalidate, proxy-revalidate
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Methods: GET, OPTIONS
< Access-Control-Allow-Headers: Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,Range
< Access-Control-Expose-Headers: Location,Accept-Ranges,Content-Encoding,Content-Length,Content-Range
< 
  0     0    0     0    0     0      0      0 --:--:--  0:00:01 --:--:--     0
* Connection #0 to host auckland.figshare.com left intact

Ответы [ 2 ]

1 голос
/ 12 апреля 2020

Это действительно не имеет никакого отношения к тому факту, что файл traget сжат. Zip-файлы - это архивы, компоненты которых сжимаются индивидуально; невозможно распаковать zip-файл в один значимый объект. Это отличается от архивов tar с gzip, например. (Однако обычно пользовательскому агенту нежелательно автоматически распаковывать файл .tgz в файл .tar, даже если это возможно.)

Ваша проблема связана с тем, что вы не предоставили полную версию. URI для файла. Веб-сервер ответил отправкой кода возврата перенаправления (302). Это говорит агенту пользователя сделать новый запрос к ресурсу, используя URI, указанный в заголовке ответа Location.

Вам необходимо указать libcurl следовать перенаправлениям .

curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);

302 перенаправления отличаются от перенаправлений 301 тем, что перенаправление помечается как временное. Код возврата 301 подсказывает агенту пользователя, что он должен помнить перенаправление и не пытаться использовать исходный URL в будущем. Ответ 302 не должен кэшироваться; например, его можно использовать для указания местоположения самой последней версии ресурса.

0 голосов
/ 12 апреля 2020

вот (вероятно) то, что произошло:

Вы отправили запрос без заголовка Accept-Encoding, сервер (по глупости, imo) предположил, что, поскольку вы не указали никаких специфических c кодировок передачи , вы, вероятно, поддерживаете gzip .. (звучит глупо, я знаю, но правильный способ сказать "я не поддерживаю любые кодировки передачи" - это отправить заголовок Accept-Encoding: identity, но вы этого не сделали), и сервер решил ответить Content-Encoding: gzip, который ваш код игнорировал. что происходит дальше, так это то, что сжатые gzip данные были сохранены в вашем «файле_выхода».

, чтобы указать curl автоматически обрабатывать кодировки (что является самым простым решением в подавляющем большинстве случаев), просто установите CURLOPT_ACCEPT_ENCODING пустая строка, это говорит curl о попытке выполнить сжатие tansfer, и автоматически распаковывает ответ перед записью:

curl_easy_setopt(curl_handle, CURLOPT_ACCEPT_ENCODING, "");

, которая должна решить вашу проблему. теперь curl отправит заголовок, похожий на Accept-Encoding: gzip, deflate, br (точные отправленные алгоритмы сжатия будут зависеть от того, что ваш libcurl был скомпилирован для поддержки), и сервер выберет 1 из этих кодировок, или если сервер не поддерживает какую-либо из Кодировки, которые поддерживает ваша libcurl, сервер должен отправлять в несжатом виде,

, и curl, в свою очередь, автоматически распаковывает данные перед отправкой в ​​CURLOPT_WRITEFUNCTION

. Вы можете найти соответствующую документацию здесь: https://curl.haxx.se/libcurl/c/CURLOPT_ACCEPT_ENCODING.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...