该程序是对接阿里云时做得,上传其他平台可能会略有差异,请注意!
直接上代码(注意参数,最好能看懂上传的大致流程,其实也是用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;
}