參考
iOS開發(fā)探索-Base64編碼
iOS URL編碼&base64編碼
URL安全的Base64編碼,解碼
為什么需要對(duì)URL進(jìn)行編碼
網(wǎng)絡(luò)標(biāo)準(zhǔn)RFC 1738中規(guī)定URL中只能包含英文字、阿拉伯?dāng)?shù)字以及一些特殊字符。具體包括:字母和數(shù)組[0-9a-zA-Z]、特殊符號(hào)-._~:?#[]@!$&'()*+,;=` 。URL中若含有這些規(guī)定符號(hào)以外的符號(hào),URL都是不合法的,因此對(duì)于一些含有特殊符號(hào)或中文字符的URL,在請(qǐng)求前需要對(duì)URL進(jìn)行編碼。
常見的 URL編碼方式
下面URL作為本例中編碼演示的原始URL
NSString *urlString = @"http://lotheve.com/個(gè)人信息?name=URL編碼&other=這個(gè)參數(shù)有很多符號(hào)!*'();:@&=+ $,/?#[]%";
URL編碼 方式一
使用Foundation框架中的stringByAddingPercentEscapesUsingEncoding方法對(duì)url進(jìn)行編碼(該方法在iOS9后棄用)。編解碼代碼如下:
NSString *encodeUrlString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; //url編碼(utf8)
NSString *decodeUrlString = [encodeUrlString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; //url解碼
NSLog(@"編碼后的URL:%@",encodeUrlString);
NSLog(@"解碼后的URL:%@",decodeUrlString);
打印結(jié)果:

使用這種方式編碼,從結(jié)果中可以看到除了中文被編碼,!*'();:@&=+ $,/?#[]% 這些符號(hào)中只有空格#[]%被編碼。事實(shí)上這種方式只能對(duì)以下14種字符編碼(不含冒號(hào)):`#%^{}[]|"<>空格這種方式適合于編碼路徑或者參數(shù)中包含中文或其他非法字符的URL,不適合于編碼參數(shù)中包含URL保留字符【如?/&=等】的URL,因?yàn)槿魠?shù)中含有例如&的保留字符,用這種方法不會(huì)對(duì)&進(jìn)行編碼,URL在識(shí)別的時(shí)候會(huì)將&作為參數(shù)的分隔符,最終識(shí)別的參數(shù)會(huì)有誤。不過(guò)可以先將需要轉(zhuǎn)碼的參數(shù)編碼之后再設(shè)置給URL,這樣就不會(huì)有問題了。
URL編碼 方式二
使用Foundation框架中的stringByAddingPercentEncodingWithAllowedCharacters方法對(duì)url進(jìn)行編碼(適用于iOS9之后)。通過(guò)傳入一個(gè)NSCharacterSet字符集對(duì)象決定對(duì)哪些字符進(jìn)行編碼,所有字符集對(duì)象中的字符都會(huì)被編碼。系統(tǒng)提供了一些包裝好的字符集對(duì)象,也可以自定義需要編碼的字符集對(duì)象。
NSString *encodeUrlString = [urlString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]; //編碼
NSString *decodeUrlString = [encodeUrlString stringByRemovingPercentEncoding]; //解碼
NSLog(@"編碼后的URL:%@",encodeUrlString);
NSLog(@"解碼后的URL:%@",decodeUrlString);
打印結(jié)果:

各種系統(tǒng)包裝好的編碼字符集包含的字符范圍:
URLFragmentAllowedCharacterSet “#%<>[]^`{|}
URLHostAllowedCharacterSet “#%/<>?@\^`{|}
URLPasswordAllowedCharacterSet “#%/:<>?@[]^`{|}
URLPathAllowedCharacterSet “#%;<>?[]^`{|}
URLQueryAllowedCharacterSet “#%<>[]^`{|}
URLUserAllowedCharacterSet “#%/:<>?@[]^`
URL編碼 方式三
使用CoreFoundation框架中的函數(shù)對(duì)url進(jìn)行編碼(該方法在iOS9之后同樣棄用)。
NSString *encodeUrlString = CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)urlString, NULL, (CFStringRef)@"!*'();:@&=+ $,/?%#[]", kCFStringEncodingUTF8)); //編碼
NSString *decodeUrlString = CFBridgingRelease(CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault, (CFStringRef)encodeUrlString, (CFStringRef)@"!*'();:@&=+ $,/?%#[]", kCFStringEncodingUTF8)); //解碼
NSLog(@"編碼后的URL:%@",encodeUrlString);
NSLog(@"解碼后的URL:%@",decodeUrlString);
打印結(jié)果:

編碼函數(shù)中的第四個(gè)參數(shù)為需要編碼的字符的集合字符串,url中所有在這些字符范圍內(nèi)的字符都會(huì)被編碼,可以根據(jù)實(shí)際需求定要轉(zhuǎn)碼的字符范圍。
URL編碼一個(gè)容易混淆的地方
雖說(shuō)是URL編碼,但實(shí)際操作其實(shí)是對(duì)字符串進(jìn)行編碼,因此在編碼過(guò)程中編碼對(duì)象【字符串】并沒有什么“URL光環(huán)”,編碼時(shí)完全根據(jù)需要編碼的字符集合對(duì)整個(gè)URL進(jìn)行遍歷編碼,即便是對(duì)于協(xié)議名http,只要目標(biāo)編碼字符集合中含有“http”在,協(xié)議同樣也會(huì)被編碼。
Base64編、解碼
NSString *origStr = @"這是一個(gè)要經(jīng)過(guò)base64編碼的原始字符串";
NSData *data = [origStr dataUsingEncoding:NSUTF8StringEncoding];
NSString *base64EncodeString = [data base64EncodedStringWithOptions:0]; //編碼
NSData *decodeData = [[NSData alloc] initWithBase64EncodedString:base64EncodeString options:0]; //解碼
NSString *decodeString = [[NSString alloc] initWithData:decodeData encoding:NSUTF8StringEncoding];
NSLog(@"原始字符串:%@",origStr);
NSLog(@"base64編碼字符串:%@",base64EncodeString);
NSLog(@"base64解碼后字符串:%@",decodeString);
什么是 URL安全的Base64編碼
指的是將標(biāo)準(zhǔn)base64編碼后的字符串用url傳遞的場(chǎng)景,由于標(biāo)準(zhǔn)base64編碼結(jié)果中可能含有/+符號(hào),因此應(yīng)該先對(duì)base64編碼結(jié)果中的/+分別替換成-_,不然拼接到url中會(huì)有問題(/+在url作為保留字符有其自身的作用,如同&=?)。
如何進(jìn)行 URL安全的Base64編碼
Base64可以將二進(jìn)制轉(zhuǎn)碼成可見字符方便進(jìn)行http傳輸,但是base64轉(zhuǎn)碼時(shí)會(huì)生成“+”,“/”,“=”這些被URL進(jìn)行轉(zhuǎn)碼的特殊字符,導(dǎo)致兩方面數(shù)據(jù)不一致。
我們可以在發(fā)送前將“+”,“/”,“=”替換成URL不會(huì)轉(zhuǎn)碼的字符,接收到數(shù)據(jù)后,再將這些字符替換回去,再進(jìn)行解碼。
OC實(shí)現(xiàn):
#pragma - 將saveBase64編碼中的"-","_"字符串轉(zhuǎn)換成"+","/",字符串長(zhǎng)度余4倍的位補(bǔ)"="
+(NSData*)safeUrlBase64Decode:(NSString*)safeUrlbase64Str
{
// '-' -> '+'
// '_' -> '/'
// 不足4倍長(zhǎng)度,補(bǔ)'='
NSMutableString * base64Str = [[NSMutableString alloc]initWithString:safeUrlbase64Str];
base64Str = (NSMutableString * )[base64Str stringByReplacingOccurrencesOfString:@"-" withString:@"+"];
base64Str = (NSMutableString * )[base64Str stringByReplacingOccurrencesOfString:@"_" withString:@"/"];
NSInteger mod4 = base64Str.length % 4;
if(mod4 > 0)
[base64Str appendString:[@"====" substringToIndex:(4-mod4)]];
NSLog(@"Base64原文:%@", base64Str);
return [GTMBase64 decodeData:[base64Str dataUsingEncoding:NSUTF8StringEncoding]];
}
#pragma - 因?yàn)锽ase64編碼中包含有+,/,=這些不安全的URL字符串,所以要進(jìn)行換字符
+(NSString*)safeUrlBase64Encode:(NSData*)data
{
// '+' -> '-'
// '/' -> '_'
// '=' -> ''
NSString * base64Str = [GTMBase64 stringByEncodingData:data];
NSMutableString * safeBase64Str = [[NSMutableString alloc]initWithString:base64Str];
safeBase64Str = (NSMutableString * )[safeBase64Str stringByReplacingOccurrencesOfString:@"+" withString:@"-"];
safeBase64Str = (NSMutableString * )[safeBase64Str stringByReplacingOccurrencesOfString:@"/" withString:@"_"];
safeBase64Str = (NSMutableString * )[safeBase64Str stringByReplacingOccurrencesOfString:@"=" withString:@""];
NSLog(@"safeBase64編碼:%@", safeBase64Str);
return safeBase64Str;
}