前言叨逼叨
- iOS上傳文件,可能有很多第三方的框架之類的,比如AFN或者Alamofire之類的框架,但是今天要談?wù)摰氖窃腁PI是如何進(jìn)行文件上傳。
兵馬未動(dòng)糧草先行
- 首先明確幾點(diǎn),上傳是用post實(shí)現(xiàn)的,另外服務(wù)器要提供好上傳的接口
- 上傳的類型包括單個(gè)文件,多個(gè)文件,或者JSON或者自定義對(duì)象等等
- 總思路:
- 手動(dòng)拼接請(qǐng)求頭
- 將要上傳的內(nèi)容轉(zhuǎn)換為二進(jìn)制數(shù)據(jù),并將其拼接到請(qǐng)求體中
一些必要的參數(shù)
- application/x-www-form-urlencoded
- 主要向服務(wù)器提交用戶隱私相關(guān)的信息
- 瀏覽器支持
- multipart/form-data
- 向服務(wù)器上傳小文件
- 瀏覽器支持
- application/json
- 向后臺(tái)服務(wù)器提交結(jié)構(gòu)化數(shù)據(jù)
- RESTful 設(shè)計(jì)風(fēng)格需要
- text/xml
- 向后臺(tái)服務(wù)器提交結(jié)構(gòu)化數(shù)據(jù)
- RESTful 設(shè)計(jì)風(fēng)格需要
具體實(shí)現(xiàn)
上傳單個(gè)文件
- 請(qǐng)求格式
Content-Type: multipart/form-data; boundary(分隔線)=(可以隨便寫,ASCII,字母和數(shù)字)
- 數(shù)據(jù)格式
--boundary\r\n
Content-Disposition: form-data; name="userfile"; filename="aaa.txt"\r\n
Content-Type: application/octet-stream\r\n\r\n
要上傳文件的二進(jìn)制數(shù)據(jù)
\r\n--boundary--
-
說(shuō)明
userfile
:負(fù)責(zé)上傳文件腳本中的 字段名,開發(fā)的時(shí)候,可以咨詢后端程序員filename
:將文件保存在服務(wù)器上的文件名稱-
Content-Type
:客戶端告訴服務(wù)器上傳文件的文件類型text/plain
image/jpg
image/png
image/gif
text/html
application/json
-
application/octet-stream
(8進(jìn)制流),如果不想告訴服務(wù)器具體的文件類型,可以使用這個(gè) Content-Type
注意:每一行末尾需要有一定的 \r\n
提示:有些服務(wù)器可以直接使用 \n,但是新浪微博如果使用 \n 上傳文件,服務(wù)器會(huì)返回“沒(méi)有權(quán)限”的錯(cuò)誤!
代碼實(shí)現(xiàn)
生成 formData 二進(jìn)制數(shù)據(jù)
#define boundary @"itheima-upload"
/// 生成 formData 二進(jìn)制數(shù)據(jù)
///
/// @param fieldName 服務(wù)器字段名
/// @param fileName 文件名
/// @param fileData 上傳文件二進(jìn)制數(shù)據(jù)
///
/// @return formData 二進(jìn)制數(shù)據(jù)
- (NSData *)formData:(NSString *)fieldName fileName:(NSString *)fileName fileData:(NSData *)fileData {
NSMutableData *dataM = [NSMutableData data];
// 拼接數(shù)據(jù)
NSMutableString *strM = [NSMutableString string];
[strM appendFormat:@"--%@\r\n", boundary];
[strM appendFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", fieldName, fileName];
[strM appendString:@"Content-Type: application/octet-stream\r\n\r\n"];
[dataM appendData:[strM dataUsingEncoding:NSUTF8StringEncoding]];
[dataM appendData:fileData];
NSString *tail = [NSString stringWithFormat:@"\r\n--%@--", boundary];
[dataM appendData:[tail dataUsingEncoding:NSUTF8StringEncoding]];
return dataM.copy;
}
上傳單個(gè)文件
/// 上傳單個(gè)文件
///
/// @param fieldName 服務(wù)器自短命
/// @param fileName 文件名
/// @param fileData 上傳文件二進(jìn)制數(shù)據(jù)
- (void)uploadFile:(NSString *)fieldName fileName:(NSString *)fileName fileData:(NSData *)fileData {
// 1. url - 負(fù)責(zé)上傳的腳本
NSURL *url = [NSURL URLWithString:@"http://localhost/post/upload.php"];
// 2. request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
NSString *typeValue = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
[request setValue:typeValue forHTTPHeaderField:@"Content-Type"];
request.HTTPBody = [self formData:fieldName fileName:fileName fileData:fileData];
// 3. connection
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSLog(@"%@", [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL]);
}];
}
測(cè)試代碼
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"001.png" withExtension:nil];
NSData *data = [NSData dataWithContentsOfURL:fileURL];
[self uploadFile:@"userfile" fileName:@"abc" fileData:data];
}