c++ 實現華爲雲對象存儲上傳數據

目的:方便某些不能使用官方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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章