前言叨逼叨
- iOS上传文件,可能有很多第三方的框架之类的,比如AFN或者Alamofire之类的框架,但是今天要谈论的是原生的API是如何进行文件上传。
兵马未动粮草先行
- 首先明确几点,上传是用post实现的,另外服务器要提供好上传的接口
- 上传的类型包括单个文件,多个文件,或者JSON或者自定义对象等等
- 总思路:
- 手动拼接请求头
- 将要上传的内容转换为二进制数据,并将其拼接到请求体中
一些必要的参数
- application/x-www-form-urlencoded
- 主要向服务器提交用户隐私相关的信息
- 浏览器支持
- multipart/form-data
- 向服务器上传小文件
- 浏览器支持
- application/json
- 向后台服务器提交结构化数据
- RESTful 设计风格需要
- text/xml
- 向后台服务器提交结构化数据
- RESTful 设计风格需要
具体实现
上传单个文件
- 请求格式
Content-Type: multipart/form-data; boundary(分隔线)=(可以随便写,ASCII,字母和数字)
- 数据格式
--boundary\r\n
Content-Disposition: form-data; name="userfile"; filename="aaa.txt"\r\n
Content-Type: application/octet-stream\r\n\r\n
要上传文件的二进制数据
\r\n--boundary--
-
说明
userfile
:负责上传文件脚本中的 字段名,开发的时候,可以咨询后端程序员filename
:将文件保存在服务器上的文件名称-
Content-Type
:客户端告诉服务器上传文件的文件类型text/plain
image/jpg
image/png
image/gif
text/html
application/json
-
application/octet-stream
(8进制流),如果不想告诉服务器具体的文件类型,可以使用这个 Content-Type
注意:每一行末尾需要有一定的 \r\n
提示:有些服务器可以直接使用 \n,但是新浪微博如果使用 \n 上传文件,服务器会返回“没有权限”的错误!
代码实现
生成 formData 二进制数据
#define boundary @"itheima-upload"
/// 生成 formData 二进制数据
///
/// @param fieldName 服务器字段名
/// @param fileName 文件名
/// @param fileData 上传文件二进制数据
///
/// @return formData 二进制数据
- (NSData *)formData:(NSString *)fieldName fileName:(NSString *)fileName fileData:(NSData *)fileData {
NSMutableData *dataM = [NSMutableData data];
// 拼接数据
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 服务器自短命
/// @param fileName 文件名
/// @param fileData 上传文件二进制数据
- (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];
}