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