libcurl,处理分块数据

时间:2018-06-22 10:45:56

标签: c ftp libcurl chunked

我在C中使用libcurl来获取目录中的文件列表:

curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_URL, path);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, data);

res = curl_easy_perform(curl);

在回调中,我解析输出并处理文件:

static size_t my_fwrite(void * buffer, size_t size, size_t nmemb, void * root)
{
    ftp_user_data * data = (ftp_user_data *)root;

    char * rec = bxi_malloc(size * nmemb + 1);
    bxi_memcpy(rec, buffer, size * nmemb);

    printf("### %u (%u*%u)\n", (u32)(size*nmemb), (u32)size, (u32)nmemb);
    lines_process(rec, data);

    bxi_free(rec);

    return size * nmemb;
}

但是我有一个问题。如果目录足够大,则答案会分块显示:

### 2865 (1*2865)
## 
drwxr-xr-x   2 film     tk           4096 Jun  6 10:03 .
drwxr-xr-x 114 film     tk          53248 Jun 21 19:02 ..
-rw-r--r--   4 film     tk       19944333271 Jun  6 04:01 00f94595-a72c-44d7-8d7a-990c04a96f90
-rw-r--r--   3 film     tk        3439134 Jun  6 01:01 049307a7-b335-4981-a77c-735628473343
-rw-r--r--   4 film     tk       138285646 Jun  6 01:20 075a6aa4-8eab-43cc-9262-04f19ce11c6a
<...>
-rw-r--r--   4 film     tk       2712631246 Jun  6 00:41 61043b6f-6897-499a-8ca3-8c3d2401af10
-rw-r--r--   4 film     tk       2459646286 Jun  6 01:14 6afd69e4-b098-453a-ae4f-790e3f08fff0
-rw-r--r--   4 film     tk       2401778628 Jun  6 01:17 75ade815-1138-4db7-b096-49f945996e8f
-rw-r--r--   4 film     tk       32681128626 Jun  6 03:47 77fa0b2e-7188-4b05-9cd0-db054282885b
-rw-r--r--   4 film     tk       6


### 2866 (1*2866)
## 
0794406 Jun  6 01:01 78567514-d7be-4190-8951-6455f43c7bc5
-rw-r--r--   4 film     tk       2381291004 Jun  6 01:23 789bdabc-d893-4853-9fae-75ff9b3e9d95
-rw-r--r--   4 film     tk       863532650 Jun  6 00:51 7e58294b-4ce0-4a21-8829-aa7b3c0abedc
-rw-r--r--   4 film     tk       27817391742 Jun  6 02:57 81d72c9f-5d4b-4d54-8cb1-a108dac46ff2
-rw-r--r--   4 film     tk       21342057477 Jun  6 04:35 83e64160-03a6-400d-924a-d1848d6b85b8
<...>
-rw-r--r--   3 film     tk          16603 Jun  6 01:23 PKL_992597cf-0c66-4f57-ba34-71706ccd7e53.xml
-rw-r--r--   3 film     tk          16599 Jun  6 01:23 PKL_b32fca6b-5f36-40fb-a64f-3d110edd1b74.xml

这当然破坏了lines_process函数中的解析算法。

我如何要求服务器或libcurl一次向我发送数据?还是有另一种方法来找出答案是否分块?

非常感谢。

2 个答案:

答案 0 :(得分:1)

找到解决方案:

由于用户函数的回调将在curl_easy_perform返回之前被调用几次,因此可以将缓冲区的指针转移到my_fwrite,然后 realloc 在那里缓存,追加新数据,并仅在curl_easy_perform返回控件后进行处理:

curl_easy_perform started
    my_fwrite(enter 0, appended 2000)
    my_fwrite(enter 2000, appended 2000)
    my_fwrite(enter 4000, appended 2000)
curl_easy_perform exited
postprocess the allocated buffer.

非常感谢David Collins提出的另一种解决方案。不幸的是,我无法应用它,因为我使用的服务器会忽略设置(NIH设置)。

UPD,代码:

回调数据结构:

typedef struct
{
    char     * path;
    CML_Node * root;
    char     * lines;
} ftp_user_data;

回调函数:

static size_t my_fwrite(void * buffer, size_t size, size_t nmemb, void * root)
{
    ftp_user_data * data = (ftp_user_data *)root;

    u32 oldlen = bxi_strlen(data->lines);

    data->lines = bxi_realloc(data->lines, oldlen + size * nmemb + 1);
    bxi_memcpy(data->lines + oldlen, buffer, size * nmemb);
    data->lines[oldlen + size * nmemb] = '\0';

    printf("### %u (%u*%u)\n", (u32)(size*nmemb), (u32)size, (u32)nmemb);

    return size * nmemb;
}

curl_send函数,用于设置选项和回调

curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_URL, path);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite); // < setting callback
curl_easy_setopt(curl, CURLOPT_WRITEDATA, data);

res = curl_easy_perform(curl); // < calling the curl

if (CURLE_OK != res)
    fprintf(stderr, "Error during request: %s (%d) for path %s\n",
            curl_easy_strerror(res), res, path);

curl_easy_cleanup(curl); // < cleaning the curl

printf("Exited curl\n");

lines_process(data); // < processing stored data
bxi_free(data->lines);

因此,在得到一些小答案之后,我得到了:

### 100 (1 * 100) // < Got only 100 bytes in data->lines now
Exited curl

大答案之后我得到:

### 2048 (1*2048) // < Got 2048 bytes appended to data->lines
### 2048 (1*2048) // < Now it's 2048 + 2048 = 4096 bytes
### 305 (1*305)   // < And the last 305 get appended => strlen(data->lines) == 4401
Exited curl // < now I have 4401 byte in data->lines and can totally parse it

答案 1 :(得分:1)

  

如何请求服务器或libcurl一次向我发送数据?

我认为最快,最简单的方法可能是在请求中指定HTTP协议的1.0版。 (压缩编码仅在HTTP版本1.1中引入。)

curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
  

还是有另一种方法来找出答案是否被分块?

您可以测试是否存在“传输编码”标头并检查其值。首先,您需要安装标题回调。

curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, my_header_cb);

然后回调本身...

static size_t my_header_cb(char *data, size_t size, size_t nmemb,
                           void *userdata) {
    size_t count = size * nmemb;
    char *tok = NULL;

    tok = strtok(data, ":");
    if (!tok) {
        fprintf(stderr, "Invalid header: %s\n", data);
        return count;
    }
    puts(tok);
    if (strcasecmp(tok, "transfer-encoding") == 0) {
        tok = strtok(NULL, ":");
        if (tok && strstr(tok, "hunked") != NULL) {
            // Set a flag or do whatever else you need here.
            printf("Using chunked encoding!\n");
        }
    }

    return count;
}

如果您想要回调,例如设置一个标志以指示正在使用分块编码,请将参数传递给回调。

int chunked_flag = 0;

curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *)&chunked_flag);