前言叨逼叨
- iOS上傳文件篷扩,可能有很多第三方的框架之類的,比如AFN或者Alamofire之類的框架,但是今天要談?wù)摰氖窃腁PI是如何進行文件上傳曹阔。
兵馬未動糧草先行
- 首先明確幾點半开,上傳是用post實現(xiàn)的,另外服務(wù)器要提供好上傳的接口
- 上傳的類型包括單個文件赃份,多個文件寂拆,或者JSON或者自定義對象等等
- 總思路:
- 手動拼接請求頭
- 將要上傳的內(nèi)容轉(zhuǎn)換為二進制數(shù)據(jù),并將其拼接到請求體中
一些必要的參數(shù)
- application/x-www-form-urlencoded
- 主要向服務(wù)器提交用戶隱私相關(guān)的信息
- 瀏覽器支持
- multipart/form-data
- 向服務(wù)器上傳小文件
- 瀏覽器支持
- application/json
- 向后臺服務(wù)器提交結(jié)構(gòu)化數(shù)據(jù)
- RESTful 設(shè)計風格需要
- text/xml
- 向后臺服務(wù)器提交結(jié)構(gòu)化數(shù)據(jù)
- RESTful 設(shè)計風格需要
具體實現(xiàn)
上傳單個文件
- 請求格式
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
要上傳文件的二進制數(shù)據(jù)
\r\n--boundary--
-
說明
userfile
:負責上傳文件腳本中的 字段名,開發(fā)的時候园蝠,可以咨詢后端程序員filename
:將文件保存在服務(wù)器上的文件名稱-
Content-Type
:客戶端告訴服務(wù)器上傳文件的文件類型text/plain
image/jpg
image/png
image/gif
text/html
application/json
-
application/octet-stream
(8進制流)渺蒿,如果不想告訴服務(wù)器具體的文件類型,可以使用這個 Content-Type
注意:每一行末尾需要有一定的 \r\n
提示:有些服務(wù)器可以直接使用 \n彪薛,但是新浪微博如果使用 \n 上傳文件茂装,服務(wù)器會返回“沒有權(quán)限”的錯誤!
代碼實現(xiàn)
生成 formData 二進制數(shù)據(jù)
#define boundary @"itheima-upload"
/// 生成 formData 二進制數(shù)據(jù)
///
/// @param fieldName 服務(wù)器字段名
/// @param fileName 文件名
/// @param fileData 上傳文件二進制數(shù)據(jù)
///
/// @return formData 二進制數(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;
}
上傳單個文件
/// 上傳單個文件
///
/// @param fieldName 服務(wù)器自短命
/// @param fileName 文件名
/// @param fileData 上傳文件二進制數(shù)據(jù)
- (void)uploadFile:(NSString *)fieldName fileName:(NSString *)fileName fileData:(NSData *)fileData {
// 1. url - 負責上傳的腳本
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]);
}];
}
測試代碼
- (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];
}