iOS端直接调用
视觉智能开放平台的API接口推荐使用SDK进行调用,因为调用需要使用AccessKey ID和AccessKey Secret,推荐在服务端进行接入,在客户端直接接入有AccessKey ID和AccessKey Secret泄漏风险,请自行评估风险范围。
阿里云视觉智能开放平台各类目视觉AI能力API接入、接口使用或问题咨询等,请通过钉钉群(23109592)加入阿里云视觉智能开放平台咨询群联系我们。
使用iOS直接调用,请参见请求签名。
使用签名机制调用,文件参数当前系统推荐使用上海OSS链接,您需要将文件放入到上海OSS中,具体操作,请参见开通OSS服务。具体调用方式,请参见文件在同地域OSS。
如果是其他情况(如本地文件或者其他链接),您需要先显式地将文件转换成上海OSS链接,具体操作,请参见文件URL处理。具体调用方式,请参见文件在本地或不在同一地域 OSS。
文件在同地域OSS
如果您的文件存放在上海OSS中,可以参考请求签名进行调用。本文以银行卡识别(RecognizeBankCard)为例,仅展示关键步骤及关键代码,完整示例可下载iOSDemo。您如果调用其他算法,请参考注释和实际业务修改相应代码。
计算签名
/** * ======================================================================================================================== * 以下代码仅仅为了调用服务端接口计算签名,其逻辑可参考文档:https://help.aliyun.com/document_detail/144904.html * 这里只是为了端上演示,所以将代码写在了iOS端 * 真正上线不建议将ACCESS_KEY_ID和ACCESS_KEY_SECRET写在端上,会有泄漏风险,建议将请求API接口代码写到您的服务端 * ======================================================================================================================== */ +(NSString*)allKeysGotoSignatureWithDict:(NSMutableDictionary*)bodyDict endpoint:(NSString*)endpoint accessSecret:(NSString*)accessSecret httpMethod:(NSString*)httpMethod apiVersion:(NSString*)apiVersion{ // 系统参数 bodyDict[@"SignatureMethod"] = @"HMAC-SHA1"; bodyDict[@"SignatureNonce"] = [self getNonce]; bodyDict[@"SignatureVersion"] = @"1.0"; bodyDict[@"Timestamp"] = [self getTimestamp]; bodyDict[@"Format"] = @"JSON"; // 业务API参数 bodyDict[@"RegionId"] = @"cn-shanghai"; bodyDict[@"Version"] = apiVersion; //key升序排序 NSString *sortedQueryString = [self bodyToFormString:bodyDict]; NSMutableString *stringToSign = [NSMutableString string]; [stringToSign appendString:[NSString stringWithFormat:@"%@&",httpMethod]]; [stringToSign appendString:[NSString stringWithFormat:@"%@&",[self urlEncode:@"/"]]]; [stringToSign appendString:[self urlEncode:sortedQueryString]]; //hmacsha1 加签 NSString *sign = [self hmacSha1:[NSString stringWithFormat:@"%@&",accessSecret] data:stringToSign]; // 签名最后也要做特殊URL编码 NSString *signature = [self urlEncode:sign]; //最后结果 NSString *finalUrl = [NSString stringWithFormat:@"https://%@/?Signature=%@&%@",endpoint,signature,sortedQueryString]; NSLog(@"finalUrl:%@",finalUrl); return finalUrl; } +(NSString*)hmacSha1:(NSString*)key data:(NSString*)data{ const char *cKey = [key cStringUsingEncoding:NSUTF8StringEncoding]; const char *cData = [data cStringUsingEncoding:NSUTF8StringEncoding]; //sha1 unsigned char cHMAC[CC_SHA1_DIGEST_LENGTH]; CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, strlen(cData), cHMAC); NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)]; NSString *hash = [HMAC base64EncodedStringWithOptions:0];//将加密结果进行一次BASE64编码。 return hash; } +(NSString*)getNonce{ NSTimeInterval timestamp = [[NSDate date]timeIntervalSince1970]; NSString *string = [NSString stringWithFormat:@"%f%@",timestamp, [[NSUUID UUID]UUIDString]]; NSString*md5 = [self md5String:string]; return md5; } // md5 + (NSString *)md5String:(NSString *)string{ const char *cStr = [string UTF8String]; unsigned char result[CC_MD5_DIGEST_LENGTH]; CC_MD5(cStr, (CC_LONG)strlen(cStr), result); return [NSString stringWithFormat: @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7], result[8], result[9], result[10], result[11], result[12], result[13], result[14], result[15] ]; } +(NSString*)getTimestamp { NSDateFormatter *formatter = [[NSDateFormatter alloc]init]; formatter.timeZone = [NSTimeZone timeZoneWithName:@"GMT"]; formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss'Z'"; return [formatter stringFromDate:[NSDate date]]; } +(NSString*)bodyToFormString:(NSMutableDictionary*)query{ NSString* url = @""; if (query != nil && query.count > 0) { NSArray *keys = query.allKeys; NSArray*sortedArray = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1,id obj2) { return[obj1 compare:obj2 options:NSNumericSearch];//正序排列 }]; NSMutableArray *arr = [NSMutableArray array]; for (NSString *key in sortedArray) { NSString *value = query[key]; if (value.length==0) { continue; } NSString *key2 = [NSString stringWithFormat:@"%@=%@",[self urlEncode:key],[self urlEncode:value]]; [arr addObject:key2]; } if(arr.count > 0) { url = [arr componentsJoinedByString:@"&"]; } } return url; } +(NSString*)urlEncode:(NSString*)value{ NSString *unreserved = @"*-._"; NSMutableCharacterSet *allowedCharacterSet = [NSMutableCharacterSet alphanumericCharacterSet]; [allowedCharacterSet addCharactersInString:unreserved]; [allowedCharacterSet addCharactersInString:@" "]; NSString *encoded = [value stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet]; encoded = [encoded stringByReplacingOccurrencesOfString:@" " withString:@"%20"]; encoded = [encoded stringByReplacingOccurrencesOfString:@"+" withString:@"%20"]; encoded = [encoded stringByReplacingOccurrencesOfString:@"*" withString:@"%2A"]; encoded = [encoded stringByReplacingOccurrencesOfString:@"%7E" withString:@"~"]; return encoded ; } +(void)request:(NSString*)request responseBlock:(void(^)(NSDictionary *responseObject, NSError *error))block{ NSMutableURLRequest *msRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:request]]; [msRequest setHTTPMethod:@"POST"]; msRequest.timeoutInterval = 60; [msRequest addValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; NSURLSession *session = [NSURLSession sharedSession]; NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:msRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; NSError *parseError = nil; if(httpResponse.statusCode == 200){ NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&parseError]; NSLog(@"response:%@",responseDictionary); block([responseDictionary objectForKey:@"Data"],nil); }else{ NSLog(@"%@,error:%@",httpResponse,error); if (error) { block(nil,error); }else{ NSDictionary *errorDict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; NSLog(@"errorDict:%@",errorDict); NSString *codevalue = [errorDict objectForKey:@"Code"]; NSString *msgvalue = [errorDict objectForKey:@"Message"]; NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"code:%@,error msg:%@",codevalue,msgvalue], NSLocalizedDescriptionKey,nil]; parseError = [[NSError alloc] initWithDomain:NSCocoaErrorDomain code:httpResponse.statusCode userInfo:info]; block(nil,parseError); } } }]; [dataTask resume]; }
说明计算签名的逻辑,详情请参见请求签名。
封装接口。
#import "CallApiClient.h" #include <CommonCrypto/CommonDigest.h> #include <CommonCrypto/CommonHMAC.h> /** "YOUR_ACCESS_KEY_ID", "YOUR_ACCESS_KEY_SECRET" 的生成请参考https://help.aliyun.com/document_detail/175144.html 如果您是用的子账号AccessKey,还需要为子账号授予权限AliyunVIAPIFullAccess,请参考https://help.aliyun.com/document_detail/145025.html */ const NSString* ACCESS_KEY_ID = @"YOUR_ACCESS_KEY_ID"; const NSString* ACCESS_KEY_SECRET = @"YOUR_ACCESS_KEY_SECRET"; @implementation CallApiClient /** 以RecognizeBankCard为例。 这里只是为了端上演示流程,所以将代码写在了iOS端 真正上线不建议将ACCESS_KEY_ID和ACCESS_KEY_SECRET写在端上,会有泄漏风险,建议将请求API接口代码写到您的服务端 @param imageUrl 银行卡图片url */ +(void)recognizeBankCardWithImageUrl:(NSString*)imageUrl responseBlock:(void(^)(NSDictionary *data, NSError *error))block{ NSMutableDictionary *bodyDict = [NSMutableDictionary dictionary]; bodyDict[@"AccessKeyId"] = ACCESS_KEY_ID; // API Action,能力名称,请参考具体算法文档详情页中的Action参数,这里以银行卡识别为例:https://help.aliyun.com/document_detail/151893.html bodyDict[@"Action"] = @"RecognizeBankCard"; // 业务参数,请参考具体的AI能力的API文档进行修改,需要注意在调用其他能力时,这里的入参要根据API文档的入参名进行修改,如人脸活体检测API文档里面的参数名称是“Tasks.N.ImageURL”形式的,因为是支持同时检测多个Task的情况,所以入参时是从“Tasks.1.ImageURL”开始的。 // 举例 bodyDict[@"Tasks.1.ImageURL"] = imageUrl; bodyDict[@"ImageURL"] = imageUrl; // 验签 // 这里endpoint为API访问域名,与类目相关,具体类目的API访问域名请参考:https://help.aliyun.com/document_detail/143103.html // httpMethod推荐使用POST // apiVersion为API版本,与类目相关,具体类目的API版本请参考:https://help.aliyun.com/document_detail/464194.html NSString *finalUrl = [self allKeysGotoSignatureWithDict:bodyDict endpoint:@"ocr.cn-shanghai.aliyuncs.com" accessSecret:ACCESS_KEY_SECRET httpMethod:@"POST" apiVersion:@"2019-12-30"]; //直接发post请求 [self request:finalUrl responseBlock:^(NSDictionary *responseObject, NSError *error) { dispatch_async(dispatch_get_main_queue(), ^{ if (error) { block(nil,error); }else{ block(responseObject, nil); } }); }]; }
说明本文以银行卡识别(RecognizeBankCard)为例,如果调用其他算法,请参考注释和实际业务修改相应代码。
进行调用。
/** * 调用API */ - (void)callApi { NSString* imageUrl = @"http://viapi-test.oss-cn-shanghai.aliyuncs.com/viapi-3.0domepic/ocr/RecognizeBankCard/yhk1.jpg"; [CallApiClient recognizeBankCardWithImageUrl:imageUrl responseBlock:^(NSDictionary * _Nonnull data, NSError * _Nonnull error) { if (error) { // 请处理error } else { // 获取银行卡号,这里只是示例,请根据文档获取自己想要的出参 NSString* cardNumber = [data objectForKey:@"CardNumber"]; [self alertInfomation:[NSString stringWithFormat:@"银行卡号:%@", cardNumber]]; } }]; }
说明本文以银行卡识别(RecognizeBankCard)为例,如果调用其他算法,请参考注释和实际业务修改相应代码。
完整的示例可参考iOSDemo。
完整工程仅以银行卡识别(RecognizeBankCard)为例,如果调用其他算法,请参考注释进行相应代码修改。
Tool/CallApiClient.m文件中的ACCESS_KEY_ID、ACCESS_KEY_SECRET,recognizeBankCardWithImageUrl函数中的bodyDict[@"Action"]、endpoint、apiVersion参数及bodyDict[@"ImageURL"]这一行代表业务参数。
例如,您想使用通用分割能力,通过通用分割API文档可知该能力属于分割抠图类目(imageseg20191230),能力名称为SegmentCommonImage,您需要将endpoint改为imageseg.cn-shanghai.aliyuncs.com,bodyDict[@"Action"]改为SegmentCommonImage,apiVersion为2019-12-30不用修改,bodyDict[@"ImageURL"]参数名为ImageURL不用修改。获取结果的时候,需要获取ImageURL,其含义不是银行卡号,而是分割后的图片地址。
文件在本地或不在同一地域OSS
如果您的文件在本地或不在同一地域OSS,请参见文件URL处理,显式地将文件转换成上海OSS链接,再进行调用。
本文以银行卡识别(RecognizeBankCard)为例,仅展示关键步骤及关键代码,完整的示例可下载iOSDemo。您如果调用其他算法,请参考注释和实际业务修改相应代码。
调用GetOssStsToken接口获取临时的OSS STS Token。
/** 获取oss sts token,使用阿里云视觉智能开放平台官方OSS-Bucket作为临时存储,仅为方便用户方便调试接口使用,文件存储有效期为1天。 这里只是为了端上演示流程,所以将代码写在了iOS端 真正上线不建议将ACCESS_KEY_ID和ACCESS_KEY_SECRET写在端上,会有泄漏风险,建议将请求API接口代码写到您的服务端 */ +(void)getOssStsTokenWithAk:(NSString*)accessKey andSk:(NSString*)accessSecret responseBlock:(void(^)(NSDictionary *data, NSError *error))block { NSMutableDictionary *bodyDict = [NSMutableDictionary dictionary]; bodyDict[@"AccessKeyId"] = accessKey; // 获取阿里云视觉智能开放平台官方OSS-Bucket的stsToken的Action固定为GetOssStsToken bodyDict[@"Action"] = @"GetOssStsToken"; // 验签 // 这里endpoint为API访问域名,获取阿里云视觉智能开放平台官方OSS-Bucket的stsToken的域名固定为:viapiutils.cn-shanghai.aliyuncs.com // httpMethod推荐使用POST // apiVersion为API版本,获取阿里云视觉智能开放平台官方OSS-Bucket的stsToken的域名固定为:2020-04-01 NSString *finalUrl = [self allKeysGotoSignatureWithDict:bodyDict endpoint:@"viapiutils.cn-shanghai.aliyuncs.com" accessSecret:accessSecret httpMethod:@"POST" apiVersion:@"2020-04-01"]; //直接发post请求 [self request:finalUrl responseBlock:^(NSDictionary *responseObject, NSError *error) { dispatch_async(dispatch_get_main_queue(), ^{ if (error) { block(nil,error); }else{ block(responseObject, nil); } }); }]; }
说明更多关于本步骤的原理详情,请参见文件URL处理。
使用临时的OSS STS Token将文件上传到阿里云视觉智能开放平台官方OSS Bucket。
#import "ViapiUtils.h" #import "CallApiClient.h" #import <AliyunOSSiOS/AliyunOSSiOS.h> @implementation ViapiUtils +(void)uploadWithAk:(NSString*)accessKeyId andSk:(NSString*)accessKeySecret andData:(NSData *)imageData responseBlock:(void(^)(NSString *imageUrl, NSError *error))block { [CallApiClient getOssStsTokenWithAk:accessKeyId andSk:accessKeySecret responseBlock:^(NSDictionary * _Nonnull data, NSError * _Nonnull error) { if (error) { block(nil, error); } else { // 获取到sts token,用来初始化oss client id<OSSCredentialProvider> credential = [[OSSFederationCredentialProvider alloc] initWithFederationTokenGetter:^OSSFederationToken * { OSSFederationToken * token = [OSSFederationToken new]; token.tAccessKey = [data objectForKey:@"AccessKeyId"]; token.tSecretKey =[data objectForKey:@"AccessKeySecret"]; token.tToken =[data objectForKey:@"SecurityToken"]; return token; }]; OSSClientConfiguration *conf = [OSSClientConfiguration new]; conf.maxRetryCount = 3; // 网络请求遇到异常失败后的重试次数 conf.timeoutIntervalForRequest = 20; // 网络请求的超时时间 conf.timeoutIntervalForResource = 24*60*60; // 允许资源传输的最长时间 conf.maxConcurrentRequestCount = 30; OSSClient *client = [[OSSClient alloc] initWithEndpoint:@"http://oss-cn-shanghai.aliyuncs.com" credentialProvider:credential clientConfiguration:conf]; OSSPutObjectRequest * put = [OSSPutObjectRequest new]; // bucketName固定填viapi-customer-temp put.bucketName = @"viapi-customer-temp"; // objectKey必须是<accessKeyId>/xxx NSString *sourceNameObjectKey = [NSString stringWithFormat:@"%@/%@",accessKeyId,[[NSUUID UUID]UUIDString]]; put.objectKey = sourceNameObjectKey; NSLog(@"put.objectKey:%@ [NSData dataWithContentsOfFile:file]==%@",put.objectKey,data); put.uploadingData = imageData; OSSTask * putTask = [client putObject:put]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [putTask continueWithBlock:^id _Nullable(OSSTask * _Nonnull task) { if (!task.error) { OSSTask *downloadURLTask = [client presignConstrainURLWithBucketName:@"viapi-customer-temp" withObjectKey:put.objectKey withExpirationInterval:24*3600]; NSString * string = downloadURLTask.result; NSLog(@"inner string:%@", string); NSArray *arr =[string componentsSeparatedByString:@"?"]; NSString* imageUrl = arr.count > 0 ? arr[0] :string; NSLog(@"upload object success! %@", imageUrl); block(imageUrl, nil); } else { NSLog(@"upload object failed, error: %@" , putTask.error); block(nil, putTask.error); } return nil; }]; [putTask waitUntilFinished]; // 阻塞直到上传完成 }); } }]; } @end
本步骤需要使用OSS SDK,更多信息,请参见OSSiOS SDK示例。
这一步需要在Frameworks中引入libresolv.tbd和AliyunOSSiOS.framework。其中,libresolv.tbd为系统库,关于编译和获取AliyunOSSiOS.framework,请参见OSSiOS SDK安装。
上传完成之后得到OSS的URL地址,按照文件在同地域OSS方式进行调用。
/** 以RecognizeBankCard为例。 这里只是为了端上演示流程,所以将代码写在了iOS端 真正上线不建议将ACCESS_KEY_ID和ACCESS_KEY_SECRET写在端上,会有泄漏风险,建议将请求API接口代码写到您的服务端 @param imageUrl 银行卡图片url */ +(void)recognizeBankCardWithImageUrl:(NSString*)imageUrl andAk:(NSString*)accessKey andSk:(NSString*)accessSecret responseBlock:(void(^)(NSDictionary *data, NSError *error))block{ NSMutableDictionary *bodyDict = [NSMutableDictionary dictionary]; bodyDict[@"AccessKeyId"] = accessKey; // API Action,能力名称,请参考具体算法文档详情页中的Action参数,这里以银行卡识别为例:https://help.aliyun.com/document_detail/151893.html bodyDict[@"Action"] = @"RecognizeBankCard"; // 业务参数,请参考具体的AI能力的API文档进行修改,需要注意在调用其他能力时,这里的入参要根据API文档的入参名进行修改,如人脸活体检测API文档里面的参数名称是“Tasks.N.ImageURL”形式的,因为是支持同时检测多个Task的情况,所以入参时是从“Tasks.1.ImageURL”开始的。 // 举例 bodyDict[@"Tasks.1.ImageURL"] = imageUrl; bodyDict[@"ImageURL"] = imageUrl; // 验签 // 这里endpoint为API访问域名,与类目相关,具体类目的API访问域名请参考:https://help.aliyun.com/document_detail/143103.html // httpMethod推荐使用POST // apiVersion为API版本,与类目相关,具体类目的API版本请参考:https://help.aliyun.com/document_detail/464194.html NSString *finalUrl = [self allKeysGotoSignatureWithDict:bodyDict endpoint:@"ocr.cn-shanghai.aliyuncs.com" accessSecret:accessSecret httpMethod:@"POST" apiVersion:@"2019-12-30"]; //直接发post请求 [self request:finalUrl responseBlock:^(NSDictionary *responseObject, NSError *error) { dispatch_async(dispatch_get_main_queue(), ^{ if (error) { block(nil,error); }else{ block(responseObject, nil); } }); }]; }
说明本文以银行卡识别(RecognizeBankCard)为例,如果调用其他算法,请参考注释和实际业务修改相应代码。
完整的示例可下载iOSDemo。您如果调用其他算法,请参考注释和实际业务修改相应代码。
说明完整工程仅以银行卡识别(RecognizeBankCard)为例,如果调用其他算法,请参考注释和实际业务修改相应代码。
ViewController.m文件中的ACCESS_KEY_ID、ACCESS_KEY_SECRET,Tool/CallApiClient.m文件中recognizeBankCardWithImageUrl函数中的bodyDict[@"Action"]、endpoint、apiVersion参数及bodyDict[@"ImageURL"]这一行代表业务参数。
例如,您想使用通用分割能力,通过通用分割API文档可知该能力属于分割抠图类目(imageseg20191230),能力名称为SegmentCommonImage,您需要将endpoint改为imageseg.cn-shanghai.aliyuncs.com,bodyDict[@"Action"]改为SegmentCommonImage,apiVersion为2019-12-30不用修改,bodyDict[@"ImageURL"]参数名为ImageURL不用修改。获取结果的时候,需要获取ImageURL,其含义不是银行卡号,而是分割后的图片地址。