C: ошибка 408 при загрузке файла на Google Диск через HTTP-запрос

#c #rest #http #curl #google-drive-api

#c #rest #http #curl #google-drive-api

Вопрос:

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

Пример кода 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(amp;(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), amp;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 *)amp;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: %sn", 
    curl_easy_strerror(res));
    }
    else {
        printf("%sn", 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

Комментарии:

1. @KamilCuk забыл упомянуть, я запускаю ./a.out после этого

2. В вашем коде много ошибок. Пожалуйста, предоставьте лучший MCVE. chunk.memory — фрагмент является указателем и memory неизвестным элементом поля. res вероятно, это «CURLcode . chunk5″ chunk . curl_handle есть CURL * . Было бы неплохо добавить stat вызов и size_t WriteMemoryCallback(char *ptr, size_t size, size_t nmemb, void *userdata) { printf("%s %p %zu %zu %p '%.*s'n", __func__, ptr, size, nmemb, userdata, nmemb, ptr); return nmemb; } объявление.

3. @KamilCuk Привет, я обновил свой пост, пожалуйста, посмотрите, спасибо!

4. @KamilCuk да, вы правы, но я последовал примеру libcurl для fileupload, все еще ища другие способы публикации файла с помощью libcurl

5. @KamilCuk Я отредактировал свой вопрос, изменив PUT метод на POST , пожалуйста, посмотрите, спасибо!

Ответ №1:

Это:

 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 Набор — это тело сообщения. Вы задаете для тела сообщения uploadType=media значение . Вы не хотите отправлять эту строку, вы хотите отправить файл. Строка uploadType=media является частью URL-адреса, и вы должны просто добавить ее в URL-адрес. Google ответил 404 not found — 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* using 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", amp;file_info);
    printf("%zun", 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: %sn", 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"
 }
}
 

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