Почему загрузка файла прекращается после отправки нескольких фрагментов данных (с использованием multipart / form-data)? - PullRequest
0 голосов
/ 29 марта 2019

Я использую libcurl для загрузки файла прошивки на аппаратное устройство.Я использую multipart / form-data, и похоже, что загрузка файла начинается нормально, но не загружает весь файл.
Загружаемый файл имеет размер 144 855 725 байт, но, похоже, только два блока по 64 КБпослал.

В размещенном коде я использую функцию обратного вызова чтения.Я также пытался просто передать имя файла в функцию curl_mime_filedata, и результаты были одинаковыми.
Одно интересное замечание: при запуске программы я получаю успешный ответ от curl_easy_perform примерно половину времени.В другую половину времени я получу ошибку 56 «Сбой при получении данных от однорангового узла».Еще одно интересное замечание: существует небольшое расхождение в размере файла (144 855 725) и размере, который локон воспринимает как размер загрузки (144 856 042).Я предполагаю, что это потому, что он учитывает все байты в теле (не только файл).Это правильно?

Вот некоторые (урезанные) выходные данные, когда я запускаю программу.
Размер файла: 144855725
ОБЩЕЕ ВРЕМЯ: 0,000092
ВВЕРХ: 0 из 0 ВНИЗ: 00
UP: 0 из 144856042 DOWN: 0 из 0
ReadCallback: Size = 1, Nmemb = 65267
Мы читаем 65267 байтов из файла
UP: 65536 из 144856042 DOWN: 0 из 0
ReadCallback: Size = 1, Nmemb = 65536
Мы читаем 65536 байтов из файла
UP: 131072 из 144856042 DOWN: 0 из 0
результат curl ERROR = <56: Ошибка при получении данных изpeer>
Не удалось загрузить файл прошивки

size_t ReadCallback(char *BufferOut, size_t Size, size_t Nmemb, void *StreamIn)
{
   curl_off_t nread;
   size_t retcode = fread(BufferOut, Size, Nmemb, (FILE *)StreamIn);
   nread = (curl_off_t)retcode;
   cout << "ReadCallback: Size=" << Size << ", Nmemb=" << Nmemb << endl;
   cout << "We read " << nread << " bytes from the file" << endl;
   return retcode;
}
int main(void)
{
   CURL *pCurl;
   CURLcode res;
   std::stringstream ss;
   struct curl_slist *headerList = NULL;
   string accessToken;
   struct TransferProgress transProgress;
   string filePath;
   FILE * pFile;
   long lSize;

   curl_global_init(CURL_GLOBAL_ALL);
   pCurl = curl_easy_init();

   if (pCurl)
   {
      EC520UutComms comms;
      curl_mime *multipart;
      curl_mimepart *part;

      accessToken = comms.GetAccessToken(pCurl);
      SetOptionsToDefault(pCurl);

      // Specify the target URL
      std::string str(comms.BaseURL() + kAPI_Upgrade);
      cout << "URL <" + str + ">" << endl;
      curl_easy_setopt(pCurl, CURLOPT_URL, str.c_str());

      multipart = curl_mime_init(pCurl);
      // Add the Content-Disposition
      part = curl_mime_addpart(multipart);
      curl_mime_name(part, "Content-Disposition");
      curl_mime_data(part, "form-data; name=\"upgrade_file\"; filename=\"\"", CURL_ZERO_TERMINATED);
      // Add the file
      part = curl_mime_addpart(multipart);
      curl_mime_type(part, "application/octet-stream");

      filePath = "C:\\Temp\\TestFile.tst";
//       curl_mime_filedata(part, filePath.c_str());
       fopen_s(&pFile, filePath.c_str(), "rb");
      // obtain file size:
      fseek(pFile, 0, SEEK_END);
      lSize = ftell(pFile);
      rewind(pFile);
      cout << "File size: " << lSize << endl;

      curl_mime_data_cb(part, lSize, ReadCallback, NULL, NULL, pFile);

      curl_easy_setopt(pCurl, CURLOPT_MIMEPOST, multipart);
      // This is a long upload, disable the timeout
      curl_easy_setopt(pCurl, CURLOPT_TIMEOUT, 0L);

      // Create headers
      ss.str("");
      ss << "Authorization: Bearer " << accessToken;
      headerList = curl_slist_append(headerList, ss.str().c_str());
      // Accept
      headerList = curl_slist_append(headerList,
         "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");

      curl_easy_setopt(pCurl, CURLOPT_HTTPHEADER, headerList);

      curl_easy_setopt(pCurl, CURLOPT_XFERINFOFUNCTION, TransferInfo);
      transProgress.curl = pCurl;
      curl_easy_setopt(pCurl, CURLOPT_XFERINFODATA, &transProgress);
      curl_easy_setopt(pCurl, CURLOPT_NOPROGRESS, 0L);

      // Now send the message
      res = curl_easy_perform(pCurl);

      curl_slist_free_all(headerList);
      curl_mime_free(multipart);

      if (res == CURLE_OK)
      {
         cout << "Firmware file successfully uploaded" << endl;
      }
      else
      {
         cout << "curl result ERROR = <" + to_string(res) + ": " + curl_easy_strerror(res) + ">" << endl;
         cout << "Failed to upload firmware file" << endl;
      }
   }
   curl_easy_cleanup(pCurl);
}

Я ожидаю, что будет загружен весь файл, а не пара фрагментов файла.

1 Ответ

1 голос
/ 29 марта 2019

Вы неправильно заполняете структуру curl_mime.Код, который вы указали, не соответствует формату MIME, который вы описали в своем предыдущем вопросе :

--1a2fc07a-d882-4470-a1da-79716d34cd9b
Content-Disposition: form-data; name="upgrade_file"; filename=""
Content-Type: application/octet-stream

// File data goes here //
--1a2fc07a-d882-4470-a1da-79716d34cd9b
Content-Disposition: form-data; name="submit"

Install OS
--1a2fc07a-d882-4470-a1da-79716d34cd9b--

Попробуйте вместо этого:

size_t ReadCallback(char *buffer, size_t size, size_t nitems, void *arg)
{
    cout << "ReadCallback: size=" << size << ", nitems=" << nitems << endl;
    size_t retcode = fread(buffer, size, nitems, (FILE *)arg);
    cout << "We read " << retcode << " bytes from the file" << endl;
    return retcode;
}

int SeekCallback(void *arg, curl_off_t offset, int origin)
{
    if (fseek((FILE *)arg, offset, origin) == 0)
        return CURL_SEEKFUNC_OK;
    else
        return CURL_SEEKFUNC_FAIL;
}

int main()
{
    CURL *pCurl;
    CURLcode res;
    struct curl_slist *headerList = NULL;
    string accessToken;
    struct TransferProgress transProgress;
    string filePath;
    FILE * pFile;
    long lSize;

    curl_global_init(CURL_GLOBAL_ALL);
    pCurl = curl_easy_init();
    if (pCurl)
    {
        EC520UutComms comms;
        curl_mime *multipart;
        curl_mimepart *part;

        accessToken = comms.GetAccessToken(pCurl);
        SetOptionsToDefault(pCurl);

        // Specify the target URL
        std::string str(comms.BaseURL() + kAPI_Upgrade);
        cout << "URL <" + str + ">" << endl;
        curl_easy_setopt(pCurl, CURLOPT_URL, str.c_str());

        multipart = curl_mime_init(pCurl);

        part = curl_mime_addpart(multipart);
        curl_mime_name(part, "upgrade_file");
        curl_mime_filename(part, "");
        curl_mime_type(part, "application/octet-stream");
        filePath = "C:\\Temp\\TestFile.tst";
        // curl_mime_filedata(part, filePath.c_str());
        fopen_s(&pFile, filePath.c_str(), "rb");
        // obtain file size:
        fseek(pFile, 0, SEEK_END);
        lSize = ftell(pFile);
        rewind(pFile);
        cout << "File size: " << lSize << endl;
        curl_mime_data_cb(part, lSize, ReadCallback, SeekCallback, NULL, pFile);

        part = curl_mime_addpart(multipart);
        curl_mime_name(part, "submit");
        curl_mime_data(part, "Install OS", CURL_ZERO_TERMINATED);

        curl_easy_setopt(pCurl, CURLOPT_MIMEPOST, multipart);

        // This is a long upload, disable the timeout
        curl_easy_setopt(pCurl, CURLOPT_TIMEOUT, 0L);

        // Create headers
        headerList = curl_slist_append(headerList, ("Authorization: Bearer " + accessToken).c_str());
        headerList = curl_slist_append(headerList, "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");

        curl_easy_setopt(pCurl, CURLOPT_HTTPHEADER, headerList);

        curl_easy_setopt(pCurl, CURLOPT_XFERINFOFUNCTION, TransferInfo);

        transProgress.curl = pCurl;
        curl_easy_setopt(pCurl, CURLOPT_XFERINFODATA, &transProgress);

        curl_easy_setopt(pCurl, CURLOPT_NOPROGRESS, 0L);

        // Now send the message
        res = curl_easy_perform(pCurl);

        fclose(pFile);
        curl_slist_free_all(headerList);
        curl_mime_free(multipart);

        if (res == CURLE_OK)
        {
            cout << "Firmware file successfully uploaded" << endl;
        }
        else
        {
            cout << "curl result ERROR = <" + to_string(res) + ": " + curl_easy_strerror(res) + ">" << endl;
            cout << "Failed to upload firmware file" << endl;
        }

        curl_easy_cleanup(pCurl);
    }

    return 0;
}
...