iOS端直接调用

视觉智能开放平台的API接口推荐使用SDK进行调用,推荐在服务端进行接入,在客户端直接接入AccessKey ID和AccessKey Secret有泄露风险,可以使用STS授权用户调用服务。

背景信息

在进行iOS调用之前,需要使用STS服务获取临时访问凭证。阿里云STS(Security Token Service)是阿里云提供的一种临时访问权限管理服务。您可以通过STS服务给其他用户颁发临时访问凭证,该用户可使用临时访问凭证,在规定时间内调用视觉智能开放平台的各项服务。临时访问凭证无需透露您的长期密钥,保障您的账户更加安全。获取临时访问凭证,请参见获取角色的临时身份凭证

说明

阿里云视觉智能开放平台各类目视觉AI能力API接入、接口使用或问题咨询等,请通过钉钉群(23109592)加入阿里云视觉智能开放平台咨询群联系我们。

方案一:若文件在上海地域OSS

若您的文件存放在上海OSS中,可以参见请求签名的方式进行调用,本文以银行卡识别(RecognizeBankCard)为例,仅展示关键步骤及关键代码,完整的示例可下载iOSDemo。如果您需要调用其他算法,请参见注释并根据实际业务修改相应的代码。

交互流程

image

前提条件

获取STS临时凭证:

  1. 授予权限:

    在获取STS临时凭证之前,调用者(RAM用户和RAM角色)需要被授权有调用STS接口的权限。您可以通过设置RAM权限策略来实现这一点。相关的设置步骤和权限策略可参见使用STS临时访问凭证访问OSS文档。您需要根据实际需求配置更细粒度的授权策略,防止出现权限过大的风险。关于更细粒度的授权策略配置详情,请参见视觉智能开放平台自定义权限策略参考

    重要

    为后续步骤进行,调用者(RAM用户和RAM角色)需要被授权AliyunSTSAssumeRoleAccess(调用STS服务AssumeRole接口的权限)、AliyunVIAPIFullAccess(这里为了下列示例,给出的是管理视觉智能API的权限,但是在实际工作中,强烈建议您根据实际需求配置更细粒度的授权策略,防止出现权限过大的风险。关于更细粒度的授权策略配置详情,请参见视觉智能开放平台自定义权限策略参考)。

  2. 调用AssumeRole接口:

    使用已授权的RAM用户或RAM角色调用AssumeRole接口,并按照接口文档填写必要参数。查阅AssumeRole接口的官方文档以了解详细的接口说明和使用方法。

  3. 使用STS Token:

    调用AssumeRole接口成功后,您会收到一个包含AccessKeyIdAccessKeySecretSecurityToken的STS Token(如下代码)。在实际调用其他阿里云服务的接口时,您需要将代码中的<ALIBABA_CLOUD_ACCESS_KEY_ID><ALIBABA_CLOUD_ACCESS_KEY_SECRET><ALIBABA_CLOUD_SECURITY_TOKEN>替换为阿里云STS Token数据中获取的临时AccessKeyIdAccessKeySecretSecurityToken

{
  "RequestId": "429D9967-C809-5A30-B65E-9B742CF*****",
  "AssumedRoleUser": {
    "Arn": "acs:ram::175805416243****:role/STStokenTestRole/STSsessionName",
    "AssumedRoleId": "39779315882322****:STSsessionName"
  },
  "Credentials": {
    "SecurityToken": "exampleToken",
    "AccessKeyId": "STS.exampleAccessKeyID",
    "AccessKeySecret": "exampleAccessKeySecret",
    "Expiration": "2024-06-12T03:21:29Z"
  }
}

步骤一:配置基本参数

下面提供的代码段是调用阿里云的"银行卡识别"服务,在iOSDemo的Tool/CallApiClient.m文件中。需要将代码中的<ALIBABA_CLOUD_ACCESS_KEY_ID>、<ALIBABA_CLOUD_ACCESS_KEY_SECRET>、<ALIBABA_CLOUD_SECURITY_TOKEN>替换为前提条件中获取的阿里云STS Token数据的临时AccessKeyId、AccessKeySecret、SecurityToken

/**
  <ALIBABA_CLOUD_ACCESS_KEY_ID>、<ALIBABA_CLOUD_ACCESS_KEY_SECRET>、<ALIBABA_CLOUD_SECURITY_TOKEN>需替换为STS Token数据中获取的临时AccessKeyId、AccessKeySecret、SecurityToken
  如果您是用的子账号AccessKey,还需要为子账号授予权限AliyunVIAPIFullAccess,请参考https://help.aliyun.com/document_detail/145025.html
 */
const NSString* ACCESS_KEY_ID = @"<ALIBABA_CLOUD_ACCESS_KEY_ID>";
const NSString* ACCESS_KEY_SECRET = @"<ALIBABA_CLOUD_ACCESS_KEY_SECRET>";
const NSString* SECURITY_TOKEN = @"<ALIBABA_CLOUD_SECURITY_TOKEN>";

步骤二:调用服务端接口并计算签名

下面提供的代码段是针对视觉智能开放平台API的签名和请求发送过程的实现。签名是云服务常用的一种安全措施,用于确保发送到云服务的请求是未经篡改的,并且是由拥有相应凭证的合法用户发起的。具体逻辑文档请参见文档请求签名

以下是该代码段的具体含义和步骤:

  1. 设置API请求参数:使用bodyDict字典存储必要的请求参数,例如Action(API名称)、ImageURL(图片地址)等,并添加一些签名所需系统参数,如Timestamp(当前时间戳)、SignatureNonce(唯一随机值)。

  2. 生成签名:

    • 对参数按照ASCII码的顺序进行排序(bodyToFormString:)。

    • 根据HTTP请求方法、资源路径和查询字符串创建签名字符串。

    • 使用HMAC-SHA1算法对签名字符串进行签名,然后进行Base64编码(hmacSha1:data:)。

  3. 构造请求URL:插入生成的签名和其他查询参数到URL中,形成最终的完整请求URL。

  4. 发送请求:

    • 创建一个HTTP POST请求(request:)。

    • 设置请求的相关属性,如Content-Type

    • 使用NSURLSession将请求发送出去,并在请求完成后通过block回调处理响应结果或错误。

  5. 处理响应:

    • 请求成功,解析响应数据,并通过回调将结果传递回去。

    • 请求失败,解析错误信息,并通过回调将错误传递回去。

代码示例(单击查看详情)

/**
 * ========================================================================================================================
 * 以下代码仅仅为了调用服务端接口计算签名,其逻辑可参考文档:https://help.aliyun.com/document_detail/144904.html
 * ========================================================================================================================
 */

+(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];
}

@end

步骤三:封装请求并发送数据至指定视觉智能开放平台API

下面提供的代码段是如何在iOS应用中调用阿里云的RecognizeBankCard API来识别银行卡信息。整个过程涉及到准备请求参数、生成请求签名、构造请求URL,以及发起网络请求和处理响应的步骤。

以下是该代码段的具体含义和步骤:

  1. 准备请求参数:声明一个NSMutableDictionary类型的字典bodyDict来存放请求需要的参数,包括:

    • AccessKeyId:访问阿里云API需要的访问密钥。

    • Action:API的操作名称,此处为RecognizeBankCard,表示调用阿里云的银行卡识别服务。

    • ImageURL:需要识别的银行卡图片的URL。

    • SecurityToken:为了支持使用临时访问凭证而添加的参数,需要从Security Token Service(STS)获取。

  2. 签名请求:所有请求到阿里云服务的API需要进行签名以保证请求的安全性。在这个示例中,allKeysGotoSignatureWithDict:endpoint:accessSecret:httpMethod:apiVersion:方法用于计算签名并构造最终的请求URL。签名过程中会用到如HTTP方法(POST)、API版本和EndPoint等信息。

  3. 构造请求URL:使用前述方法计算得到包含签名的请求URLfinalUrl

  4. 发起POST请求:通过自定义request:responseBlock:方法使用NSURLSession发起一个POST请求到计算好的finalUrl。请求成功或失败后,会通过回调block返回数据或错误信息给调用者。

  5. 异步处理响应:网络请求和响应处理是异步的,确保不会阻塞主线程。回调block的执行被安排在主线程的dispatch队列中,以便可以安全地更新UI或处理数据。

代码示例(单击查看详情

/**
 以RecognizeBankCard为例。
 @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文档
    bodyDict[@"ImageURL"] = imageUrl;

    // 添加STS的SecurityToken
    bodyDict[@"SecurityToken"] = SECURITY_TOKEN;
    // 验签
    // 这里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);
            }
        });
    }];
}

步骤四:页面触发按钮并调用视觉智能开放平台API

这段代码展示了如何在iOS应用中调用封装后的CallApiClient类中的recognizeBankCardWithImageUrl:responseBlock:方法来识别一张银行卡图片的信息。这是一个Objective-C的示例,用于展示从应用层面如何使用API客户端。

  1. 设定图片URL:

    • 定义变量imageUrl并将其设置为银行卡图片的URL地址。这张图片存储在阿里云的OSS上,并且是公开可访问的。

  2. 调用API识别银行卡:

    • 通过CallApiClient类的静态方法recognizeBankCardWithImageUrl:responseBlock:调用银行卡识别API,传入图片URL和一个回调block。这个回调block将在请求完成后执行,无论是成功还是失败。

  3. 处理响应:

    • 在回调block中,首先检查error对象来确定API调用是否成功。

      • 如果调用出现错误(error不为nil),则应该处理这个错误。错误处理可能包括日志记录、显示错误信息给用户等。

      • 如果调用成功(errornil),则可以从回调传回的data字典中提取银行卡识别的结果。这里的例子演示了如何获取银行卡号(cardNumber)。实际上,根据API的文档,您可能还可以获取到其他有用的信息(如银行名称、卡类型等)。

  4. 显示结果:

    • 在成功获取银行卡号后,示例中使用alertInfomation:方法(在这个示例中未实现,需要你自己定义)显示一个提示框,将银行卡号展示给用户。这是一种简单直接的反馈方式,适用于快速原型或测试应用。

说明

这段代码整体上展示了从发起API请求到接收并处理API响应的完整流程。它对于理解如何在实际iOS应用中集成和使用外部API是一个有用的参考。需要注意的是,在一个完整的应用实现中,你可能还需要考虑更多的错误处理和异常场景。此外,出于用户体验的考虑,在请求发出期间,可能还需要添加加载指示器来告知用户正在进行网络操作。

代码示例(单击查看详情)

/**
 * 调用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) {
            if ([error.localizedDescription containsString:@"InvalidAccessKeyId.NotFound"]) {
                [self alertInfomation:@"请求报错,请检查您代码中的YOUR_ACCESS_KEY_ID和YOUR_ACCESS_KEY_SECRET是否已经修改正确。"];
            } else if([error.localizedDescription containsString:@"InvalidApi.NotPurchase"]) {
                [self alertInfomation:@"请求报错,您的账号未开通视觉智能开放平台相应类目,请进行开通:https://help.aliyun.com/document_detail/465341.html"];
            } else if([error.localizedDescription containsString:@"Unauthorized"]) {
                [self alertInfomation:@"请求报错,您的子账号未授予AliyunVIAPIFullAccess权限,请参考https://help.aliyun.com/document_detail/145025.html"];
            } else if([error.localizedDescription containsString:@"InvalidAction.NotFound"]) {
                [self alertInfomation:@"请求报错,请检查您调用的API和类目是否匹配,API和类目的关系请参考:https://help.aliyun.com/document_detail/465341.html,和访问的域名是否匹配,类目和域名的关系请参考:https://help.aliyun.com/document_detail/143103.html"];
            } else {
                [self alertInfomation:error.localizedDescription];
            }
        } else {
            // 获取银行卡号,这里只是示例,请根据文档获取自己想要的出参
            NSString* cardNumber = [data objectForKey:@"CardNumber"];
            [self alertInfomation:[NSString stringWithFormat:@"银行卡号:%@", cardNumber]];
        }
    }];
}

方案二:若文件在本地或可访问的URL

若您的文件存放在本地或可访问的URL,请参见文件URL处理,显式地将文件转换成上海OSS链接,再按照若文件在上海地域OSS进行调用。本文以银行卡识别(RecognizeBankCard)为例,仅展示关键步骤及关键代码,完整的示例可下载iOSDemo。如果您调用其他算法,请参见注释和实际业务修改相应代码。

交互流程

image

前提条件

获取STS临时凭证:

  1. 授予权限:

    在获取STS临时凭证之前,调用者(RAM用户和RAM角色)需要被授权有调用STS接口的权限。您可以通过设置RAM权限策略来实现这一点。相关的设置步骤可参见使用STS临时访问凭证访问OSS文档。您需要根据实际需求配置更细粒度的授权策略,防止出现权限过大的风险。关于更细粒度的授权策略配置详情,请参见视觉智能开放平台自定义权限策略参考

    重要

    为后续步骤进行,调用者(RAM用户和RAM角色)需要被授权AliyunSTSAssumeRoleAccess(调用STS服务AssumeRole接口的权限)、RAM角色授予上传OSS文件的权限、AliyunVIAPIFullAccess(这里为了下列实例,给出的是管理视觉智能API的权限,您需要根据实际需求配置更细粒度的授权策略,防止出现权限过大的风险。关于更细粒度的授权策略配置详情,请参见视觉智能开放平台自定义权限策略参考

  2. 调用AssumeRole接口:

    使用已授权的RAM用户或RAM角色调用AssumeRole接口,并按照接口文档填写必要参数。查阅AssumeRole接口的官方文档以了解详细的接口说明和使用方法。

  3. 使用STS Token:

    调用AssumeRole接口成功后,您会收到一个包含AccessKeyId、AccessKeySecret和SecurityToken的STS Token(如下代码)。在实际调用其他阿里云服务的接口时,您需要将代码中的<ALIBABA_CLOUD_ACCESS_KEY_ID>、<ALIBABA_CLOUD_ACCESS_KEY_SECRET>、<ALIBABA_CLOUD_SECURITY_TOKEN>替换为阿里云STS Token数据中获取的临时AccessKeyId、AccessKeySecret、SecurityToken

{
  "RequestId": "429D9967-C809-5A30-B65E-9B742CF*****",
  "AssumedRoleUser": {
    "Arn": "acs:ram::175805416243****:role/STStokenTestRole/STSsessionName",
    "AssumedRoleId": "39779315882322****:STSsessionName"
  },
  "Credentials": {
    "SecurityToken": "exampleToken",
    "AccessKeyId": "STS.exampleAccessKeyID",
    "AccessKeySecret": "exampleAccessKeySecret",
    "Expiration": "2024-06-12T03:21:29Z"
  }
}

步骤一:配置基本参数

下面提供的代码段是调用阿里云的"银行卡识别"服务,在iOSDemo的Tool/CallApiClient.m文件中。需要将代码中的<ALIBABA_CLOUD_ACCESS_KEY_ID>、<ALIBABA_CLOUD_ACCESS_KEY_SECRET>、<ALIBABA_CLOUD_SECURITY_TOKEN>替换为前提条件中获取的阿里云STS Token数据的临时AccessKeyId、AccessKeySecret、SecurityToken

说明

此处的临时的AccessKeyId、AccessKeySecret、SecurityToken是为了避免暴露自己的AccessKeyId和AccessKeySecret在前端界面上。下面(步骤二)获取的STS Token临时访问权限是为了将上传文件到临时OSS bucket,从而得到RL地址。

/**
  <ALIBABA_CLOUD_ACCESS_KEY_ID>、<ALIBABA_CLOUD_ACCESS_KEY_SECRET>、<ALIBABA_CLOUD_SECURITY_TOKEN>需替换为STS Token数据中获取的临时AccessKeyId、AccessKeySecret、SecurityToken
  如果您是用的子账号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";
const NSString* SECURITY_TOKEN = @"<ALIBABA_CLOUD_SECURITY_TOKEN>";

步骤二:调用GetOssStsToken接口获取临时OSS STS Token

下面提供的代码段是利用阿里云的Access Key ID和Access Key Secret去请求一个临时的阿里云OSS(对象存储服务)STS(Security Token Service)令牌。这个STS令牌将允许用户以阿里云视觉智能开放平台官方OSS-Bucket为目标存储介质,上传文件或数据。为便于用户调试接口,文件在OSS上的存储有效期被设定为1天。

以下是该代码段的具体含义和步骤:

  1. 准备请求数据:使用给定的accessKeyaccessSecret,构建一个请求字典bodyDict,该字典包括需要的操作Action,这里是"GetOssStsToken"

  2. 签名和构建最终URL:根据提供的参数,包括API的端点(endpoint),HTTP请求方法(建议为POST),以及API的版本号(这里为"2020-04-01"),使用这些信息对请求进行签名,并构建出最终的请求URL。

  3. 发送请求:通过一个POST请求,将准备好的签名和数据发送到设定的API端点。

  4. 处理响应:通过异步方式处理服务端返回的响应。如果遇到错误,回调函数将传递错误信息;如果成功,回调函数将传递包含STS令牌数据的字典。

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,其含义不是银行卡号,而是分割后的图片地址。

代码示例(单击查看详情)

/**
 获取oss sts token,使用阿里云视觉智能开放平台官方OSS-Bucket作为临时存储,仅为方便用户方便调试接口使用,文件存储有效期为1天。
 */
+(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);
            }
        });
    }];
}

步骤三:使用临时OSS STS Token将文件上传到阿里云视觉智能开放平台官方OSS Bucket

下面提供的代码段是将一些图像数据上传到阿里云对象存储服务(OSS)使用一个临时的STS Token进行认证和授权的功能。

以下是该代码段的具体含义和步骤:

  1. 通过CallApiClient获取STS Token: 使用提供的Access Key ID (accessKeyId) 和Access Key Secret (accessKeySecret),以及一个可能已经存在的securityToken,来请求从CallApiClient获取STS Token。

  2. 初始化OSS Client: 使用获取到的临时凭证信息,创建一个OSS客户端(OSSClient)实例以进行后续的OSS操作。客户端配置(OSSClientConfiguration)包括设置重试次数、请求超时和资源传输最长时间等。

  3. 设置上传请求: 创建一个OSSPutObjectRequest对象以指定上传文件的详细信息。文件将被上传到固定的Bucket("viapi-customer-temp"),而对象键(objectKey)则由Access Key ID和一个随机UUID组成以确保唯一性。

  4. 执行上传任务: 调用putObject:方法来实际上传图像数据,这个过程将会异步进行。

  5. 获取上传文件的URL: 成功上传后,使用OSS客户端预签名URL功能(presignConstrainURLWithBucketName:withObjectKey:withExpirationInterval:)获取上传文件的访问URL。

  6. 回调返回URL: 执行回调函数block,传递上传文件的URL给调用者,或者在上传失败时返回错误信息。

说明

这一步需要在Frameworks中引入libresolv.tbd和AliyunOSSiOS.framework。其中,libresolv.tbd为系统库,关于编译和获取AliyunOSSiOS.framework,请参见OSSiOS SDK安装

代码示例(单击查看详情)

#import "ViapiUtils.h"
#import "CallApiClient.h"
#import <AliyunOSSiOS/AliyunOSSiOS.h>

@implementation ViapiUtils

+(void)uploadWithAk:(NSString*)accessKeyId andSk:(NSString*)accessKeySecret andToken:(NSString*)securityToken  andData:(NSData *)imageData responseBlock:(void(^)(NSString *imageUrl, NSError *error))block {
    [CallApiClient getOssStsTokenWithAk:accessKeyId andSk:accessKeySecret andToken:securityToken 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";
            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的URL地址,调用视觉智能开放平台API

下面提供的代码段用于上传文件到OSS后获取URL地址,调用视觉智能开放平台API。具体的操作步骤与若文件在上海地域OSS进行调用一致。且每一步代码都已封装好,完整的示例代码可下载iOSDemo

以下是该代码段的具体含义和步骤:

  1. 参数设置:通过构建一个字典 bodyDict,设置所需的 API 参数。这包括:

    • AccessKeyIdAccessKeySecret:用于身份验证的凭证。

    • SecurityToken:作为临时安全凭证,通常与STS服务一起使用。

    • ImageURL:需要识别的银行卡图片的URL。

    • Action:指明要调用的API能力,这里是"RecognizeBankCard",即识别银行卡。

  2. 准备API请求:

    • 代码构建了一个签名后的最终URLfinalUrl,该过程涵盖了设置API的端点(endpoint)、推荐使用的POST方法、API的版本号,以及根据访问密钥、API端点和其他参数生成签名的过程。

  3. 执行API请求:向构建好的 finalUrl 发送POST请求。这是通过 request:responseBlock: 方法实现的,该方法的具体实现尚未显示,但可假定它负责将请求发送到服务器并处理响应。

  4. 响应处理:请求的响应通过异步回调 responseBlock 进行处理。如果请求成功,API的响应数据(识别出的银行卡信息等)会通过 block(responseObject, nil) 返回给调用者。如果请求过程中出现错误,通过 block(nil, error) 将错误返回给调用者。

Tool/CallApiClient.m文件中recognizeBankCardWithImageUrl函数中的bodyDict[@"Action"]endpointapiVersion参数及bodyDict[@"ImageURL"]这一行代表业务参数。

例如,您想使用通用分割能力,通过通用分割API文档可知该能力属于分割抠图类目(imageseg20191230),能力名称为SegmentCommonImage,您需要将endpoint改为imageseg.cn-shanghai.aliyuncs.combodyDict[@"Action"]改为SegmentCommonImageapiVersion2019-12-30不用修改,bodyDict[@"ImageURL"]参数名为ImageURL不用修改。获取结果的时候,需要获取ImageURL,其含义不是银行卡号,而是分割后的图片地址。

代码示例(单击查看详情)

+(void)recognizeBankCardWithImageUrl:(NSString*)imageUrl andAk:(NSString*)accessKey andSk:(NSString*)accessSecret andToken:(NSString*)securityToken  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文档
    bodyDict[@"ImageURL"] = imageUrl;

    bodyDict[@"SecurityToken"] = securityToken;

    // 验签
    // 这里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);
            }
        });
    }];
}