文档

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。您如果调用其他算法,请参考注释和实际业务修改相应代码。

  1. 计算签名

    /**
     * ========================================================================================================================
     * 以下代码仅仅为了调用服务端接口计算签名,其逻辑可参考文档: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];
    }
    说明

    计算签名的逻辑,详情请参见请求签名

  2. 封装接口。

    #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)为例,如果调用其他算法,请参考注释和实际业务修改相应代码。

  3. 进行调用。

    /**
     * 调用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。您如果调用其他算法,请参考注释和实际业务修改相应代码。

  1. 调用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处理

  2. 使用临时的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安装

  3. 上传完成之后得到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,其含义不是银行卡号,而是分割后的图片地址。

  • 本页导读 (1)
文档反馈