您的位置:首页 > 理论基础 > 计算机网络

cur 以post的方式进行http上传文件(自定义http head)

2017-11-13 10:06 465 查看
最近在项目中需要在嵌入式终端上传录音文件,最后选择了curl来支持该项目,网上现有的一些资源比较简单,并没能满足需求,自己结合curl的example做了改进,具备一下功能:

(1)以post的方式上传文件,并对http head进行了自定义;

(2)文件名封装在附加的文件头里面。

该代码已经测试通过。

/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.haxx.se/docs/copyright.html. *
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
/* <DESC>
* Issue an HTTP POST and provide the data through the read callback.
* </DESC>
*/
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>

typedef enum file_part
{
FILE_HEAD_PART = 0,
FILE_CONTENT_PART,
FILE_END_PART,
FILE_TRANSFER_FINISH,
}E_FilePart;

typedef struct http_upload
{
FILE* rFile; // 上传的文件
size_t fileSizeLeft; // 文件剩余字节
size_t filePos; // 文件内容位置
char *headerPtr; // 附加的文件起始头,主要用于(1)告知服务器上传文件的名字;(2)一些自定义参数
size_t headerSizeLeft; // 附加的文件起始头剩余字节
char *endPtr; // 附加的文件结束尾
size_t endSizeLeft; // 附加的文件结束尾剩余字节
E_FilePart filePart; // 0 传输HeaderInfo, 1 传输文件, 2 传输EndInfo, -1则结束
size_t totalSize; // 总共传输的字节数,附加的文件起始头+文件+附加的文件结束尾
}T_HttpUpload;

#define HTTP_HEAD_MAX_SIZE (256)
#define HTTP_END_MAX_SIZE (1024)

const char BOUNDARY[] = {"---WebKitFormBoundary"}; // 自定义的附加文件起始头
static char sHeaderInfo[HTTP_HEAD_MAX_SIZE];
static char sEndInfo[HTTP_END_MAX_SIZE];

static size_t read_callback(void *dest, size_t size, size_t nmemb, void *userp)
{
T_HttpUpload *httpUpload = (T_HttpUpload *)userp;
size_t bufferSize = size*nmemb;
size_t copyThisMuch;

printf("file left %zu, %zu, %zu, size*nmemb = %zu, filePart = %d, totalSize = %zu\n",
httpUpload->headerSizeLeft, httpUpload->fileSizeLeft,
httpUpload->endSizeLeft, bufferSize, httpUpload->filePart,
httpUpload->totalSize);

if(FILE_HEAD_PART == httpUpload->filePart)
{
copyThisMuch = httpUpload->headerSizeLeft;
if(copyThisMuch > bufferSize)
copyThisMuch = bufferSize;
memcpy(dest, httpUpload->headerPtr, copyThisMuch);

httpUpload->headerPtr += copyThisMuch;
httpUpload->headerSizeLeft -= copyThisMuch;
if(httpUpload->headerSizeLeft <= 0)
{
httpUpload->filePart = FILE_CONTENT_PART;
}
httpUpload->totalSize += copyThisMuch;
return copyThisMuch;
}
else if(FILE_CONTENT_PART == httpUpload->filePart)
{
copyThisMuch = httpUpload->fileSizeLeft;
if(copyThisMuch > bufferSize)
copyThisMuch = bufferSize;
copyThisMuch = fread(dest, 1, copyThisMuch, httpUpload->rFile);

httpUpload->filePos += copyThisMuch;
httpUpload->fileSizeLeft -= copyThisMuch;
if(httpUpload->fileSizeLeft <= 0)
{
httpUpload->filePart = FILE_END_PART;
}
httpUpload->totalSize += copyThisMuch;
return copyThisMuch;
}
else if(FILE_END_PART == httpUpload->filePart)
{
copyThisMuch = httpUpload->endSizeLeft;
if(copyThisMuch > bufferSize)
copyThisMuch = bufferSize;
memcpy(dest, httpUpload->endPtr, copyThisMuch);

httpUpload->endPtr += copyThisMuch;
httpUpload->endSizeLeft -= copyThisMuch;
if(httpUpload->endSizeLeft <= 0)
{
httpUpload->filePart = FILE_TRANSFER_FINISH;
}
httpUpload->totalSize += copyThisMuch;
return copyThisMuch;
}
else
{
printf("file transfer finish, totalSize = %zu .............\n", httpUpload->totalSize);
}

return 0;
}

int HttpUploadFile(const char* url, const char* fileName)
{
CURL *curl;
CURLcode res;
char chunkString[256];
size_t totalSize;

/* In windows, this will init the winsock stuff */
res = curl_global_init(CURL_GLOBAL_DEFAULT);
/* Check for errors */
if(res != CURLE_OK)
{
fprintf(stderr, "curl_global_init() failed: %s\n",
curl_easy_strerror(res));
return 1;
}

/* get a curl handle */
curl = curl_easy_init();
if(curl)
{
T_HttpUpload httpUpload;
memset(&httpUpload, sizeof(httpUpload), 0);
httpUpload.filePart = FILE_HEAD_PART;
httpUpload.totalSize = 0;

memset(sHeaderInfo, 0 , HTTP_HEAD_MAX_SIZE);
// 设置起始文件头
sprintf(sHeaderInfo, "%s%s%s%s%s%s%s%s%s%s", "--", BOUNDARY, "\r\n",
"Content-Disposition: form-data; name=\"", "file", "\"; filename=\"", fileName, "\"", "\r\n",
"\r\n");
httpUpload.headerPtr = sHeaderInfo;
httpUpload.headerSizeLeft = strlen(httpUpload.headerPtr);

// 设置结束文件信息
memset(sEndInfo, 0 , HTTP_END_MAX_SIZE);
sprintf(sEndInfo, "%s%s%s","\r\n--", BOUNDARY, "--\r\n" );
httpUpload.endPtr = sEndInfo;
httpUpload.endSizeLeft = strlen(httpUpload.endPtr);

printf("\n\nHeaderInfo = :%s\n", httpUpload.headerPtr);
printf("HeaderInfo End\n");

printf("\nEndInfo = :%s\n", httpUpload.endPtr);
printf("EndInfo End\n");

//获取要上传的文件指针
httpUpload.rFile = fopen(fileName, "rb");
if (0 == httpUpload.rFile)
{
printf("the file %s that you will read is not exist", fileName);
return -1;
}

// 获取文件大小
fseek(httpUpload.rFile, 0, 2);
httpUpload.filePos = 0;
httpUpload.fileSizeLeft = ftell(httpUpload.rFile);
rewind(httpUpload.rFile);

/* First set the URL that is about to receive our POST. */
curl_easy_setopt(curl, CURLOPT_URL, url);

struct curl_slist *chunk=NULL;
/* Remove a header curl would otherwise add by itself */
//chunk = curl_slist_append(chunk, "Accept:");

/* Add a custom header */
//chunk = curl_slist_append(chunk, "Another: yes");
sprintf(chunkString, "%s%s%s", "Content-Type: ", "multipart/form-data; boundary=", BOUNDARY); // 重新设置Content-Type

chunk = curl_slist_append(chunk, chunkString);

// 上传的文件总长度, 服务器需要剥离附加文件头,以及附加文件尾,中间部分才是文件的实际内容;服务器可以通过文件的文件头解析出文件名字
totalSize = httpUpload.headerSizeLeft + httpUpload.fileSizeLeft + httpUpload.endSizeLeft;
sprintf(chunkString, "%s%zu","Content-Length: ", totalSize);
chunk = curl_slist_append(chunk, chunkString);

/* Modify a header curl otherwise adds differently */
//chunk = curl_slist_append(chunk, "Host: example.com");

/* Add a header with "blank" contents to the right of the colon. Note that
we're then using a semicolon in the string we pass to curl! */
//chunk = curl_slist_append(chunk, "X-silly-header;");

/* set our custom set of headers */
res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);

/* Now specify we want to POST data */
curl_easy_setopt(curl, CURLOPT_POST, 1L);

/* we want to use our own read function */
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);

/* pointer to pass to our read function */
curl_easy_setopt(curl, CURLOPT_READDATA, &httpUpload);

/* get verbose debug output please */
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

/*
If you use POST to a HTTP 1.1 server, you can send data without knowing
the size before starting the POST if you use chunked encoding. You
enable this by adding a header like "Transfer-Encoding: chunked" with
CURLOPT_HTTPHEADER. With HTTP 1.0 or without chunked transfer, you must
specify the size in the request.
*/

/* Set the expected POST size. If you want to POST large amounts of data,
consider CURLOPT_POSTFIELDSIZE_LARGE */
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)(httpUpload.headerSizeLeft + httpUpload.fileSizeLeft + httpUpload.endSizeLeft));

/* Perform the request, res will get the return code */
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));

/* always cleanup */
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}

int main(int argc, char *argv[])
{
if(argc < 2)
{
printf("no input file name ...... \n");
return -1;
}
if(HttpUploadFile("换成自己的URL地址", argv[1]) != 0)
{
printf("HttpUploadFile failed\n");
}

return 0;
}



                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  curl post http head
相关文章推荐