LinuxC语言实现上传功能(curl)

该程序是对接阿里云时做得,上传其他平台可能会略有差异,请注意!

直接上代码(注意参数,最好能看懂上传的大致流程,其实也是用curl):

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "curl.h"
#include "util.h"

static char AccessKeyIdG[64] = {0};
static char SecretAccessKeyG[64] = {0};
static char Server_addr[64] = {0};
static char Bucket[64] = {0};

#define CONFIGINI	"/mnt/mtd/SystemConfig.ini"


time_t start_time,end_time;

#define S3_METADATA_HEADER_NAME_PREFIX     "x-amz-meta-"

#define S3_MAX_METADATA_COUNT \
    (2048 / (sizeof(S3_METADATA_HEADER_NAME_PREFIX "nv") - 1))
#define COMPACTED_METADATA_BUFFER_SIZE \
		(2048 * sizeof(S3_METADATA_HEADER_NAME_PREFIX "n: v"))

// Declare a string multibuffer with the given name of the given maximum size
#define string_multibuffer(name, size)                                  \
    char name[size];                                                    \
    int name##Size

typedef struct _RequestComputedValues
{

    char host[80];

    char uri[128];

    int fileLen; // 要传输文件的长度
    // All x-amz- headers, in normalized form (i.e. NAME: VALUE, no other ws)
    char *amzHeaders[S3_MAX_METADATA_COUNT + 2]; // + 2 for acl and date

    // The number of x-amz- headers
    int amzHeadersCount;

    // Storage for amzHeaders (the +256 is for x-amz-acl and x-amz-date)
    char amzHeadersRaw[COMPACTED_METADATA_BUFFER_SIZE + 256 + 1];

    // URL-Encoded key
    char urlEncodedKey[60];

    string_multibuffer(canonicalizedAmzHeaders,
                       COMPACTED_METADATA_BUFFER_SIZE + 256 + 1);

    // Canonicalized resource
    char canonicalizedResource[128];

    // Content-Type header (or empty)
    char contentTypeHeader[2];

    // Content-MD5 header (or empty)
    char md5Header[2];
    // Authorization header
    char authorizationHeader[128];
} RequestComputedValues;


int Config_Read_String_Params(const char* lpSessionName,const char* lpKeyName,char* lpOutBuff)
{
    int 		value;
    int			ret;
    char		szStr[300];
    ret = GetProfileString(CONFIGINI,lpSessionName,lpKeyName,szStr,300);
    if(ret == -1)
    {
        ret = GetProfileInteger(CONFIGINI,lpSessionName,lpKeyName,&value);
        if(ret == -1)
        {

            printf("Load [%s] config [%s] error\n",lpSessionName,lpKeyName);

            return -1;
        }
        else
        {
            sprintf(lpOutBuff,"%d",value);
            printf("Load [%s][%s] = [%s]\n",lpSessionName,lpKeyName,lpOutBuff);
        }
    }
    else
    {
        sprintf(lpOutBuff,"%s",szStr);
        printf("Load [%s][%s] = [%s]\n",lpSessionName,lpKeyName,lpOutBuff);
    }
    return 0;
}
int checkout_server_addr()
{
    int ret = 0;

    Config_Read_String_Params("Tutk","AccessKeyIdG",AccessKeyIdG);
    if(strlen(AccessKeyIdG) == 0)
    {
        printf("AccessKeyIdG : NULL \n");
        return 0;
    }
    else
    {
        printf("AccessKeyIdG :%s\n",AccessKeyIdG);
    }
    
    Config_Read_String_Params("Tutk","SecretAccessKeyG",SecretAccessKeyG);
    if(strlen(SecretAccessKeyG) == 0)
    {
        printf("SecretAccessKeyG : NULL \n");
        return 0;
    }
    else
    {
        printf("SecretAccessKeyG :%s\n",SecretAccessKeyG);
    }
    
    Config_Read_String_Params("Tutk","Server_addr",Server_addr);
    if(strlen(Server_addr) == 0)
    {
        printf("Server_addr : NULL \n");
        return 0;
    }
    else
    {
        printf("Server_addr :%s\n",Server_addr);
    }
    
    Config_Read_String_Params("Tutk","Bucket",Bucket);
    if(strlen(Bucket) == 0)
    {
        printf("Bucket : NULL \n");
        return 0;
    }
    else
    {
        printf("Bucket :%s\n",Bucket);
    }
    
    return 1;
}

static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp)
{
    printf("[%s:%d] size[%d] nmemb[%d]\n", __FUNCTION__, __LINE__, size, nmemb);
    FILE *fp = (FILE *)userp;
    if(!fp)
    {
        printf("[%s:%d] file point fp NULL\n", __FUNCTION__, __LINE__);
    }
    if(size*nmemb < 1)
    {
        printf("read_callback error, size[%d] nmemb[%d]\n", size, nmemb);
        return 0;
    }
    end_time = time(NULL);
    if((end_time - start_time) > 120)
    {
        printf("sending overtime:%d   close file...\n", end_time - start_time);
        close(fp);
        return 0;
    }
    else
    {
        printf("sending...:%ld\n", end_time);
        size_t retcode = fread(ptr, size, nmemb, fp);
        printf("read_callback fread retcode:%d\n", retcode);
        return retcode;
    }
}

int init_amz_header(RequestComputedValues *values)
{
    int len = 0;
    // Append a header to amzHeaders, trimming whitespace from the end.
    // Does NOT trim whitespace from the beginning.
#define headers_append(isNewHeader, format, ...)                        \
	do {                                                                \
	    if (isNewHeader) {                                              \
	        values->amzHeaders[values->amzHeadersCount++] =             \
	            &(values->amzHeadersRaw[len]);                          \
	    }                                                               \
	    len += snprintf(&(values->amzHeadersRaw[len]),                  \
	                    sizeof(values->amzHeadersRaw) - len,            \
	                    format, __VA_ARGS__);                           \
	    if (len >= (int) sizeof(values->amzHeadersRaw)) {               \
	        return -1;                      \
	    }                                                               \
	    while ((len > 0) && (values->amzHeadersRaw[len - 1] == ' ')) {  \
	        len--;                                                      \
	    }                                                               \
	    values->amzHeadersRaw[len++] = 0;                               \
	} while (0)

    //headers_append(1, "x-amz-server-side-encryption: %s", "AES256");
    // Add the x-amz-date header
    time_t now = time(NULL);
    char date[64];
    struct tm *gmt;
    gmt = gmtime(&now);
    strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S GMT", gmt);
    headers_append(1, "Date: %s", date);
}

#define  MAX_MSG_LEN 1024
static int aws_authorizationHeader(RequestComputedValues *values)
{
    printf("[%s][%s][%d]: \n",__FILE__,__FUNCTION__,__LINE__);

    // We allow for:
    // 17 bytes for HTTP-Verb + \n
    // 129 bytes for Content-MD5 + \n
    // 129 bytes for Content-Type + \n
    // 1 byte for empty Date + \n
    // CanonicalizedAmzHeaders & CanonicalizedResource
    char signbuf[17 + 129 + 129 + 1 +
                 (sizeof(values->canonicalizedAmzHeaders) - 1) +
                 (sizeof(values->canonicalizedResource) - 1) + 1] = {0};
    int len = 0;

#define signbuf_append(format, ...)                             \
    len += snprintf(&(signbuf[len]), sizeof(signbuf) - len,     \
                    format, __VA_ARGS__)

    signbuf_append("%s\n", "PUT");

    // For MD5 and Content-Type, use the value in the actual header, because
    // it's already been trimmed
    signbuf_append("%s\n", values->md5Header[0] ?
                   &(values->md5Header[sizeof("Content-MD5: ") - 1]) : "");
    /*
    signbuf_append("%s\n", "video/avi");
    sprintf(values->contentTypeHeader, "Content-Type: %s", "video/avi");
    */

    signbuf_append
    ("%s\n", values->contentTypeHeader[0] ?
     &(values->contentTypeHeader[sizeof("Content-Type:") - 1]) : "");

    //signbuf_append("%s", values->canonicalizedAmzHeaders);
    char date[128] = {0};
    //sprintf(values->canonicalizedAmzHeaders, "Date: %s", date);
    memcpy(date, &values->canonicalizedAmzHeaders[sizeof("Date:")-1], sizeof(date));
    signbuf_append("%s", date);
    signbuf_append("%s", values->canonicalizedResource);

    // Generate an HMAC-SHA-1 of the signbuf
    unsigned char hmac[20];

    OSS_HMAC_SHA1(hmac, (unsigned char *) SecretAccessKeyG,
                  strlen(SecretAccessKeyG),
                  (unsigned char *) signbuf, len);

    //DPRINT("HMAC_SHA1 hmac:[%s]\n", hmac);


    // Now base-64 encode the results
    char b64[((20 + 1) * 4) / 3];
    int b64Len = oss_base64Encode(hmac, 20, b64);
    //DPRINT("base64Encode hmac:[%s] b64[%s]\n", hmac, b64);
    snprintf(values->authorizationHeader, sizeof(values->authorizationHeader),
             "Authorization: OSS %s:%.*s", AccessKeyIdG,
             b64Len, b64);

    printf("authorizationHeader:[%s]\n", values->authorizationHeader);

    printf("signbuf:{\n%s\n}\n", signbuf);


    return 1;
}

// Compose the URI to use for the request given the request parameters
static int compose_uri(char *buffer, int bufferSize,
                       const char *urlEncodedKey)
{
    int len = 0;

#define uri_append(fmt, ...)                                                 \
    do {                                                                     \
        len += snprintf(&(buffer[len]), bufferSize - len, fmt, __VA_ARGS__); \
        if (len >= bufferSize) {                                             \
            return -1;                                       \
        }                                                                    \
    } while (0)

    //uri_append("http%s://", (bucketContext->protocol == S3ProtocolHTTP) ? "" : "s");
    uri_append("http%s://", "");

    uri_append("%s.%s", Bucket, Server_addr);

    uri_append("%s", "/");

    uri_append("%s", urlEncodedKey);

    return 1;
}

// Simple comparison function for comparing two HTTP header names that are
// embedded within an HTTP header line, returning true if header1 comes
// before header2 alphabetically, false if not
static int headerle(const char *header1, const char *header2)
{
    while (1)
    {
        if (*header1 == ':')
        {
            return (*header2 != ':');
        }
        else if (*header2 == ':')
        {
            return 0;
        }
        else if (*header2 < *header1)
        {
            return 0;
        }
        else if (*header2 > *header1)
        {
            return 1;
        }
        header1++, header2++;
    }
}


// Replace this with merge sort eventually, it's the best stable sort.  But
// since typically the number of elements being sorted is small, it doesn't
// matter that much which sort is used, and gnome sort is the world's simplest
// stable sort.  Added a slight twist to the standard gnome_sort - don't go
// forward +1, go forward to the last highest index considered.  This saves
// all the string comparisons that would be done "going forward", and thus
// only does the necessary string comparisons to move values back into their
// sorted position.
static void header_gnome_sort(const char **headers, int size)
{
    int i = 0, last_highest = 0;

    while (i < size)
    {
        if ((i == 0) || headerle(headers[i - 1], headers[i]))
        {
            i = ++last_highest;
        }
        else
        {
            const char *tmp = headers[i];
            headers[i] = headers[i - 1];
            headers[--i] = tmp;
        }
    }
}

static void canonicalize_amz_headers(RequestComputedValues *values)
{
    // Make a copy of the headers that will be sorted
    const char *sortedHeaders[S3_MAX_METADATA_COUNT];

    memcpy(sortedHeaders, values->amzHeaders,
           (values->amzHeadersCount * sizeof(sortedHeaders[0])));

    // Now sort these
    header_gnome_sort(sortedHeaders, values->amzHeadersCount);

    // Now copy this sorted list into the buffer, all the while:
    // - folding repeated headers into single lines, and
    // - folding multiple lines
    // - removing the space after the colon
    int lastHeaderLen = 0, i;
    char *buffer = values->canonicalizedAmzHeaders;
    for (i = 0; i < values->amzHeadersCount; i++)
    {
        const char *header = sortedHeaders[i];
        const char *c = header;
        // If the header names are the same, append the next value
        if ((i > 0) &&
                !strncmp(header, sortedHeaders[i - 1], lastHeaderLen))
        {
            // Replacing the previous newline with a comma
            *(buffer - 1) = ',';
            // Skip the header name and space
            c += (lastHeaderLen + 1);
        }
        // Else this is a new header
        else
        {
            // Copy in everything up to the space in the ": "
            while (*c != ' ')
            {
                *buffer++ = *c++;
            }
            // Save the header len since it's a new header
            lastHeaderLen = c - header;
            // Skip the space
            c++;
        }
        // Now copy in the value, folding the lines
        while (*c)
        {
            // If c points to a \r\n[whitespace] sequence, then fold
            // this newline out
            if ((*c == '\r') && (*(c + 1) == '\n') && is_blank(*(c + 2)))
            {
                c += 3;
                while (is_blank(*c))
                {
                    c++;
                }
                // Also, what has most recently been copied into buffer amy
                // have been whitespace, and since we're folding whitespace
                // out around this newline sequence, back buffer up over
                // any whitespace it contains
                while (is_blank(*(buffer - 1)))
                {
                    buffer--;
                }
                continue;
            }
            *buffer++ = *c++;
        }
        // Finally, add the newline
        *buffer++ = '\n';
    }

    // Terminate the buffer
    *buffer = 0;
}

void init_header(RequestComputedValues *values, char *filename, int filesize)
{
    if(!filename)
    {
        printf("filename NULL, error\n");
        return ;
    }
    //memset(&rq_computed, 0, sizeof(rq_computed));
    values->fileLen = filesize;
    init_amz_header(values);

    //set Url
    snprintf(values->host, sizeof(values->host),
             "%s.%s", Bucket, Server_addr);
    printf("init_header values->host[%s]\n", values->host);
    //filename
    snprintf(values->urlEncodedKey, sizeof(values->urlEncodedKey),
             "%s", filename);
    printf("init_header values->urlEncodedKey[%s]\n", values->urlEncodedKey);

    canonicalize_amz_headers(values);
    printf("canonicalize_amz_headers:[%s]\n", values->canonicalizedAmzHeaders);
    //设置上传虚拟地址
    snprintf(values->canonicalizedResource, sizeof(values->canonicalizedResource),
             "/%s/%s", Bucket, values->urlEncodedKey);

    aws_authorizationHeader(values);


    compose_uri(values->uri, sizeof(values->uri), values->urlEncodedKey);

}

int setup_url(RequestComputedValues *values, CURL *curl)
{
    printf("[%s][%s][%d]: \n",__FILE__,__FUNCTION__,__LINE__);
    int status;
#define curl_easy_setopt_safe(opt, val)                                 \
	if ((status = curl_easy_setopt										\
		 (curl, opt, val)) != CURLE_OK) {						\
		return status;						\
	}

    // Ask curl to parse the Last-Modified header.  This is easier than
    // parsing it ourselves.

    printf("[%s][%s][%d]: \n",__FILE__,__FUNCTION__,__LINE__);
    struct curl_slist *headers_slist = 0;
    // Append standard headers
#define append_standard_header(fieldName)                               \
    if (values-> fieldName [0]) {                                       \
        headers_slist = curl_slist_append(headers_slist,          \
                                             values-> fieldName);       \
    }

    append_standard_header(contentTypeHeader);
    append_standard_header(md5Header);
    // Append x-amz- headers
    int i;
    for (i = 0; i < values->amzHeadersCount; i++)
    {
        printf("values->amzHeaders[%d]:%s\n", i, values->amzHeaders[i]);
        headers_slist =
            curl_slist_append(headers_slist, values->amzHeaders[i]);
    }

    append_standard_header(authorizationHeader);

    {
        char header[256];
        snprintf(header, sizeof(header), "Content-Length: %llu",
                 (unsigned long long) values->fileLen);
        headers_slist = curl_slist_append(headers_slist, header);
        headers_slist = curl_slist_append(headers_slist, "Transfer-Encoding:");
        headers_slist = curl_slist_append(headers_slist, "Expect:");
    }


    // Set the HTTP headers
    curl_easy_setopt_safe(CURLOPT_HTTPHEADER, headers_slist);

    curl_easy_setopt_safe(CURLOPT_URL, values->uri);

    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 120);//设置超时时间

    // Set request type.
    curl_easy_setopt_safe(CURLOPT_UPLOAD, 1);

    printf("[%s][%s][%d]: \n",__FILE__,__FUNCTION__,__LINE__);
    return 1;

}

int _upload(char *avi_filename,char *upload_path)
{
    CURL *curl;
    CURLcode res;

    struct stat file_info;
    char filename[60] = {0};
    FILE *out = NULL;

    printf("----------avi_filename: %s\n",avi_filename);
    printf("----------upload_path: %s\n",upload_path);

    if(!checkout_server_addr())
    {
        printf("checkout_server_addr failed!\n");
        return -1;
    }
    printf("checkout_server_addr successfully!\n");

    if(avi_filename != NULL)
    {
        strcpy(filename, avi_filename);
        out = fopen(filename, "rb");
        if(!out)
        {
            printf("fopen filename %s failed\n", filename);
            return -1;
        }
    }
    else
    {
        printf("avi_filename is NULL!\n");
        return -1;
    }
    stat(filename, &file_info);
    int uploadsize = file_info.st_size;

    /* 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));

        fclose(out);

        return -1;
    }

    RequestComputedValues rq_computed;
    memset(&rq_computed, 0, sizeof(rq_computed));

    char object_name[128] = {0};
    //strcpy(object_name, upload_path);
    sprintf(object_name, "%s", upload_path);
    printf("----------object_name: %s\n",object_name);
    printf("----------uploadsize: %d\n",uploadsize);

    init_header(&rq_computed, object_name, uploadsize);

    /* get a curl handle */
    curl = curl_easy_init();
    if(curl)
    {
        printf("url:%s\n", rq_computed.uri);
        /* First set the URL that is about to receive our POST. */
        //curl_easy_setopt(curl, CURLOPT_URL, url);

        setup_url(&rq_computed, curl);

#if 0
        /* enable uploading */
        curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);

        /* HTTP PUT please */
        curl_easy_setopt(curl, CURLOPT_PUT, 1L);
#endif

        /* 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, out);

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

        /* Perform the request, res will get the return code */
        start_time = time(NULL);
        printf("start_time:%ld\n", start_time);

        res = curl_easy_perform(curl);
        /* Check for errors */
        if(res != CURLE_OK)                     //传输失败
        {
            fprintf(stderr, "curl_easy_perform() failed: %s\n",
                    curl_easy_strerror(res));

            fclose(out);
            curl_easy_cleanup(curl);
            curl_global_cleanup();
            
            return -1;                                      
        }
        /* always cleanup */
        curl_easy_cleanup(curl);
    }
    curl_global_cleanup();
    printf("send finish at time:%ld\n", time(NULL));

    fclose(out);

    return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值