七牛-ETag算法(OC)


一、摘要

1.七牛上傳文件,用hash來唯一標識七牛存儲空間中的某個文件,該hash是以ETag算法計算出的一段哈希值;

2.算法介紹:https://developer.qiniu.com/kodo/manual/1231/appendix

3.七牛的提供的實現語言中(https://github.com/qiniu/qetag),沒有給出OC實現;

4.參考js代碼:https://www.jianshu.com/p/3785fc314fc5

二、代碼

- (void)test2 {
    /*測試鏈接
     http://yqq.file.mediportal.com.cn/yqq_5b911b43955f06317c6bd792/3974e8eaab11b2dd5b357e60e5a587d1  etag:FtfVrVsdpVf9t_tCfyvsVC-1p6aW
     4M以上文件親測正確
     */
    
    NSURL *url = [NSURL URLWithString:@"http://yqq.file.mediportal.com.cn/yqq_5b911b43955f06317c6bd792/3974e8eaab11b2dd5b357e60e5a587d1"];
    NSError *error;
    NSData * data = [NSData dataWithContentsOfURL:url options:NSDataReadingMapped error:&error];
    NSString *etag = [self caculateETagWith:data];
    NSLog(@"etag------%@", etag);
}

//算法實現
- (NSString *)caculateETagWith:(NSData *)data
{
    unsigned long blockSize = 4 * 1024 * 1024;
    NSMutableData *sha1Data = [NSMutableData data];
    Byte prefix = 0x16;
    int blockCount = 0;
    
    unsigned long bufferSize = [data length];
    //獲取余數
    unsigned long remainder = bufferSize % blockSize;
    //獲取商
    double fa = (double)bufferSize / blockSize;
    //向下取整
    blockCount = floor(fa);
    
    if (bufferSize > blockSize) {//大於4M的文件
        NSMutableData *sha2Data = [NSMutableData data];
        for (int i = 0; i < blockCount+1; i++) {
            NSUInteger length = blockSize;
            if (i == blockCount && remainder > 0) {
                length = remainder;
            }
            //將每個塊(包括4M塊和小於4M的塊)進行sha1加密並拼接起來
            NSData *subData = [data subdataWithRange:NSMakeRange(i * blockSize, length)];
            [sha2Data appendData:[self sha1:subData]];
        }
        //將拼接塊進行二次sha1加密
        [sha1Data appendData:[self sha1:sha2Data]];
    } else {
        [sha1Data appendData:[self sha1:data]];
    }

    if (!sha1Data.length) return @"Fto5o-5ea0sNMlW_75VgGJCv2AcJ";
    
    NSData *sha1Buffer = sha1Data;
    if (bufferSize > blockSize) {
        //大於4M,頭部拼接0x96單個字節
        prefix = 0x96;
    }
    
    Byte preByte[] = {prefix};
    NSMutableData *mutaData = [NSMutableData dataWithBytes:preByte length:1];
    [mutaData appendData:sha1Buffer];
    
    //將長度為21個字節的二進制數據進行url_safe_base64計算
    return [self safeBase64WithSha1Str:mutaData];
}

/*
 sha1加密(加密后的data長度為20)
 */
- (NSData*)sha1:(NSData *)data
{
    //注:如果用以下代碼,轉換出的data長度為40
//    const char *cstr = [sourceStr cStringUsingEncoding:NSUTF8StringEncoding];
//    NSData *data = [NSData dataWithBytes:cstr length:sourceStr.length];
//    NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
//    for(int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++)
//        [output appendFormat:@"%02x", digest[i]];
//    return output;
    
    //sha1Data長度為20(CC_SHA1_DIGEST_LENGTH系統設定為20)
    uint8_t digest[CC_SHA1_DIGEST_LENGTH];
    CC_SHA1(data.bytes, (CC_LONG)data.length, digest);
    NSData * sha1Data = [[NSData alloc] initWithBytes:digest length:CC_SHA1_DIGEST_LENGTH];
    return sha1Data;
}

- (NSString *)safeBase64WithSha1Str:(NSData *)base64
{
    //Base64編碼中包含有"+,/,="不安全的URL字符串,我們要對這些字符進行轉換
    NSString *base64Str = [GTMBase64 encodeBase64Data:base64];

    NSMutableString *safeBase64Str = [[NSMutableString alloc] initWithString:base64Str];

    safeBase64Str = (NSMutableString *)[safeBase64Str stringByReplacingOccurrencesOfString:@"+"withString:@"-"];

    safeBase64Str = (NSMutableString *)[safeBase64Str stringByReplacingOccurrencesOfString:@"/"withString:@"_"];

    safeBase64Str = (NSMutableString *)[safeBase64Str stringByReplacingOccurrencesOfString:@"="withString:@""];
    
    return safeBase64Str;
}

說明:url?stat可查看七牛文件的etag值(即hash值),如下:

http://yqq.file.mediportal.com.cn/yqq_5b911b43955f06317c6bd792/3974e8eaab11b2dd5b357e60e5a587d1?stat

     {"fsize":687810,"uploaded":687810,"hash":"FtfVrVsdpVf9t_tCfyvsVC-1p6aW","mimeType":"image/jpeg"}

 

 

GitHub

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM