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

最近在项目中需要在嵌入式终端上传录音文件,最后选择了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;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流媒体程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值