天天看點

Qt 騰訊雲對象存儲騰訊雲對象存儲七牛雲對象存儲

Qt 騰訊雲對象存儲

  • 騰訊雲對象存儲
    • 開發資料
    • 上傳檔案
    • 線上調用工具
    • 上傳檔案MD5計算方式
  • 七牛雲對象存儲

騰訊雲對象存儲

公司需要把軟體日志檔案傳到服務端

對于 Qt 開發者來說當然不會用它們的 SDK 了,用是不可能用的,又不是不會coding

2021-01-27 看的文檔開始開發

開發資料

請求簽名 :https://cloud.tencent.com/document/product/436/7778

公共請求頭部:https://cloud.tencent.com/document/product/436/7778

上傳檔案

對,沒錯,上傳檔案用的叫 put object

上傳檔案到存儲桶,對沒錯,這個名字很 OK

put object:https://cloud.tencent.com/document/product/436/7749

為了搞這個上傳檔案 api, 搞了一天半才測試成功,一直吐槽文檔寫的跟奧利給一樣,氣的不行,第一天下班我還送出了工單,騰訊雲小姐姐還電話給我問我要錯誤傳回的參數,後來我自己調通了。

Qt 騰訊雲對象存儲騰訊雲對象存儲七牛雲對象存儲

putUrl(“1.txt”)

注意把裡面的 xxxx 都換成你自己申請的參數

/** hmac-sha1
 * @brief HttpClientClass::hmacSha1
 * @param key    秘鑰
 * @param baseString   消息
 * @param isBase64   是否傳回base64
 * @return
 */
QByteArray HttpClientClass::hmacSha1(QByteArray key, QByteArray baseString,bool isBase64)
{
    int blockSize = 64; // HMAC-SHA-1 block size, defined in SHA-1 standard
    if (key.length() > blockSize) { // if key is longer than block size (64), reduce key length with SHA-1 compression
        key = QCryptographicHash::hash(key, QCryptographicHash::Sha1);
    }
    QByteArray innerPadding(blockSize, char(0x36)); // initialize inner padding with char"6"
    QByteArray outerPadding(blockSize, char(0x5c)); // initialize outer padding with char"/"
    // ascii characters 0x36 ("6") and 0x5c ("/") are selected because they have large
    // Hamming distance (http://en.wikipedia.org/wiki/Hamming_distance)
    for (int i = 0; i < key.length(); i++) {
        innerPadding[i] = innerPadding[i] ^ key.at(i); // XOR operation between every byte in key and innerpadding, of key length
        outerPadding[i] = outerPadding[i] ^ key.at(i); // XOR operation between every byte in key and outerpadding, of key length
    }
    // result = hash ( outerPadding CONCAT hash ( innerPadding CONCAT baseString ) ).toBase64
    QByteArray total = outerPadding;
    QByteArray part = innerPadding;
    part.append(baseString);
    total.append(QCryptographicHash::hash(part, QCryptographicHash::Sha1));
    QByteArray hashed = QCryptographicHash::hash(total, QCryptographicHash::Sha1);
    if(isBase64){
        return hashed.toBase64();
    }
    return hashed;
}
bool HttpClientClass::putUrl(const QString & filePath)
{
    QFileInfo fileInfo(filePath);
    if(!fileInfo.exists()){
        SCWarning<<QString("Not found file: %1").arg(filePath);
        return false;
    }

    //put到騰訊雲
    //(存儲桶)Bucket:collection-xxxxxx
    //(區域)Region:ap-shanghai
    QString qiniuPostUrl = "https://collection-xxxxxx.cos.ap-shanghai.myqcloud.com";
    QString SecretId = "AKxxxxxxxxxxxxxxxxxxxxxxxxx";
    QString SecretKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx";

    QFile file (fileInfo.filePath());
    if(!file.open(QIODevice::ReadOnly)){
        SCWarning<<QString("Open file failed: %1:%2").arg(filePath).arg(file.errorString());
        return false;
    }
    QByteArray fileData = file.readAll();
    //使用md5值作為檔案的唯一名,保證伺服器上檔案名唯一
    QString md5file = QString(QCryptographicHash::hash( fileData, QCryptographicHash::Md5).toHex());
    //騰訊雲需要用的檔案名md5 (注意這裡的md5計算方式,這裡也卡了很久)
    QString fileMd5 = QString(QCryptographicHash::hash( fileData, QCryptographicHash::Md5).toBase64());
    SCDebug<<"fileMd5Str:"<<fileMd5;
    file.close();

    QString ContentLength = QString::number(fileData.size());
    QString ContentType = "text/plain";//text/plan
    //調試時,這裡可以改成固定名 QString ContentDisposition = fileInfo.fileName()
    QString ContentDisposition = QString(md5file+"_"+fileInfo.fileName());
    QNetworkRequest req;
    req.setRawHeader("Host","collection-xxxxxx.cos.ap-shanghai.myqcloud.com");
    req.setRawHeader("Content-Length",ContentLength.toLocal8Bit());
    req.setRawHeader("Content-Type", ContentType.toLocal8Bit());
    req.setRawHeader("Content-Disposition", ContentDisposition.toLocal8Bit());
    req.setRawHeader("Content-MD5", fileMd5.toLocal8Bit());
    SCDebug<<"ContentLength:"<<ContentLength;
    QUrl url(qiniuPostUrl+"/"+ContentDisposition);
    req.setUrl(url);
    //有效期
    QDateTime startDateTime = QDateTime::currentDateTime();
    QDateTime endDateTime = startDateTime.addSecs(3600);
    quint64 startTimeS =  startDateTime.toSecsSinceEpoch();
    quint64 endTimeS =  endDateTime.toSecsSinceEpoch();
    QString KeyTime = QString("%1;%2").arg(startTimeS).arg(endTimeS);
    //可以把日期改成,官方提供的測試工具日期一直,友善檢視自己的參數哪裡出錯了
    //    KeyTime = "1611800323;1611807523";
    SCDebug<<"KeyTime:"<<KeyTime;
    QByteArray SignKey = hmacSha1(SecretKey.toLocal8Bit(),KeyTime.toLocal8Bit(),false).toHex();
    SCDebug<<"SignKey:"<<QString(SignKey);
    QByteArray UrlParamList = QByteArray(); 
    QByteArray HttpParameters =  QByteArray();
    QMap<QString,QString>headMap;
    for(int i=0; i<req.rawHeaderList().size(); ++i){
        auto key = req.rawHeaderList().at(i);
        headMap.insert( key.toLower(),QUrl::toPercentEncoding(req.rawHeader(key)));
    }
    QByteArray  HeaderList = QString(headMap.keys().join(";")).toLocal8Bit();
    SCDebug<<"HeaderList:"<<QString(HeaderList);
    QByteArray HttpHeaders;
    for(int i=0; i<headMap.keys().size(); ++i){
        auto key = headMap.keys().at(i);
        HttpHeaders.append(key);
        HttpHeaders.append('=');
        HttpHeaders.append(headMap.value(key));
        if(i != headMap.keys().size() -1)
            HttpHeaders.append("&");
    }

    SCDebug<<"HttpHeaders:"<<HttpHeaders.size()<<QString(HttpHeaders);
    QString HttpURI = QString(QString("/%1").arg(ContentDisposition));
    //HttpParameters 這裡隻能用+号拼接,因為urlEncoded字元中可能出現%3這樣誤拼接
    QByteArray HttpString = QString("put\n"+HttpURI+"\n"+QString(HttpParameters)+"\n"+QString(HttpHeaders)+"\n").toLocal8Bit();
    //.arg(HttpURI).arg(QString(HttpParameters)).arg(QString(HttpHeaders)).toLocal8Bit();
    SCDebug<<"HttpString:"<<QString(HttpString);
    QByteArray httpStringSha1 = QCryptographicHash::hash(HttpString, QCryptographicHash::Sha1).toHex();
    SCDebug<<"httpStringSha1:"<<QString(httpStringSha1);
    QByteArray StringToSign = QString("sha1\n"+KeyTime+"\n"+httpStringSha1+"\n").toLocal8Bit();
    SCDebug<<"StringToSign:"<<QString(StringToSign);
    QByteArray Signature = hmacSha1(SignKey,StringToSign,false).toHex();
    SCDebug<<"Signature:"<<QString(Signature);
    //這裡隻能用+号拼接,因為urlEncoded字元中可能出現%3這樣誤拼接
    QString authorization = QString("q-sign-algorithm=sha1"
                                    "&q-ak="+SecretId+
                                    "&q-sign-time="+KeyTime+
                                    "&q-key-time="+KeyTime+
                                    "&q-header-list="+QString(HeaderList)+
                                    "&q-url-param-list="+QString(UrlParamList)+
                                    "&q-signature=")+QString(Signature);
    //            .arg(SecretId).arg(KeyTime).arg(KeyTime)
    //            .arg(QString(HeaderList)).arg(QString(UrlParamList)).arg(QString(Signature));
    SCDebug<<"Authorization:"<<authorization;
    req.setRawHeader("Authorization", authorization.toLocal8Bit());
    //put
    _reply = _pManager->put(req,fileData);
    SCDebug<<"_repley:"<<_reply->url();

    connect(_reply,SIGNAL(finished()),this,SLOT(slotReplyFinished()));
    connect(_reply,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(slotReplyError(QNetworkReply::NetworkError)));
    return true;
}
           

線上調用工具

依這個工具為主,填入參數,切換到 [線上調用] 點選發送請求,會提示傳回成功,檢視簽名過程與程式中輸出的是否一緻

https://console.cloud.tencent.com/api/explorer?Product=cos&Version=2018-11-26&Action=PutObject&SignVersion=

Qt 騰訊雲對象存儲騰訊雲對象存儲七牛雲對象存儲

上傳檔案MD5計算方式

做完發現騰訊雲文檔中有幾處二義性的地方

Qt 騰訊雲對象存儲騰訊雲對象存儲七牛雲對象存儲

七牛雲對象存儲

https://blog.csdn.net/u012020854/article/details/113335590

繼續閱讀