【问题标题】:How to download compressed files using the curl C API?如何使用 curl C API 下载压缩文件?
【发布时间】:2020-04-12 10:23:54
【问题描述】:

我想使用 libcurl C API 从 URL 下载压缩文件。我有以下代码:

// 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 文件(实际上只是带有 omex 扩展名的 zip 文件)没有:


#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

【问题讨论】:

  • 您要求提供详细的跟踪信息。他们展示了什么?

标签: c++ c curl download


【解决方案1】:

这实际上与traget文件被压缩的事实无关。 Zip 文件是其组件被单独压缩的档案;无法将 zip 文件解压缩为单个有意义的对象。例如,这与 gzipped tar 档案不同。 (然而,用户代理通常不希望自动将 .tgz 文件解压缩为 .tar 文件,即使可以。)

您的问题源于您没有提供文件的完整 URI。 Web 服务器通过发送重定向 (302) 返回代码进行响应。这告诉用户代理使用 Location 响应标头中提供的 URI 对资源发出新请求。

你需要告诉 libcurl 给follow redirects

curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);

302 重定向与 301 重定向的不同之处在于重定向被标记为临时。 301 返回码向用户代理建议它应该记住重定向并且以后不要尝试使用原始 URL。不应缓存 302 响应;例如,它可能用于提供当前最新版本资源的位置。

【讨论】:

  • @CiaranWelsh:当你有完整的数据时,回答问题总是更容易。请记住,我们无法阅读您的肩膀 :-) 您拥有的任何有用信息(如调试跟踪)都应包含在问题中。
【解决方案2】:

这是(可能)发生的事情:

您发送的请求没有 Accept-Encoding 标头,服务器(愚蠢地,imo)假设由于您没有指定任何特定的传输编码,您可能支持 gzip ..(听起来很愚蠢,我知道,但正确说“我不支持任何传输编码”的方式是发送标头Accept-Encoding: identity,但你没有这样做),服务器决定用Content-Encoding: gzip回答,你的代码忽略了它。接下来发生的事情是 gzip 压缩的数据保存在您的“输出文件名”中。

告诉 curl 自动处理编码(这是最简单的解决方案,绝大多数时候),只需将CURLOPT_ACCEPT_ENCODING 设置为空字符串,这告诉 curl 尝试进行压缩转换,并自动解压缩响应写之前:

curl_easy_setopt(curl_handle, CURLOPT_ACCEPT_ENCODING, "");

这应该可以解决您的问题。现在 curl 将发送一个看起来像 Accept-Encoding: gzip, deflate, br 的标头(发送的确切压缩算法将取决于您的 libcurl 编译支持的内容),并且服务器将选择其中一种编码,或者如果服务器不支持任何您的 libcurl 支持的编码,服务器应该发送未压缩的数据,

然后 curl 会在将数据发送到 CURLOPT_WRITEFUNCTION 之前自动解压缩数据

您可以在此处找到相关文档:https://curl.haxx.se/libcurl/c/CURLOPT_ACCEPT_ENCODING.html

【讨论】:

  • 非常有见地,帮助我理解我在做什么,但正确的答案是由@rici 提供的。感谢您的努力。
猜你喜欢
  • 2011-05-28
  • 2010-09-06
  • 1970-01-01
  • 2020-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-03
  • 1970-01-01
相关资源
最近更新 更多