iOS端直接調(diào)用
視覺智能開放平臺(tái)的API接口推薦使用SDK進(jìn)行調(diào)用,推薦在服務(wù)端進(jìn)行接入,在客戶端直接接入AccessKey ID和AccessKey Secret有泄露風(fēng)險(xiǎn),可以使用STS授權(quán)用戶調(diào)用服務(wù)。
背景信息
在進(jìn)行iOS調(diào)用之前,需要使用STS服務(wù)獲取臨時(shí)訪問(wèn)憑證。阿里云STS(Security Token Service)是阿里云提供的一種臨時(shí)訪問(wèn)權(quán)限管理服務(wù)。您可以通過(guò)STS服務(wù)給其他用戶頒發(fā)臨時(shí)訪問(wèn)憑證,該用戶可使用臨時(shí)訪問(wèn)憑證,在規(guī)定時(shí)間內(nèi)調(diào)用視覺智能開放平臺(tái)的各項(xiàng)服務(wù)。臨時(shí)訪問(wèn)憑證無(wú)需透露您的長(zhǎng)期密鑰,保障您的賬戶更加安全。獲取臨時(shí)訪問(wèn)憑證,請(qǐng)參見獲取角色的臨時(shí)身份憑證。
若文件存放在上海OSS中:
如果您的文件存放在上海OSS(阿里云對(duì)象存儲(chǔ) Object Storage Service)中,則需要使用請(qǐng)求簽名機(jī)制進(jìn)行調(diào)用。有關(guān)具體的調(diào)用方式,請(qǐng)參見方案一:若文件在上海地域OSS。對(duì)于OSS的具體操作,請(qǐng)參見開通OSS服務(wù)。
若文件在本地文件或者其他鏈接:
若是涉及其他情況(如本地文件或者其他鏈接),您需要先將文件鏈接轉(zhuǎn)換成上海OSS鏈接,具體操作,請(qǐng)參見文件URL處理。有關(guān)具體的調(diào)用方式,請(qǐng)參見方案二:若文件在本地或可訪問(wèn)的URL。
阿里云視覺智能開放平臺(tái)各類目視覺AI能力API接入、接口使用或問(wèn)題咨詢等,請(qǐng)通過(guò)釘釘群(23109592)加入阿里云視覺智能開放平臺(tái)咨詢?nèi)郝?lián)系我們。
方案一:若文件在上海地域OSS
若您的文件存放在上海OSS中,可以參見請(qǐng)求簽名的方式進(jìn)行調(diào)用,本文以銀行卡識(shí)別(RecognizeBankCard)為例,僅展示關(guān)鍵步驟及關(guān)鍵代碼,完整的示例可下載iOSDemo。如果您需要調(diào)用其他算法,請(qǐng)參見注釋并根據(jù)實(shí)際業(yè)務(wù)修改相應(yīng)的代碼。
交互流程
前提條件
獲取STS臨時(shí)憑證:
授予權(quán)限:
在獲取STS臨時(shí)憑證之前,調(diào)用者(RAM用戶和RAM角色)需要被授權(quán)有調(diào)用STS接口的權(quán)限。您可以通過(guò)設(shè)置RAM權(quán)限策略來(lái)實(shí)現(xiàn)這一點(diǎn)。相關(guān)的設(shè)置步驟和權(quán)限策略可參見使用STS臨時(shí)訪問(wèn)憑證訪問(wèn)OSS文檔。您需要根據(jù)實(shí)際需求配置更細(xì)粒度的授權(quán)策略,防止出現(xiàn)權(quán)限過(guò)大的風(fēng)險(xiǎn)。關(guān)于更細(xì)粒度的授權(quán)策略配置詳情,請(qǐng)參見視覺智能開放平臺(tái)自定義權(quán)限策略參考。
重要為后續(xù)步驟進(jìn)行,調(diào)用者(RAM用戶和RAM角色)需要被授權(quán)AliyunSTSAssumeRoleAccess(調(diào)用STS服務(wù)AssumeRole接口的權(quán)限)、AliyunVIAPIFullAccess(這里為了下列示例,給出的是管理視覺智能API的權(quán)限,但是在實(shí)際工作中,強(qiáng)烈建議您根據(jù)實(shí)際需求配置更細(xì)粒度的授權(quán)策略,防止出現(xiàn)權(quán)限過(guò)大的風(fēng)險(xiǎn)。關(guān)于更細(xì)粒度的授權(quán)策略配置詳情,請(qǐng)參見視覺智能開放平臺(tái)自定義權(quán)限策略參考)。
調(diào)用AssumeRole接口:
使用已授權(quán)的RAM用戶或RAM角色調(diào)用AssumeRole接口,并按照接口文檔填寫必要參數(shù)。查閱AssumeRole接口的官方文檔以了解詳細(xì)的接口說(shuō)明和使用方法。
使用STS Token:
調(diào)用AssumeRole接口成功后,您會(huì)收到一個(gè)包含
AccessKeyId
、AccessKeySecret
和SecurityToken
的STS Token(如下代碼)。在實(shí)際調(diào)用其他阿里云服務(wù)的接口時(shí),您需要將代碼中的<ALIBABA_CLOUD_ACCESS_KEY_ID>
、<ALIBABA_CLOUD_ACCESS_KEY_SECRET>
、<ALIBABA_CLOUD_SECURITY_TOKEN>
替換為阿里云STS Token數(shù)據(jù)中獲取的臨時(shí)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"
}
}
步驟一:配置基本參數(shù)
下面提供的代碼段是調(diào)用阿里云的"銀行卡識(shí)別"服務(wù),在iOSDemo的Tool/CallApiClient.m文件中。需要將代碼中的<ALIBABA_CLOUD_ACCESS_KEY_ID>、<ALIBABA_CLOUD_ACCESS_KEY_SECRET>、<ALIBABA_CLOUD_SECURITY_TOKEN>
替換為前提條件中獲取的阿里云STS Token數(shù)據(jù)的臨時(shí)AccessKeyId、AccessKeySecret、SecurityToken
。
/**
<ALIBABA_CLOUD_ACCESS_KEY_ID>、<ALIBABA_CLOUD_ACCESS_KEY_SECRET>、<ALIBABA_CLOUD_SECURITY_TOKEN>需替換為STS Token數(shù)據(jù)中獲取的臨時(shí)AccessKeyId、AccessKeySecret、SecurityToken
如果您是用的子賬號(hào)AccessKey,還需要為子賬號(hào)授予權(quán)限AliyunVIAPIFullAccess,請(qǐng)參考http://bestwisewords.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>";
步驟二:調(diào)用服務(wù)端接口并計(jì)算簽名
下面提供的代碼段是針對(duì)視覺智能開放平臺(tái)API的簽名和請(qǐng)求發(fā)送過(guò)程的實(shí)現(xiàn)。簽名是云服務(wù)常用的一種安全措施,用于確保發(fā)送到云服務(wù)的請(qǐng)求是未經(jīng)篡改的,并且是由擁有相應(yīng)憑證的合法用戶發(fā)起的。具體邏輯文檔請(qǐng)參見文檔請(qǐng)求簽名。
以下是該代碼段的具體含義和步驟:
設(shè)置API請(qǐng)求參數(shù):使用
bodyDict
字典存儲(chǔ)必要的請(qǐng)求參數(shù),例如Action
(API名稱)、ImageURL
(圖片地址)等,并添加一些簽名所需系統(tǒng)參數(shù),如Timestamp
(當(dāng)前時(shí)間戳)、SignatureNonce
(唯一隨機(jī)值)。生成簽名:
對(duì)參數(shù)按照ASCII碼的順序進(jìn)行排序(
bodyToFormString:
)。根據(jù)HTTP請(qǐng)求方法、資源路徑和查詢字符串創(chuàng)建簽名字符串。
使用HMAC-SHA1算法對(duì)簽名字符串進(jìn)行簽名,然后進(jìn)行Base64編碼(
hmacSha1:data:
)。
構(gòu)造請(qǐng)求URL:插入生成的簽名和其他查詢參數(shù)到URL中,形成最終的完整請(qǐng)求URL。
發(fā)送請(qǐng)求:
創(chuàng)建一個(gè)HTTP POST請(qǐng)求(
request:
)。設(shè)置請(qǐng)求的相關(guān)屬性,如
Content-Type
。使用
NSURLSession
將請(qǐng)求發(fā)送出去,并在請(qǐng)求完成后通過(guò)block回調(diào)處理響應(yīng)結(jié)果或錯(cuò)誤。
處理響應(yīng):
請(qǐng)求成功,解析響應(yīng)數(shù)據(jù),并通過(guò)回調(diào)將結(jié)果傳遞回去。
請(qǐng)求失敗,解析錯(cuò)誤信息,并通過(guò)回調(diào)將錯(cuò)誤傳遞回去。
/**
* ========================================================================================================================
* 以下代碼僅僅為了調(diào)用服務(wù)端接口計(jì)算簽名,其邏輯可參考文檔:http://bestwisewords.com/document_detail/144904.html
* ========================================================================================================================
*/
+(NSString*)allKeysGotoSignatureWithDict:(NSMutableDictionary*)bodyDict endpoint:(NSString*)endpoint accessSecret:(NSString*)accessSecret httpMethod:(NSString*)httpMethod apiVersion:(NSString*)apiVersion{
// 系統(tǒng)參數(shù)
bodyDict[@"SignatureMethod"] = @"HMAC-SHA1";
bodyDict[@"SignatureNonce"] = [self getNonce];
bodyDict[@"SignatureVersion"] = @"1.0";
bodyDict[@"Timestamp"] = [self getTimestamp];
bodyDict[@"Format"] = @"JSON";
// 業(yè)務(wù)API參數(shù)
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];
//最后結(jié)果
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];//將加密結(jié)果進(jìn)行一次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
步驟三:封裝請(qǐng)求并發(fā)送數(shù)據(jù)至指定視覺智能開放平臺(tái)API
下面提供的代碼段是如何在iOS應(yīng)用中調(diào)用阿里云的RecognizeBankCard
API來(lái)識(shí)別銀行卡信息。整個(gè)過(guò)程涉及到準(zhǔn)備請(qǐng)求參數(shù)、生成請(qǐng)求簽名、構(gòu)造請(qǐng)求URL,以及發(fā)起網(wǎng)絡(luò)請(qǐng)求和處理響應(yīng)的步驟。
以下是該代碼段的具體含義和步驟:
準(zhǔn)備請(qǐng)求參數(shù):聲明一個(gè)
NSMutableDictionary
類型的字典bodyDict
來(lái)存放請(qǐng)求需要的參數(shù),包括:AccessKeyId
:訪問(wèn)阿里云API需要的訪問(wèn)密鑰。Action
:API的操作名稱,此處為RecognizeBankCard
,表示調(diào)用阿里云的銀行卡識(shí)別服務(wù)。ImageURL
:需要識(shí)別的銀行卡圖片的URL。SecurityToken
:為了支持使用臨時(shí)訪問(wèn)憑證而添加的參數(shù),需要從Security Token Service(STS)獲取。
簽名請(qǐng)求:所有請(qǐng)求到阿里云服務(wù)的API需要進(jìn)行簽名以保證請(qǐng)求的安全性。在這個(gè)示例中,
allKeysGotoSignatureWithDict:endpoint:accessSecret:httpMethod:apiVersion:
方法用于計(jì)算簽名并構(gòu)造最終的請(qǐng)求URL。簽名過(guò)程中會(huì)用到如HTTP方法(POST)、API版本和EndPoint等信息。構(gòu)造請(qǐng)求URL:使用前述方法計(jì)算得到包含簽名的請(qǐng)求URL
finalUrl
。發(fā)起POST請(qǐng)求:通過(guò)自定義
request:responseBlock:
方法使用NSURLSession
發(fā)起一個(gè)POST請(qǐng)求到計(jì)算好的finalUrl
。請(qǐng)求成功或失敗后,會(huì)通過(guò)回調(diào)block
返回?cái)?shù)據(jù)或錯(cuò)誤信息給調(diào)用者。異步處理響應(yīng):網(wǎng)絡(luò)請(qǐng)求和響應(yīng)處理是異步的,確保不會(huì)阻塞主線程。回調(diào)
block
的執(zhí)行被安排在主線程的dispatch隊(duì)列中,以便可以安全地更新UI或處理數(shù)據(jù)。
/**
以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,能力名稱,請(qǐng)參考具體算法文檔詳情頁(yè)中的Action參數(shù),這里以銀行卡識(shí)別為例:http://bestwisewords.com/document_detail/151893.html
bodyDict[@"Action"] = @"RecognizeBankCard";
// 業(yè)務(wù)參數(shù),請(qǐng)參考具體的AI能力的API文檔
bodyDict[@"ImageURL"] = imageUrl;
// 添加STS的SecurityToken
bodyDict[@"SecurityToken"] = SECURITY_TOKEN;
// 驗(yàn)簽
// 這里endpoint為API訪問(wèn)域名,與類目相關(guān),具體類目的API訪問(wèn)域名請(qǐng)參考:http://bestwisewords.com/document_detail/143103.html
// httpMethod推薦使用POST
// apiVersion為API版本,與類目相關(guān),具體類目的API版本請(qǐng)參考:http://bestwisewords.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"];
//直接發(fā)post請(qǐng)求
[self request:finalUrl responseBlock:^(NSDictionary *responseObject, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
block(nil,error);
}else{
block(responseObject, nil);
}
});
}];
}
步驟四:頁(yè)面觸發(fā)按鈕并調(diào)用視覺智能開放平臺(tái)API
這段代碼展示了如何在iOS應(yīng)用中調(diào)用封裝后的CallApiClient
類中的recognizeBankCardWithImageUrl:responseBlock:
方法來(lái)識(shí)別一張銀行卡圖片的信息。這是一個(gè)Objective-C的示例,用于展示從應(yīng)用層面如何使用API客戶端。
設(shè)定圖片URL:
定義變量
imageUrl
并將其設(shè)置為銀行卡圖片的URL地址。這張圖片存儲(chǔ)在阿里云的OSS上,并且是公開可訪問(wèn)的。
調(diào)用API識(shí)別銀行卡:
通過(guò)
CallApiClient
類的靜態(tài)方法recognizeBankCardWithImageUrl:responseBlock:
調(diào)用銀行卡識(shí)別API,傳入圖片URL和一個(gè)回調(diào)block。這個(gè)回調(diào)block將在請(qǐng)求完成后執(zhí)行,無(wú)論是成功還是失敗。
處理響應(yīng):
在回調(diào)block中,首先檢查
error
對(duì)象來(lái)確定API調(diào)用是否成功。如果調(diào)用出現(xiàn)錯(cuò)誤(
error
不為nil
),則應(yīng)該處理這個(gè)錯(cuò)誤。錯(cuò)誤處理可能包括日志記錄、顯示錯(cuò)誤信息給用戶等。如果調(diào)用成功(
error
為nil
),則可以從回調(diào)傳回的data
字典中提取銀行卡識(shí)別的結(jié)果。這里的例子演示了如何獲取銀行卡號(hào)(cardNumber
)。實(shí)際上,根據(jù)API的文檔,您可能還可以獲取到其他有用的信息(如銀行名稱、卡類型等)。
顯示結(jié)果:
在成功獲取銀行卡號(hào)后,示例中使用
alertInfomation:
方法(在這個(gè)示例中未實(shí)現(xiàn),需要你自己定義)顯示一個(gè)提示框,將銀行卡號(hào)展示給用戶。這是一種簡(jiǎn)單直接的反饋方式,適用于快速原型或測(cè)試應(yīng)用。
這段代碼整體上展示了從發(fā)起API請(qǐng)求到接收并處理API響應(yīng)的完整流程。它對(duì)于理解如何在實(shí)際iOS應(yīng)用中集成和使用外部API是一個(gè)有用的參考。需要注意的是,在一個(gè)完整的應(yīng)用實(shí)現(xiàn)中,你可能還需要考慮更多的錯(cuò)誤處理和異常場(chǎng)景。此外,出于用戶體驗(yàn)的考慮,在請(qǐng)求發(fā)出期間,可能還需要添加加載指示器來(lái)告知用戶正在進(jìn)行網(wǎng)絡(luò)操作。
/**
* 調(diào)用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:@"請(qǐng)求報(bào)錯(cuò),請(qǐng)檢查您代碼中的YOUR_ACCESS_KEY_ID和YOUR_ACCESS_KEY_SECRET是否已經(jīng)修改正確。"];
} else if([error.localizedDescription containsString:@"InvalidApi.NotPurchase"]) {
[self alertInfomation:@"請(qǐng)求報(bào)錯(cuò),您的賬號(hào)未開通視覺智能開放平臺(tái)相應(yīng)類目,請(qǐng)進(jìn)行開通:http://bestwisewords.com/document_detail/465341.html"];
} else if([error.localizedDescription containsString:@"Unauthorized"]) {
[self alertInfomation:@"請(qǐng)求報(bào)錯(cuò),您的子賬號(hào)未授予AliyunVIAPIFullAccess權(quán)限,請(qǐng)參考http://bestwisewords.com/document_detail/145025.html"];
} else if([error.localizedDescription containsString:@"InvalidAction.NotFound"]) {
[self alertInfomation:@"請(qǐng)求報(bào)錯(cuò),請(qǐng)檢查您調(diào)用的API和類目是否匹配,API和類目的關(guān)系請(qǐng)參考:http://bestwisewords.com/document_detail/465341.html,和訪問(wèn)的域名是否匹配,類目和域名的關(guān)系請(qǐng)參考:http://bestwisewords.com/document_detail/143103.html"];
} else {
[self alertInfomation:error.localizedDescription];
}
} else {
// 獲取銀行卡號(hào),這里只是示例,請(qǐng)根據(jù)文檔獲取自己想要的出參
NSString* cardNumber = [data objectForKey:@"CardNumber"];
[self alertInfomation:[NSString stringWithFormat:@"銀行卡號(hào):%@", cardNumber]];
}
}];
}
方案二:若文件在本地或可訪問(wèn)的URL
若您的文件存放在本地或可訪問(wèn)的URL,請(qǐng)參見文件URL處理,顯式地將文件轉(zhuǎn)換成上海OSS鏈接,再按照若文件在上海地域OSS進(jìn)行調(diào)用。本文以銀行卡識(shí)別(RecognizeBankCard)為例,僅展示關(guān)鍵步驟及關(guān)鍵代碼,完整的示例可下載iOSDemo。如果您調(diào)用其他算法,請(qǐng)參見注釋和實(shí)際業(yè)務(wù)修改相應(yīng)代碼。
交互流程
前提條件
獲取STS臨時(shí)憑證:
授予權(quán)限:
在獲取STS臨時(shí)憑證之前,調(diào)用者(RAM用戶和RAM角色)需要被授權(quán)有調(diào)用STS接口的權(quán)限。您可以通過(guò)設(shè)置RAM權(quán)限策略來(lái)實(shí)現(xiàn)這一點(diǎn)。相關(guān)的設(shè)置步驟可參見使用STS臨時(shí)訪問(wèn)憑證訪問(wèn)OSS文檔。您需要根據(jù)實(shí)際需求配置更細(xì)粒度的授權(quán)策略,防止出現(xiàn)權(quán)限過(guò)大的風(fēng)險(xiǎn)。關(guān)于更細(xì)粒度的授權(quán)策略配置詳情,請(qǐng)參見視覺智能開放平臺(tái)自定義權(quán)限策略參考。
重要為后續(xù)步驟進(jìn)行,調(diào)用者(RAM用戶和RAM角色)需要被授權(quán)AliyunSTSAssumeRoleAccess(調(diào)用STS服務(wù)AssumeRole接口的權(quán)限)、RAM角色授予上傳OSS文件的權(quán)限、AliyunVIAPIFullAccess(這里為了下列實(shí)例,給出的是管理視覺智能API的權(quán)限,您需要根據(jù)實(shí)際需求配置更細(xì)粒度的授權(quán)策略,防止出現(xiàn)權(quán)限過(guò)大的風(fēng)險(xiǎn)。關(guān)于更細(xì)粒度的授權(quán)策略配置詳情,請(qǐng)參見視覺智能開放平臺(tái)自定義權(quán)限策略參考)
調(diào)用AssumeRole接口:
使用已授權(quán)的RAM用戶或RAM角色調(diào)用AssumeRole接口,并按照接口文檔填寫必要參數(shù)。查閱AssumeRole接口的官方文檔以了解詳細(xì)的接口說(shuō)明和使用方法。
使用STS Token:
調(diào)用AssumeRole接口成功后,您會(huì)收到一個(gè)包含AccessKeyId、AccessKeySecret和SecurityToken的STS Token(如下代碼)。在實(shí)際調(diào)用其他阿里云服務(wù)的接口時(shí),您需要將代碼中的
<ALIBABA_CLOUD_ACCESS_KEY_ID>、<ALIBABA_CLOUD_ACCESS_KEY_SECRET>、<ALIBABA_CLOUD_SECURITY_TOKEN>
替換為阿里云STS Token數(shù)據(jù)中獲取的臨時(shí)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"
}
}
步驟一:配置基本參數(shù)
下面提供的代碼段是調(diào)用阿里云的"銀行卡識(shí)別"服務(wù),在iOSDemo的Tool/CallApiClient.m文件中。需要將代碼中的<ALIBABA_CLOUD_ACCESS_KEY_ID>、<ALIBABA_CLOUD_ACCESS_KEY_SECRET>、<ALIBABA_CLOUD_SECURITY_TOKEN>
替換為前提條件中獲取的阿里云STS Token數(shù)據(jù)的臨時(shí)AccessKeyId、AccessKeySecret、SecurityToken
。
此處的臨時(shí)的AccessKeyId、AccessKeySecret、SecurityToken是為了避免暴露自己的AccessKeyId和AccessKeySecret在前端界面上。下面(步驟二)獲取的STS Token臨時(shí)訪問(wèn)權(quán)限是為了將上傳文件到臨時(shí)OSS bucket,從而得到RL地址。
/**
<ALIBABA_CLOUD_ACCESS_KEY_ID>、<ALIBABA_CLOUD_ACCESS_KEY_SECRET>、<ALIBABA_CLOUD_SECURITY_TOKEN>需替換為STS Token數(shù)據(jù)中獲取的臨時(shí)AccessKeyId、AccessKeySecret、SecurityToken
如果您是用的子賬號(hào)AccessKey,還需要為子賬號(hào)授予權(quán)限AliyunVIAPIFullAccess,請(qǐng)參考http://bestwisewords.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>";
步驟二:調(diào)用GetOssStsToken接口獲取臨時(shí)OSS STS Token
下面提供的代碼段是利用阿里云的Access Key ID和Access Key Secret去請(qǐng)求一個(gè)臨時(shí)的阿里云OSS(對(duì)象存儲(chǔ)服務(wù))STS(Security Token Service)令牌。這個(gè)STS令牌將允許用戶以阿里云視覺智能開放平臺(tái)官方OSS-Bucket為目標(biāo)存儲(chǔ)介質(zhì),上傳文件或數(shù)據(jù)。為便于用戶調(diào)試接口,文件在OSS上的存儲(chǔ)有效期被設(shè)定為1天。
以下是該代碼段的具體含義和步驟:
準(zhǔn)備請(qǐng)求數(shù)據(jù):使用給定的
accessKey
和accessSecret
,構(gòu)建一個(gè)請(qǐng)求字典bodyDict
,該字典包括需要的操作Action
,這里是"GetOssStsToken"
。簽名和構(gòu)建最終URL:根據(jù)提供的參數(shù),包括API的端點(diǎn)(endpoint),HTTP請(qǐng)求方法(建議為POST),以及API的版本號(hào)(這里為
"2020-04-01"
),使用這些信息對(duì)請(qǐng)求進(jìn)行簽名,并構(gòu)建出最終的請(qǐng)求URL。發(fā)送請(qǐng)求:通過(guò)一個(gè)POST請(qǐng)求,將準(zhǔn)備好的簽名和數(shù)據(jù)發(fā)送到設(shè)定的API端點(diǎn)。
處理響應(yīng):通過(guò)異步方式處理服務(wù)端返回的響應(yīng)。如果遇到錯(cuò)誤,回調(diào)函數(shù)將傳遞錯(cuò)誤信息;如果成功,回調(diào)函數(shù)將傳遞包含STS令牌數(shù)據(jù)的字典。
Tool/CallApiClient.m文件中recognizeBankCardWithImageUrl函數(shù)中的bodyDict[@"Action"]、endpoint、apiVersion參數(shù)及bodyDict[@"ImageURL"]這一行代表業(yè)務(wù)參數(shù)。
例如,您想使用通用分割能力,通過(guò)通用分割A(yù)PI文檔可知該能力屬于分割摳圖類目(imageseg20191230),能力名稱為SegmentCommonImage,您需要將endpoint改為imageseg.cn-shanghai.aliyuncs.com,bodyDict[@"Action"]改為SegmentCommonImage,apiVersion為2019-12-30不用修改,bodyDict[@"ImageURL"]參數(shù)名為ImageURL不用修改。獲取結(jié)果的時(shí)候,需要獲取ImageURL,其含義不是銀行卡號(hào),而是分割后的圖片地址。
/**
獲取oss sts token,使用阿里云視覺智能開放平臺(tái)官方OSS-Bucket作為臨時(shí)存儲(chǔ),僅為方便用戶方便調(diào)試接口使用,文件存儲(chǔ)有效期為1天。
*/
+(void)getOssStsTokenWithAk:(NSString*)accessKey andSk:(NSString*)accessSecret responseBlock:(void(^)(NSDictionary *data, NSError *error))block {
NSMutableDictionary *bodyDict = [NSMutableDictionary dictionary];
bodyDict[@"AccessKeyId"] = accessKey;
// 獲取阿里云視覺智能開放平臺(tái)官方OSS-Bucket的stsToken的Action固定為GetOssStsToken
bodyDict[@"Action"] = @"GetOssStsToken";
// 驗(yàn)簽
// 這里endpoint為API訪問(wèn)域名,獲取阿里云視覺智能開放平臺(tái)官方OSS-Bucket的stsToken的域名固定為:viapiutils.cn-shanghai.aliyuncs.com
// httpMethod推薦使用POST
// apiVersion為API版本,獲取阿里云視覺智能開放平臺(tái)官方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"];
//直接發(fā)post請(qǐng)求
[self request:finalUrl responseBlock:^(NSDictionary *responseObject, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
block(nil,error);
}else{
block(responseObject, nil);
}
});
}];
}
步驟三:使用臨時(shí)OSS STS Token將文件上傳到阿里云視覺智能開放平臺(tái)官方OSS Bucket
下面提供的代碼段是將一些圖像數(shù)據(jù)上傳到阿里云對(duì)象存儲(chǔ)服務(wù)(OSS)使用一個(gè)臨時(shí)的STS Token進(jìn)行認(rèn)證和授權(quán)的功能。
以下是該代碼段的具體含義和步驟:
通過(guò)
CallApiClient
獲取STS Token: 使用提供的Access Key ID (accessKeyId
) 和Access Key Secret (accessKeySecret
),以及一個(gè)可能已經(jīng)存在的securityToken
,來(lái)請(qǐng)求從CallApiClient
獲取STS Token。初始化OSS Client: 使用獲取到的臨時(shí)憑證信息,創(chuàng)建一個(gè)OSS客戶端(
OSSClient
)實(shí)例以進(jìn)行后續(xù)的OSS操作。客戶端配置(OSSClientConfiguration
)包括設(shè)置重試次數(shù)、請(qǐng)求超時(shí)和資源傳輸最長(zhǎng)時(shí)間等。設(shè)置上傳請(qǐng)求: 創(chuàng)建一個(gè)
OSSPutObjectRequest
對(duì)象以指定上傳文件的詳細(xì)信息。文件將被上傳到固定的Bucket("viapi-customer-temp"
),而對(duì)象鍵(objectKey
)則由Access Key ID和一個(gè)隨機(jī)UUID組成以確保唯一性。執(zhí)行上傳任務(wù): 調(diào)用
putObject:
方法來(lái)實(shí)際上傳圖像數(shù)據(jù),這個(gè)過(guò)程將會(huì)異步進(jìn)行。獲取上傳文件的URL: 成功上傳后,使用OSS客戶端預(yù)簽名URL功能(
presignConstrainURLWithBucketName:withObjectKey:withExpirationInterval:
)獲取上傳文件的訪問(wèn)URL。回調(diào)返回URL: 執(zhí)行回調(diào)函數(shù)
block
,傳遞上傳文件的URL給調(diào)用者,或者在上傳失敗時(shí)返回錯(cuò)誤信息。
這一步需要在Frameworks中引入libresolv.tbd和AliyunOSSiOS.framework。其中,libresolv.tbd為系統(tǒng)庫(kù),關(guān)于編譯和獲取AliyunOSSiOS.framework,請(qǐng)參見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,用來(lái)初始化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; // 網(wǎng)絡(luò)請(qǐng)求遇到異常失敗后的重試次數(shù)
conf.timeoutIntervalForRequest = 20; // 網(wǎng)絡(luò)請(qǐng)求的超時(shí)時(shí)間
conf.timeoutIntervalForResource = 24*60*60; // 允許資源傳輸?shù)淖铋L(zhǎng)時(shí)間
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地址,調(diào)用視覺智能開放平臺(tái)API
下面提供的代碼段用于上傳文件到OSS后獲取URL地址,調(diào)用視覺智能開放平臺(tái)API。具體的操作步驟與若文件在上海地域OSS進(jìn)行調(diào)用一致。且每一步代碼都已封裝好,完整的示例代碼可下載iOSDemo。
以下是該代碼段的具體含義和步驟:
參數(shù)設(shè)置:通過(guò)構(gòu)建一個(gè)字典
bodyDict
,設(shè)置所需的 API 參數(shù)。這包括:AccessKeyId
和AccessKeySecret
:用于身份驗(yàn)證的憑證。SecurityToken
:作為臨時(shí)安全憑證,通常與STS服務(wù)一起使用。ImageURL
:需要識(shí)別的銀行卡圖片的URL。Action
:指明要調(diào)用的API能力,這里是"RecognizeBankCard"
,即識(shí)別銀行卡。
準(zhǔn)備API請(qǐng)求:
代碼構(gòu)建了一個(gè)簽名后的最終URL
finalUrl
,該過(guò)程涵蓋了設(shè)置API的端點(diǎn)(endpoint)、推薦使用的POST方法、API的版本號(hào),以及根據(jù)訪問(wèn)密鑰、API端點(diǎn)和其他參數(shù)生成簽名的過(guò)程。
執(zhí)行API請(qǐng)求:向構(gòu)建好的
finalUrl
發(fā)送POST請(qǐng)求。這是通過(guò)request:responseBlock:
方法實(shí)現(xiàn)的,該方法的具體實(shí)現(xiàn)尚未顯示,但可假定它負(fù)責(zé)將請(qǐng)求發(fā)送到服務(wù)器并處理響應(yīng)。響應(yīng)處理:請(qǐng)求的響應(yīng)通過(guò)異步回調(diào)
responseBlock
進(jìn)行處理。如果請(qǐng)求成功,API的響應(yīng)數(shù)據(jù)(識(shí)別出的銀行卡信息等)會(huì)通過(guò)block(responseObject, nil)
返回給調(diào)用者。如果請(qǐng)求過(guò)程中出現(xiàn)錯(cuò)誤,通過(guò)block(nil, error)
將錯(cuò)誤返回給調(diào)用者。
Tool/CallApiClient.m
文件中recognizeBankCardWithImageUrl
函數(shù)中的bodyDict[@"Action"]
、endpoint
、apiVersion
參數(shù)及bodyDict[@"ImageURL"]
這一行代表業(yè)務(wù)參數(shù)。
例如,您想使用通用分割能力,通過(guò)通用分割A(yù)PI文檔可知該能力屬于分割摳圖類目(imageseg20191230),能力名稱為SegmentCommonImage
,您需要將endpoint
改為imageseg.cn-shanghai.aliyuncs.com
,bodyDict[@"Action"]
改為SegmentCommonImage
,apiVersion
為2019-12-30
不用修改,bodyDict[@"ImageURL"]
參數(shù)名為ImageURL
不用修改。獲取結(jié)果的時(shí)候,需要獲取ImageURL
,其含義不是銀行卡號(hào),而是分割后的圖片地址。
+(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,能力名稱,請(qǐng)參考具體算法文檔詳情頁(yè)中的Action參數(shù),這里以銀行卡識(shí)別為例:http://bestwisewords.com/document_detail/151893.html
bodyDict[@"Action"] = @"RecognizeBankCard";
// 業(yè)務(wù)參數(shù),請(qǐng)參考具體的AI能力的API文檔
bodyDict[@"ImageURL"] = imageUrl;
bodyDict[@"SecurityToken"] = securityToken;
// 驗(yàn)簽
// 這里endpoint為API訪問(wèn)域名,與類目相關(guān),具體類目的API訪問(wèn)域名請(qǐng)參考:http://bestwisewords.com/document_detail/143103.html
// httpMethod推薦使用POST
// apiVersion為API版本,與類目相關(guān),具體類目的API版本請(qǐng)參考:http://bestwisewords.com/document_detail/464194.html
NSString *finalUrl = [self allKeysGotoSignatureWithDict:bodyDict endpoint:@"ocr.cn-shanghai.aliyuncs.com" accessSecret:accessSecret httpMethod:@"POST" apiVersion:@"2019-12-30"];
//直接發(fā)post請(qǐng)求
[self request:finalUrl responseBlock:^(NSDictionary *responseObject, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
block(nil,error);
}else{
block(responseObject, nil);
}
});
}];
}