C: Ошибка 408 ответа при загрузке файла на Google Диск через HTTP-запрос - PullRequest
3 голосов
/ 17 апреля 2019

Я следовал примеру Google Tutorial & libcurl до simple upload изображения на Google Drive через HTTP, но получил ошибку 404 Not Found, которая должна происходить только при resumable upload, согласно Google Guide .

Пример кода Google:

POST https://www.googleapis.com/upload/drive/v3/files?uploadType=media HTTP/1.1
Content-Type: image/jpeg
Content-Length: [NUMBER_OF_BYTES_IN_FILE]
Authorization: Bearer [YOUR_AUTH_TOKEN]

[JPEG_DATA]

Мой код:

/* for storing the response */
struct MemoryStruct {
    char *memory;
    size_t size;
};

static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
    size_t realsize = size * nmemb;
    struct MemoryStruct *mem = (struct MemoryStruct *)userp;

    char *ptr = realloc(mem->memory, mem->size + realsize + 1);
    if(ptr == NULL) {
        /* out of memory! */
        printf("not enough memory (realloc returned NULL)\n");
        return 0;
    }
    mem->memory = ptr;
    memcpy(&(mem->memory[mem->size]), contents, realsize);
    mem->size += realsize;
    mem->memory[mem->size] = 0;

    return realsize;
}

int main(void){
    CURL *curl_handle;
    CURLcode res;

    struct MemoryStruct chunk;

    chunk.memory = malloc(1);
    chunk.size = 0;

    curl_global_init(CURL_GLOBAL_ALL);

    curl_handle = curl_easy_init();
    struct stat file_info;
    FILE *fd;

    /* get a FILE * of the image */
    fd = fopen("grinch.jpg", "rb");
    if(!fd)
        return 1;

    if(fstat(fileno(fd), &file_info) != 0)
        return 1;

    struct curl_slist *chunk = NULL;

    /* Content-Type: image/jpeg */
    chunk = curl_slist_append(chunk5, "Content-Type: image/jpeg");

    /* Content-Length: [NUMBER_OF_BYTES_IN_FILE] */
    char size[200];
    snprintf(size, 200, "Content-Length: %lld", (curl_off_t)file_info.st_size);

    chunk = curl_slist_append(chunk, size);

    /* Authorization: Bearer [YOUR_AUTH_TOKEN] */
    char auth[300];
    strcat(auth, "Authorization: Bearer ");
    strcat(auth, access_token);

    chunk = curl_slist_append(chunk, auth);

    curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, chunk);

    /* POST https://www.googleapis.com/upload/drive/v3/files?uploadType=media */
    curl_easy_setopt(curl_handle, CURLOPT_URL, "https://www.googleapis.com/upload/drive/v3/files");

    curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, "uploadType=media");

    /* [JPEG_DATA] */
    curl_easy_setopt(curl_handle, CURLOPT_POST, 1L);

    curl_easy_setopt(curl_handle, CURLOPT_READDATA, fd);

    curl_easy_setopt(curl_handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size);

    /* Save response */
    curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);

    curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);

    curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);

    curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");

    res = curl_easy_perform(curl_handle);

    /* Print response */
    if(res != CURLE_OK) {
        fprintf(stderr, "curl_easy_perform() failed: %s\n", 
    curl_easy_strerror(res));
    }
    else {
        printf("%s\n", chunk.memory);
    }

Но ответ, который я получил, не соответствовал учебнику.

Google сказал, что должен ответить:

HTTP/1.1 200
Content-Type: application/json

{
  "name": "myObject"
}

Ответ, который я получил, когда запускаю прямо в XCode:

POST /upload/drive/v3/files HTTP/1.1
Host: www.googleapis.com
User-Agent: libcurl-agent/1.0
Accept: */*
Content-Type: image/jpeg
Content-Length: 5123
Authorization: Bearer [my access token]

* upload completely sent off: 16 out of 16 bytes
< HTTP/1.1 408 Request Timeout
< Content-Type: text/html; charset=UTF-8
< Referrer-Policy: no-referrer
< Content-Length: 1557
< Date: Wed, 17 Apr 2019 08:32:18 GMT
< Alt-Svc: quic=":443"; ma=2592000; v="46,44,43,39"
< Connection: close
< 
* Closing connection 0
<!DOCTYPE html>
<html lang=en>
  <meta charset=utf-8>
  <meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
  <title>Error 408 (Request Timeout)!!1</title>
  <style>
    *{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}
  </style>
  <a href=//www.google.com/><span id=logo aria-label=Google></span></a>
  <p><b>408.</b> <ins>That’s an error.</ins>
  <p>Your client has taken too long to issue its request.  <ins>That’s all we know.</ins>

Program ended with exit code: 0

Я не могу найти объяснение Error 408 (Request Timeout)!! в Google Tutorial

1 Ответ

1 голос
/ 17 апреля 2019

Это:

curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, "uploadType=media");

совпадает с:

curl_easy_setopt(curl_handle, CURLOPT_READDATA, "uploadType=media");
curl_easy_setopt(curl_handle, CURLOPT_READFUNCTION, a_fucntion_that_reads_that_string);

Набор CURLOPT_POSTFIELDS - это тело POST.Вы устанавливаете тело сообщения на uploadType=media.Вы не хотите отправлять эту строку, вы хотите отправить файл.Строка uploadType=media является частью URL, и вы должны просто добавить ее в URL.Google ответил с 404 не найден - URL /upload/drive/v3/files является недействительным, должно быть /upload/drive/v3/files?uploadType=media.

Вы должны установить правильный URL:

curl_easy_setopt(curl_handle, CURLOPT_URL, "https://www.googleapis.com/upload/drive/v3/files?uploadType=media");

удалить

// remove
// curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, "uploadType=media");

и просто прочитайте данные из FILE*, используя CURLOPT_READDATA.

Я остался со следующей программой (извините за беспорядок):

#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/stat.h>

const char access_token[] = "Proceeding at three quarters impulse. Make it so, commander!";

int main() {

    CURL * curl_handle = curl_easy_init();
    struct stat file_info;
    FILE *fd;

    stat("grinch.jpg", &file_info);
    printf("%zu\n", file_info.st_size);

    /* get a FILE * of the image */
    fd = fopen("grinch.jpg", "rb");
    if (fd == NULL) {
        abort();
    }

    struct curl_slist *chunk = NULL;

    /* Content-Type: image/jpeg */
    chunk = curl_slist_append(chunk, "Content-Type: image/jpeg");

    /* Content-Length: [NUMBER_OF_BYTES_IN_FILE] */
    char size[200];
    snprintf(size, 200, "Content-Length: %lld", (long long int)file_info.st_size);

    chunk = curl_slist_append(chunk, size);

    /* Authorization: Bearer [YOUR_AUTH_TOKEN] */
    char auth[300];
    strcat(auth, "Authorization: Bearer ");
    strcat(auth, access_token);

    chunk = curl_slist_append(chunk, auth);

    curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, chunk);
    curl_easy_setopt(curl_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    curl_easy_setopt(curl_handle, CURLOPT_URL, "https://www.googleapis.com/upload/drive/v3/files?uploadType=media");
    curl_easy_setopt(curl_handle, CURLOPT_POST, 1L);
    curl_easy_setopt(curl_handle, CURLOPT_READDATA, fd);
    curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
    CURLcode res = curl_easy_perform(curl_handle);

    /* Print response */
    if(res != CURLE_OK) {
        fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
    }
    else {
        printf(" SUCCESS  \n");
    }

    curl_easy_cleanup(curl_handle);
    curl_global_cleanup();

}

Для запросаЯ получаю ответ:

{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "authError",
    "message": "Invalid Credentials",
    "locationType": "header",
    "location": "Authorization"
   }
  ],
  "code": 401,
  "message": "Invalid Credentials"
 }
}

Но, скорее всего, мои учетные данные плохие;).

...