iOS开发中集成阿里云视频上传SDK总结

3 篇文章 0 订阅

阿里云iOS视频上传SDK说明文档

安装

这里写图片描述

说明1:这两个Framework均需导入。
说明2:此处引入头文件

#import <VODUpload/VODUploadClient.h>
#import <VODUpload/VODUploadModel.h>
实例化

首先,添加属性

@property (nonatomic, strong) VODUploadClient *uploader;
@property (nonatomic, strong) NSString *UploadAuth;
@property (nonatomic, strong) NSString *UploadAddress;

然后,初始化

#pragma mark --初始化
- (void)initVODUpload {
    __weak typeof(self) weakSelf = self;
    OnUploadStartedListener testUploadStartedCallbackFunc = ^(UploadFileInfo* fileInfo) {
        NSLog(@"upload started .");
        [weakSelf.uploader setUploadAuthAndAddress:fileInfo uploadAuth:self.UploadAuth uploadAddress:self.UploadAddress];
    };
    OnUploadSucceedListener testSuccessCallbackFunc = ^(UploadFileInfo* fileInfo){
        NSLog(@"file:%@ upload success!", fileInfo.filePath);
    };
    OnUploadFailedListener testFailedCallbackFunc = ^(UploadFileInfo* fileInfo, NSString *code, NSString * message){
        NSLog(@"failed, filePath:%@, code = %@, error message = %@", fileInfo.filePath, code, message);
    };
    // 单位:字节
    OnUploadProgressListener testProgressCallbackFunc = ^(UploadFileInfo* fileInfo, long uploadedSize, long totalSize) {
        NSLog(@"progress uploadedSize : %li, totalSize : %li", uploadedSize, totalSize);
    };
    OnUploadTokenExpiredListener testTokenExpiredCallbackFunc = ^{
        NSLog(@"*token expired.");
        // get token and call resmeUploadWithAuth.
    };
    OnUploadRertyListener testUploadRertyListener = ^{
        NSLog(@"retry begin.");
    };
    OnUploadRertyResumeListener testUploadRertyResumeListener = ^{
        NSLog(@"retry resume.");
    };
    VODUploadListener *listener;
    listener = [[VODUploadListener alloc] init];
    listener.started = testUploadStartedCallbackFunc;
    listener.success = testSuccessCallbackFunc;
    listener.failure = testFailedCallbackFunc;
    listener.progress = testProgressCallbackFunc;
    listener.expire = testTokenExpiredCallbackFunc;
    listener.retry = testUploadRertyListener;
    listener.retryResume = testUploadRertyResumeListener;

    self.uploader = [[VODUploadClient alloc] init];
    [self.uploader init:@"替换为你的AccessKeyId" accessKeySecret:@"替换为你的AccessKeySecret" listener:listener];
}

注意:AccessKeyId和AccessKeySecret需要替换

上传视频

#pragma mark --上传视频文件
- (void)uploadVideo:(HQVideoListModel *)model {
    //添加上传文件
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *filePath = [[paths firstObject] stringByAppendingString:[NSString stringWithFormat:@"/%@", model.fileName]];

    NSLog(@"上传视频文件:%@", filePath);

    [self.uploader addFile:filePath vodInfo:nil];

    //注意:开始上传前要先获取授权,开始上传时需要用到授权信息
    [self getUploadAuthAndAddressWithFilePath:filePath];
}

注意1:使用说明 中的流程是:用户选择文件 -> 添加文件到列表 -> 开始上传 -> 设置上传凭证和地址 -> 上传完成事件。但是,实际上我们要在开始上传前获取上传凭证和地址。

注意2:在开始上传的回调中设置上传凭证和地址

     __weak typeof(self) weakSelf = self;
    OnUploadStartedListener testUploadStartedCallbackFunc = ^(UploadFileInfo* fileInfo) {
        NSLog(@"upload started .");
        [weakSelf.uploader setUploadAuthAndAddress:fileInfo uploadAuth:self.UploadAuth uploadAddress:self.UploadAddress];
    };

下面才是本文的重点

获取上传凭证和地址

#pragma mark --获取授权
- (void)getUploadAuthAndAddressWithFilePath:(NSString *)filePath {
    if (!filePath) {
        NSLog(@"视频文件路径错误");
        return;
    }

    NSArray *arr1 = [filePath componentsSeparatedByString:@"/"];
    NSString *fileName = [arr1 lastObject];
    if (!fileName) {
        NSLog(@"获取视频文件名称错误");
        return;
    }

    NSArray *arr2 = [fileName componentsSeparatedByString:@"."];
    NSString *fileTitle = [arr2 firstObject];
    if (!fileTitle) {
        NSLog(@"获取视频文件标题错误");
        return;
    }

    //获取时间戳
    NSString *dateStr = [NSString stringWithFormat:@"%@", [NSDate date]];
    dateStr = [dateStr substringWithRange:NSMakeRange(0, 19)];
    dateStr = [dateStr stringByReplacingOccurrencesOfString:@" " withString:@"T"];
    dateStr = [dateStr stringByAppendingString:@"Z"];

    //生成随机数
    NSString *randomStr = [NSString stringWithFormat:@"%d", arc4random()];

    //参数
    NSDictionary *parameters = @{
                                 @"Action":@"CreateUploadVideo",
                                 @"Title":fileTitle,
                                 @"FileName":fileName,
                                 @"Format":@"JSON",
                                 @"Version":@"2017-03-21",
                                 @"AccessKeyId":@"替换为你的AccessKeyId",
                                 @"SignatureMethod":@"HMAC-SHA1",
                                 @"Timestamp":dateStr,
                                 @"SignatureVersion":@"1.0",
                                 @"SignatureNonce":randomStr,
                                 };
    NSString *signedUrlStr = [self getSignedURLStrWithHost:@"http://vod.cn-shanghai.aliyuncs.com/" withParameters:parameters];

    NSURL *url = [NSURL URLWithString:signedUrlStr];
    NSLog(@"urlStr:%@", signedUrlStr);

    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    NSURLSession *session = [NSURLSession sharedSession];

    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
        if (httpResponse.statusCode == 200) {

            NSDictionary *obj = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
            NSLog(@"请求成功:%@", obj);
            NSString *UploadAddress = [obj objectForKey:@"UploadAddress"];
            NSString *UploadAuth = [obj objectForKey:@"UploadAuth"];

            self.UploadAddress = UploadAddress;
            self.UploadAuth = UploadAuth;

            //开始上传
            [self.uploader start];

        }else {
            NSLog(@"请求失败:%@", error);
        }

    }];

    [dataTask resume];
}

注意:获取上传凭证和地址的方法有两种,一种是服务器端获取,另一种是手机端获取。

我们采用的是在手机端获取上传凭证和地址,具体做法查看API使用手册
这里边最复杂就是签名机制,下面是我写的方法,仅供参考。

#pragma mark --获取签名后的URL
- (NSString *)getSignedURLStrWithHost:(NSString *)host withParameters:(NSDictionary *)parameters {
    //签名机制
    //1.使用请求参数构造规范化的请求字符串
    //a.按照参数名称的字典顺序对请求中所有的请求参数(包括文档中描述的”公共请求参数”和给定了的请求接口的自定义参数,但不能包括”公共请求参数”中提到的Signature参数本身)进行排序。

    //获取所有参数
    NSArray *keyArr = [parameters allKeys];
    NSLog(@"排序前的keyArr:%@", keyArr);

    //排序
    NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:nil ascending:YES];
    NSArray *descriptors = [NSArray arrayWithObject:descriptor];
    keyArr = [keyArr sortedArrayUsingDescriptors:descriptors];
    NSLog(@"排序后的keyArr:%@", keyArr);

    //b.对相关请求参数的名称和值进行编码
    //c.对编码后的参数名称和值使用英文等号(=)进行连接
    //d.再把英文等号连接得到字符串按参数名称的字典顺序依次使用&符号连接,即得到规范化请求字符串

    //编码并拼接参数
    NSString *parametersStr = @"";
    for (NSInteger i = 0; i < keyArr.count; i++) {
        NSString *key = keyArr[i];
        NSString *value = parameters[key];
        if (parametersStr.length > 0) {
            parametersStr = [parametersStr stringByAppendingString:@"&"];
        }
        parametersStr = [parametersStr stringByAppendingString:[HQUtils urlStringEncoding:key]];
        parametersStr = [parametersStr stringByAppendingString:@"="];
        parametersStr = [parametersStr stringByAppendingString:[HQUtils urlStringEncoding:value]];
    }
    NSLog(@"拼接的参数:%@", parametersStr);

    //2.使用上一步构造的规范化字符串按照下面的规则构造用于计算签名的字符串
    NSString *stringToSign = [NSString stringWithFormat:@"GET&%@&%@", [HQUtils urlStringEncoding:@"/"], [HQUtils urlStringEncoding:parametersStr]];
    NSLog(@"用于计算签名的字符串:%@", stringToSign);

    //3.按照RFC2104的定义,使用上面的用于签名的字符串计算签名HMAC值。注意:计算签名时使用的Key就是用户持有的Access Key Secret并加上一个”&”字符(ASCII:38),使用的哈希算法是SHA1。
    //4.按照Base64编码规则把上面的HMAC值编码成字符串,即得到签名值(Signature)。

    NSString *signature = [self hmacsha1:stringToSign key:@"替换为你的AccessKeySecret&"];
    NSLog(@"签名后的字符串:%@", signature);

    //5.将得到的签名值作为Signature参数添加到请求参数中,即完成对请求签名的过程。
    NSString *resultStr = [NSString stringWithFormat:@"%@?Signature=%@&%@", host, signature, parametersStr];

    return resultStr;
}

#pragma mark 
- (NSString *)hmacsha1:(NSString *)text key:(NSString *)secret {
    NSData *secretData = [secret dataUsingEncoding:NSUTF8StringEncoding];
    NSData *clearTextData = [text dataUsingEncoding:NSUTF8StringEncoding];
    unsigned char result[20];
    CCHmac(kCCHmacAlgSHA1, [secretData bytes], [secretData length], [clearTextData bytes], [clearTextData length], result);
    char base64Result[32];
    size_t theResultLength = 32;
    Base64EncodeData(result, 20, base64Result, &theResultLength,YES);
    NSData *theData = [NSData dataWithBytes:base64Result length:theResultLength];
    NSString *base64EncodedResult = [[NSString alloc] initWithData:theData encoding:NSASCIIStringEncoding];
    return base64EncodedResult;
}

注意:此处需要引入三个文件

#include <CommonCrypto/CommonDigest.h>
#include <CommonCrypto/CommonHMAC.h>
#include "HQbase64.h"

其中,HQbase64.h的具体实现

#import <Foundation/Foundation.h>

extern size_t EstimateBas64EncodedDataSize(size_t inDataSize);
extern size_t EstimateBas64DecodedDataSize(size_t inDataSize);

extern bool Base64EncodeData(const void *inInputData, size_t inInputDataSize, char *outOutputData, size_t *ioOutputDataSize, BOOL wrapped);
extern bool Base64DecodeData(const void *inInputData, size_t inInputDataSize, void *ioOutputData, size_t *ioOutputDataSize);

@interface HQbase64 : NSObject

@end

HQbase64.m的具体实现

#import "HQbase64.h"

#include <math.h>

const UInt8 kBase64EncodeTable[64] = {
    /*  0 */ 'A',    /*  1 */ 'B',    /*  2 */ 'C',    /*  3 */ 'D',
    /*  4 */ 'E',    /*  5 */ 'F',    /*  6 */ 'G',    /*  7 */ 'H',
    /*  8 */ 'I',    /*  9 */ 'J',    /* 10 */ 'K',    /* 11 */ 'L',
    /* 12 */ 'M',    /* 13 */ 'N',    /* 14 */ 'O',    /* 15 */ 'P',
    /* 16 */ 'Q',    /* 17 */ 'R',    /* 18 */ 'S',    /* 19 */ 'T',
    /* 20 */ 'U',    /* 21 */ 'V',    /* 22 */ 'W',    /* 23 */ 'X',
    /* 24 */ 'Y',    /* 25 */ 'Z',    /* 26 */ 'a',    /* 27 */ 'b',
    /* 28 */ 'c',    /* 29 */ 'd',    /* 30 */ 'e',    /* 31 */ 'f',
    /* 32 */ 'g',    /* 33 */ 'h',    /* 34 */ 'i',    /* 35 */ 'j',
    /* 36 */ 'k',    /* 37 */ 'l',    /* 38 */ 'm',    /* 39 */ 'n',
    /* 40 */ 'o',    /* 41 */ 'p',    /* 42 */ 'q',    /* 43 */ 'r',
    /* 44 */ 's',    /* 45 */ 't',    /* 46 */ 'u',    /* 47 */ 'v',
    /* 48 */ 'w',    /* 49 */ 'x',    /* 50 */ 'y',    /* 51 */ 'z',
    /* 52 */ '0',    /* 53 */ '1',    /* 54 */ '2',    /* 55 */ '3',
    /* 56 */ '4',    /* 57 */ '5',    /* 58 */ '6',    /* 59 */ '7',
    /* 60 */ '8',    /* 61 */ '9',    /* 62 */ '+',    /* 63 */ '/'
};

/*
 -1 = Base64 end of data marker.
 -2 = White space (tabs, cr, lf, space)
 -3 = Noise (all non whitespace, non-base64 characters)
 -4 = Dangerous noise
 -5 = Illegal noise (null byte)
 */

const SInt8 kBase64DecodeTable[128] = {
    /* 0x00 */ -5,     /* 0x01 */ -3,     /* 0x02 */ -3,     /* 0x03 */ -3,
    /* 0x04 */ -3,     /* 0x05 */ -3,     /* 0x06 */ -3,     /* 0x07 */ -3,
    /* 0x08 */ -3,     /* 0x09 */ -2,     /* 0x0a */ -2,     /* 0x0b */ -2,
    /* 0x0c */ -2,     /* 0x0d */ -2,     /* 0x0e */ -3,     /* 0x0f */ -3,
    /* 0x10 */ -3,     /* 0x11 */ -3,     /* 0x12 */ -3,     /* 0x13 */ -3,
    /* 0x14 */ -3,     /* 0x15 */ -3,     /* 0x16 */ -3,     /* 0x17 */ -3,
    /* 0x18 */ -3,     /* 0x19 */ -3,     /* 0x1a */ -3,     /* 0x1b */ -3,
    /* 0x1c */ -3,     /* 0x1d */ -3,     /* 0x1e */ -3,     /* 0x1f */ -3,
    /* ' ' */ -2,    /* '!' */ -3,    /* '"' */ -3,    /* '#' */ -3,
    /* '$' */ -3,    /* '%' */ -3,    /* '&' */ -3,    /* ''' */ -3,
    /* '(' */ -3,    /* ')' */ -3,    /* '*' */ -3,    /* '+' */ 62,
    /* ',' */ -3,    /* '-' */ -3,    /* '.' */ -3,    /* '/' */ 63,
    /* '0' */ 52,    /* '1' */ 53,    /* '2' */ 54,    /* '3' */ 55,
    /* '4' */ 56,    /* '5' */ 57,    /* '6' */ 58,    /* '7' */ 59,
    /* '8' */ 60,    /* '9' */ 61,    /* ':' */ -3,    /* ';' */ -3,
    /* '<' */ -3,    /* '=' */ -1,    /* '>' */ -3,    /* '?' */ -3,
    /* '@' */ -3,    /* 'A' */ 0,    /* 'B' */  1,    /* 'C' */  2,
    /* 'D' */  3,    /* 'E' */  4,    /* 'F' */  5,    /* 'G' */  6,
    /* 'H' */  7,    /* 'I' */  8,    /* 'J' */  9,    /* 'K' */ 10,
    /* 'L' */ 11,    /* 'M' */ 12,    /* 'N' */ 13,    /* 'O' */ 14,
    /* 'P' */ 15,    /* 'Q' */ 16,    /* 'R' */ 17,    /* 'S' */ 18,
    /* 'T' */ 19,    /* 'U' */ 20,    /* 'V' */ 21,    /* 'W' */ 22,
    /* 'X' */ 23,    /* 'Y' */ 24,    /* 'Z' */ 25,    /* '[' */ -3,
    /* '\' */ -3,    /* ']' */ -3,    /* '^' */ -3,    /* '_' */ -3,
    /* '`' */ -3,    /* 'a' */ 26,    /* 'b' */ 27,    /* 'c' */ 28,
    /* 'd' */ 29,    /* 'e' */ 30,    /* 'f' */ 31,    /* 'g' */ 32,
    /* 'h' */ 33,    /* 'i' */ 34,    /* 'j' */ 35,    /* 'k' */ 36,
    /* 'l' */ 37,    /* 'm' */ 38,    /* 'n' */ 39,    /* 'o' */ 40,
    /* 'p' */ 41,    /* 'q' */ 42,    /* 'r' */ 43,    /* 's' */ 44,
    /* 't' */ 45,    /* 'u' */ 46,    /* 'v' */ 47,    /* 'w' */ 48,
    /* 'x' */ 49,    /* 'y' */ 50,    /* 'z' */ 51,    /* '{' */ -3,
    /* '|' */ -3,    /* '}' */ -3,    /* '~' */ -3,    /* 0x7f */ -3
};

const UInt8 kBits_00000011 = 0x03;
const UInt8 kBits_00001111 = 0x0F;
const UInt8 kBits_00110000 = 0x30;
const UInt8 kBits_00111100 = 0x3C;
const UInt8 kBits_00111111 = 0x3F;
const UInt8 kBits_11000000 = 0xC0;
const UInt8 kBits_11110000 = 0xF0;
const UInt8 kBits_11111100 = 0xFC;

size_t EstimateBas64EncodedDataSize(size_t inDataSize)
{
    size_t theEncodedDataSize = (int)ceil(inDataSize / 3.0) * 4;
    theEncodedDataSize = theEncodedDataSize / 72 * 74 + theEncodedDataSize % 72;
    return(theEncodedDataSize);
}

size_t EstimateBas64DecodedDataSize(size_t inDataSize)
{
    size_t theDecodedDataSize = (int)ceil(inDataSize / 4.0) * 3;
    //theDecodedDataSize = theDecodedDataSize / 72 * 74 + theDecodedDataSize % 72;
    return(theDecodedDataSize);
}

bool Base64EncodeData(const void *inInputData, size_t inInputDataSize, char *outOutputData, size_t *ioOutputDataSize, BOOL wrapped)
{
    size_t theEncodedDataSize = EstimateBas64EncodedDataSize(inInputDataSize);
    if (*ioOutputDataSize < theEncodedDataSize)
        return(false);
    *ioOutputDataSize = theEncodedDataSize;
    const UInt8 *theInPtr = (const UInt8 *)inInputData;
    UInt32 theInIndex = 0, theOutIndex = 0;
    for (; theInIndex < (inInputDataSize / 3) * 3; theInIndex += 3)
    {
        outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex] & kBits_11111100) >> 2];
        outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex] & kBits_00000011) << 4 | (theInPtr[theInIndex + 1] & kBits_11110000) >> 4];
        outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex + 1] & kBits_00001111) << 2 | (theInPtr[theInIndex + 2] & kBits_11000000) >> 6];
        outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex + 2] & kBits_00111111) >> 0];
        if (wrapped && (theOutIndex % 74 == 72))
        {
            outOutputData[theOutIndex++] = '\r';
            outOutputData[theOutIndex++] = '\n';
        }
    }
    const size_t theRemainingBytes = inInputDataSize - theInIndex;
    if (theRemainingBytes == 1)
    {
        outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex] & kBits_11111100) >> 2];
        outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex] & kBits_00000011) << 4 | (0 & kBits_11110000) >> 4];
        outOutputData[theOutIndex++] = '=';
        outOutputData[theOutIndex++] = '=';
        if (wrapped && (theOutIndex % 74 == 72))
        {
            outOutputData[theOutIndex++] = '\r';
            outOutputData[theOutIndex++] = '\n';
        }
    }
    else if (theRemainingBytes == 2)
    {
        outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex] & kBits_11111100) >> 2];
        outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex] & kBits_00000011) << 4 | (theInPtr[theInIndex + 1] & kBits_11110000) >> 4];
        outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex + 1] & kBits_00001111) << 2 | (0 & kBits_11000000) >> 6];
        outOutputData[theOutIndex++] = '=';
        if (wrapped && (theOutIndex % 74 == 72))
        {
            outOutputData[theOutIndex++] = '\r';
            outOutputData[theOutIndex++] = '\n';
        }
    }
    return(true);
}

bool Base64DecodeData(const void *inInputData, size_t inInputDataSize, void *ioOutputData, size_t *ioOutputDataSize)
{
    memset(ioOutputData, '.', *ioOutputDataSize);

    size_t theDecodedDataSize = EstimateBas64DecodedDataSize(inInputDataSize);
    if (*ioOutputDataSize < theDecodedDataSize)
        return(false);
    *ioOutputDataSize = 0;
    const UInt8 *theInPtr = (const UInt8 *)inInputData;
    UInt8 *theOutPtr = (UInt8 *)ioOutputData;
    size_t theInIndex = 0, theOutIndex = 0;
    UInt8 theOutputOctet;
    size_t theSequence = 0;
    for (; theInIndex < inInputDataSize; )
    {
        SInt8 theSextet = 0;

        SInt8 theCurrentInputOctet = theInPtr[theInIndex];
        theSextet = kBase64DecodeTable[theCurrentInputOctet];
        if (theSextet == -1)
            break;
        while (theSextet == -2)
        {
            theCurrentInputOctet = theInPtr[++theInIndex];
            theSextet = kBase64DecodeTable[theCurrentInputOctet];
        }
        while (theSextet == -3)
        {
            theCurrentInputOctet = theInPtr[++theInIndex];
            theSextet = kBase64DecodeTable[theCurrentInputOctet];
        }
        if (theSequence == 0)
        {
            theOutputOctet = (theSextet >= 0 ? theSextet : 0) << 2 & kBits_11111100;
        }
        else if (theSequence == 1)
        {
            theOutputOctet |= (theSextet >- 0 ? theSextet : 0) >> 4 & kBits_00000011;
            theOutPtr[theOutIndex++] = theOutputOctet;
        }
        else if (theSequence == 2)
        {
            theOutputOctet = (theSextet >= 0 ? theSextet : 0) << 4 & kBits_11110000;
        }
        else if (theSequence == 3)
        {
            theOutputOctet |= (theSextet >= 0 ? theSextet : 0) >> 2 & kBits_00001111;
            theOutPtr[theOutIndex++] = theOutputOctet;
        }
        else if (theSequence == 4)
        {
            theOutputOctet = (theSextet >= 0 ? theSextet : 0) << 6 & kBits_11000000;
        }
        else if (theSequence == 5)
        {
            theOutputOctet |= (theSextet >= 0 ? theSextet : 0) >> 0 & kBits_00111111;
            theOutPtr[theOutIndex++] = theOutputOctet;
        }
        theSequence = (theSequence + 1) % 6;
        if (theSequence != 2 && theSequence != 4)
            theInIndex++;
    }
    *ioOutputDataSize = theOutIndex;
    return(true);
}

@implementation HQbase64

@end

注意:urlStringEncoding方法的具体实现:

+(NSString *)urlStringEncoding:(NSString *)urlStr {    

    NSString *outputStr =

    (__bridge NSString *)CFURLCreateStringByAddingPercentEscapes(

                                                                 NULL, /* allocator */

                                                                 (__bridge CFStringRef)urlStr,

                                                                 NULL, /* charactersToLeaveUnescaped */

                                                                 (CFStringRef)@"!*'();:@&=+$,/?%#[]",

                                                                 kCFStringEncodingUTF8);

    return outputStr;

}

最后,参考文章:

iOS下HMAC_SHA1加密算法
NSSortDescriptor使用注意以及直接排序字符串数组

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jinrui_w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值