七牛-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

 

posted @ 2020-03-02 16:32  春天里的花骨朵  阅读(1390)  评论(0编辑  收藏  举报