目的:方便某些不能使用官方SDK的地方。
代码:
#pragma once
#include <vector>
#include <string>
#include <curl/curl.h>
#include <curl/easy.h>
#include <boost/thread.hpp>
//Access Key ID
#define OBS_AK "xxx"
//Secret Access Key
#define OBS_SK "xxx"
//OBS存储地址
#define OBS_URL "http://xxx.com"
class InitCurlClass
{
public:
static InitCurlClass& Instance()
{
static InitCurlClass obj;
return obj;
}
private:
InitCurlClass()
{
curl_global_init(CURL_GLOBAL_ALL);
}
~InitCurlClass()
{
curl_global_cleanup();
}
};
class OBSUploader
{
struct http_req_t
{
char* head;
char* tail;
};
public:
OBSUploader();
~OBSUploader();
bool UploadImage(std::string path, const char* data, size_t len);
bool Reinit(std::string& url, std::string& bucket, std::string akey = OBS_AK, std::string skey = OBS_SK);
void Release();
private:
static size_t WriteDataCallback(void *buffer, size_t size, size_t nmemb, void *data);
static size_t ReadDataCallback(void *buffer, size_t size, size_t nmemb, void *data);
std::string GetFormatDate();
std::string GetAuth(std::string& path, std::string& date, std::string type);
bool UploadData(std::string& url, std::vector<std::string>& header, const char* data, size_t len);
private:
bool m_Valied;
std::string m_AccessKey;
std::string m_SecretKey;
std::string m_StoreUrl;
std::string m_BucketName;
boost::recursive_mutex m_Mutex;
};
#include "logger/log4z.h"
#include <boost/uuid/sha1.hpp>
#include <boost/format.hpp>
#include <boost/date_time.hpp>
#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/binary_from_base64.hpp>
#include <boost/archive/iterators/transform_width.hpp>
#include <openssl/hmac.h>
#include <openssl/evp.h>
typedef boost::lock_guard<boost::recursive_mutex> GuardLock;
bool HmacSha1(std::string& key, std::string& data, std::string* digest)
{
uint8_t result[EVP_MAX_MD_SIZE] = { '\0' };
uint32_t result_len = 0;
const unsigned char* idata = (const unsigned char*)data.c_str();
//唯一用的openssl接口的地方
HMAC(EVP_sha1(), key.c_str(), key.size(), idata, data.size(), result, &result_len);
digest->assign((const char *)result, result_len);
return true;
}
bool Base64Encode(std::string * outPut, const std::string & inPut)
{
typedef boost::archive::iterators::base64_from_binary<
boost::archive::iterators::transform_width<
std::string::const_iterator, 6, 8> > Base64EncodeIter;
std::stringstream result;
copy(Base64EncodeIter(inPut.begin()),
Base64EncodeIter(inPut.end()),
std::ostream_iterator<char>(result));
size_t Num = (3 - inPut.length() % 3) % 3;
for (size_t i = 0; i < Num; i++)
{
result.put('=');
}
*outPut = result.str();
return outPut->empty() == false;
}
bool Base64Decode(std::string * outPut, const std::string & inPut)
{
typedef boost::archive::iterators::transform_width<
boost::archive::iterators::binary_from_base64<
std::string::const_iterator>, 8, 6> Base64DecodeIter;
std::stringstream result;
try
{
copy(Base64DecodeIter(inPut.begin()),
Base64DecodeIter(inPut.end()),
std::ostream_iterator<char>(result));
}
catch (...)
{
return false;
}
*outPut = result.str();
return outPut->empty() == false;
}
bool OBSUploader::UploadImage(std::string path, const char* data, size_t len)
{
GuardLock lock(m_Mutex);
if (m_Valied)
{
std::string url = m_StoreUrl + path;
std::string furl = "/" + m_BucketName + path;
std::string akey = m_AccessKey;
std::string skey = m_SecretKey;
std::string type("image/jpeg");
std::string date(GetFormatDate());//RFC 1123
std::string auth(GetAuth(furl, date, type));
std::vector<std::string> header;
header.push_back("Authorization: " + auth);
header.push_back("Date: " + date);
header.push_back("Content-Type: " + type);
return UploadData(url, header, data, len);
}
return false;
}
bool OBSUploader::Reinit(std::string& url, std::string& bucket, std::string akey /*= OBS_AK*/, std::string skey /*= OBS_SK*/)
{
GuardLock lock(m_Mutex);
m_StoreUrl = url;
m_BucketName = bucket;
m_AccessKey = akey;
m_SecretKey = skey;
if (m_StoreUrl.empty() || m_BucketName.empty())
m_Valied = false;
else
m_Valied = true;
return m_Valied;
}
void OBSUploader::Release()
{
GuardLock lock(m_Mutex);
m_Valied = false;
}
OBSUploader::OBSUploader()
: m_Valied(false)
, m_AccessKey(OBS_AK)
, m_SecretKey(OBS_SK)
, m_StoreUrl(OBS_URL)
{
InitCurlClass::Instance();
}
OBSUploader::~OBSUploader()
{
Release();
}
size_t OBSUploader::WriteDataCallback(void *buffer, size_t size, size_t nmemb, void *data)
{
if (data == NULL)
{
LOGFMTW("[OBSUploader::WriteDataCallback] Discard responsing data.");
return 0;
}
size_t count = nmemb * size;
std::string* strBuffer = (std::string*)data;
strBuffer->append((char *)buffer, count);
return count;
}
size_t OBSUploader::ReadDataCallback(void *buffer, size_t size, size_t nmemb, void *data)
{
http_req_t* req_ptr = (http_req_t*)data;
size_t buf_size = nmemb * size;
size_t ready_len = req_ptr->tail - req_ptr->head;
if (data == NULL || ready_len == 0)
{
LOGFMTW("[OBSUploader::ReadDataCallback] No data to upload.");
return 0;
}
size_t len = std::min<size_t>(buf_size, ready_len);
::memcpy(buffer, req_ptr->head, len);
req_ptr->head += len;
return len;
}
std::string OBSUploader::GetFormatDate()
{
boost::posix_time::ptime pt
= boost::posix_time::second_clock::universal_time();
std::stringstream ss;
//std::locale responsible for releasing memory.
ss.imbue(std::locale(ss.getloc(),
new boost::posix_time::time_facet("%a, %d %b %Y %H:%M:%S")));//out
ss << pt << " GMT";
return ss.str();
}
std::string OBSUploader::GetAuth(std::string& path, std::string& date, std::string type)
{
std::string HTTPVerb = "PUT";
std::string ContentMD5 = "";
std::string ContentType = type;//image/jpeg
std::string Date = date;//
std::string CanonicalizedHeaders = "";//x-obs-acl:public-read
std::string CanonicalizedResource = path;
std::string sign = boost::str(boost::format("%s\n%s\n%s\n%s\n%s%s")
% HTTPVerb %ContentMD5 %ContentType %Date
%CanonicalizedHeaders %CanonicalizedResource);
std::string digest, auth;
HmacSha1(m_SecretKey, sign, &digest);
Base64Encode(&auth, digest);
return boost::str(boost::format("OBS %s:%s") % m_AccessKey %auth);
}
bool OBSUploader::UploadData(std::string& url, std::vector<std::string>& header, const char* data, size_t len)
{
CURL* hCurl = curl_easy_init();
if (!hCurl)
{
LOGFMTW("[OBSUploader::UploadData] Initialize Error. Url:%s.", url.c_str());
return false;
}
char * head = (char *)data;
char * tail = head + len;
struct http_req_t req = { head, tail };
struct curl_slist *HeadList = NULL;
for (int i = 0; i < header.size(); ++i)
{
HeadList = curl_slist_append(HeadList, header[i].c_str());
}
long hCode = 0;
std::string response;
curl_easy_setopt(hCurl, CURLOPT_VERBOSE, 0);//DEBUG:TRUE
curl_easy_setopt(hCurl, CURLOPT_NOSIGNAL, 1L);//多线程
curl_easy_setopt(hCurl, CURLOPT_HEADER, 0);//丢弃HTTP头部信息
curl_easy_setopt(hCurl, CURLOPT_TIMEOUT, 12);//12s
curl_easy_setopt(hCurl, CURLOPT_READFUNCTION, &ReadDataCallback);
curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, &WriteDataCallback);
curl_easy_setopt(hCurl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(hCurl, CURLOPT_PUT, 1L);
curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str());
curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, HeadList);
curl_easy_setopt(hCurl, CURLOPT_READDATA, &req);
curl_easy_setopt(hCurl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)(len));
curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, &response);
CURLcode res = curl_easy_perform(hCurl);
curl_easy_getinfo(hCurl, CURLINFO_RESPONSE_CODE, &hCode);
curl_slist_free_all(HeadList);
curl_easy_cleanup(hCurl);
if (hCode != 200 || res != CURLE_OK)
{
LOGFMTW("[OBSUploader::UploadData] Failed to put HTTP data. Url:%s. Code:%d, CURLcode:%d. Response:%s",
url.c_str(), hCode, res, response.c_str());
return false;
}
return true;
}