Why does file upload stop after sending a couple of chunks of data (using multipart/form-data)?

一世执手 提交于 2019-12-13 03:49:33

问题


I'm using libcurl to upload a firmware file to a hardware device. I'm using multipart/form-data and it looks like the file upload starts okay but it doesn't load the entire file.
The file I'm uploading is 144,855,725 bytes but it appears that only two 64k chunks are being sent.

In the posted code I am using a read callback function. I had also tried just passing the file name to the curl_mime_filedata function and the results were the same.
One interesting note is that when I run the program I'll get a successful response from the curl_easy_perform about half of the time. The other half of the time I'll get error 56 "Failure when receiving data from the peer". Another interesting note is that there is a slight discrepancy in the size of the file (144,855,725) and the size that curl perceives as the download size (144,856,042). I assume this is because it is accounting for all of the bytes in the body (not just the file). Is that correct?

Here is some (pared down) output from when I run the program.
File size: 144855725
TOTAL TIME: 0.000092
UP: 0 of 0 DOWN: 0 of 0
UP: 0 of 144856042 DOWN: 0 of 0
ReadCallback: Size=1, Nmemb=65267
We read 65267 bytes from the file
UP: 65536 of 144856042 DOWN: 0 of 0
ReadCallback: Size=1, Nmemb=65536
We read 65536 bytes from the file
UP: 131072 of 144856042 DOWN: 0 of 0
curl result ERROR = <56: Failure when receiving data from the peer>
Failed to upload firmware file

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);
}

I expect the entire file to be uploaded rather than just a couple of chunks of the file.


回答1:


You are not populating the curl_mime structure correctly. The code you have shown does not match the MIME format you described in your previous question:

--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--

Try this instead:

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;
}


来源:https://stackoverflow.com/questions/55409192/why-does-file-upload-stop-after-sending-a-couple-of-chunks-of-data-using-multip

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!