一.前言
之前对AFNetWorking二次封装上传图片的方法是以文件流的方式上传的,上传文件有两张方式:1.已文件流的方式上传 2.将image转成NSData,在进行base64加密上传(NSString imageString = [data base64EncodedStringWithOptions:0];转成base64字符串imageString,再传给给后台,在传参中需要添加图片的类型(@“png”或@“jpg”等))。两种方式选择哪一种,取决于后台以哪种方式接受文件。对于第二种方式很简单,第一种方式AFNetWoring也封装好了,下面我们从最底层文件传输探讨,也就是AFNetWoring内部是怎么实现文件流传输的。即探讨一下
-(void)appendPartWithFileData:(NSData *)data
name:(NSString *)name
fileName:(NSString *)fileName
mimeType:(NSString *)mimeType;
到底是怎么实现的
二.单个文件传输(AFN在个别情况下不能完成,可直接用最底层方法传输)
/ /分割符:必须不能包含中文
#define CZBoundary @"baidu"
/**
* 上传文件
*
* @param fileData fileData 上传文件的二进制数据
* @param fieldName fieldName 上传到服务器的字段名
* @param fileNamefileName上传文件名
*/
- (void)uploadFile:(NSData *)fileData fieldName:(NSString
*)fieldName fileName:(NSString *)fileName
{
//url upload.php 是验证上传的脚本 它内部规定了保存在服务器哪个路径 和传入服务器的字段名 这是后台给的路径
NSURL *url = [NSURL URLWithString:@"http://192.168.51.9/post/upload.php"];
//request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//设置请求方法
request.HTTPMethod = @"POST";
/* Content-Type
multipart/form-data; boundary=itcast*/
//设置contentType
[request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",CZBoundary] forHTTPHeaderField:@"Content-Type"];
//设置请求体
request.HTTPBody = [self fileData:fileData fieldName:fieldName fileName:fileName];
//发送请求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(@"%@---%@",response,dict);
}];
}
/**
* 设置请求体二进制
*
* @param fileData fileData 上传文件的二进制数据
* @param fieldName fieldName description
* @param fileNamefileName上传文件名
*
* @return 请求体
*/
- (NSData *)fileData:(NSData *)fileData fieldName:(NSString
*)fieldName fileName:(NSString *)fileName
{
//创建一个可变二进制数据
NSMutableData *dataM = [NSMutableData data];
//创建一个可变字符串
NSMutableString *strM = [NSMutableString string];
[strM appendFormat:@"--%@\r\n",CZBoundary];
[strM appendFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n",fieldName,fileName];
[strM appendFormat:@"Content-Type: application/octet-stream\r\n\r\n"];
//将strM的内容拼接到dataM中
[dataM appendData:[strM dataUsingEncoding:NSUTF8StringEncoding]];
//拼接文件的二进制数据
[dataM appendData:fileData];
//拼接结尾标识
NSString *tail = [NSString stringWithFormat:@"\r\n--%@--",CZBoundary];
[dataM appendData:[tail dataUsingEncoding:NSUTF8StringEncoding]];
//拼接文件的二进制数据
return dataM;
}
-
调用示范
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent: (UIEvent *)event { NSString *filePath = [[NSBundle mainBundle]pathForResource:@"1.png" ofType:nil]; NSData *fileData = [NSData dataWithContentsOfFile:filePath]; [self uploadFile:fileData fieldName:@"userfile" fileName:@"1.png"]; }
三.多个文件传输
/**
* 上传多个文件
*
* @param fileDict 多个文件字典
* @param fieldName 上传到服务器的字段
* @param params 普通参数字典
*/
- (void)uploadFile:(NSDictionary *)fileDict fieldName:
(NSString *)fieldName params:(NSDictionary *)params
{
//url upload.php 是验证上传的脚本 它内部规定了保存在服务器哪个路径 这是后台给的路径
NSURL *url = [NSURL URLWithString:@"http://localhost/post/upload-m.php"];
//request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//设置请求方法
request.HTTPMethod = @"POST";
/* Content-Type
multipart/form-data; boundary=itcast*/
//设置contentType
[request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",CZBoundary] forHTTPHeaderField:@"Content-Type"];
//设置请求体
request.HTTPBody = [self fileData:fileDict fieldName:fieldName params:params];
//发送请求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(@"%@---%@",response,dict);
}];
}
/**
* 设置请求体二进制
*
@param fileDict 多个文件字典
* @param fieldName 上传到服务器的字段
* @param params 普通参数字典
*
* @return 请求体
*/
- (NSData *)fileData:(NSDictionary *)fileDict fieldName:
(NSString *)fieldName params:(NSDictionary *)params
{
//创建一个可变二进制数据
NSMutableData *dataM = [NSMutableData data];
//遍历文件字典
[fileDict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull fileName, id fileData, BOOL * _Nonnull stop) {
//创建一个可变字符串
NSMutableString *strM = [NSMutableString string];
[strM appendFormat:@"--%@\r\n",CZBoundary];
[strM appendFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n",fieldName,fileName];
[strM appendFormat:@"Content-Type: application/octet-stream\r\n\r\n"];
//将strM的内容拼接到dataM中
[dataM appendData:[strM dataUsingEncoding:NSUTF8StringEncoding]];
//拼接文件的二进制数据
[dataM appendData:fileData];
//拼接当前文件结尾标志
NSString *tail = [NSString stringWithFormat:@"\r\n--%@\r\n",CZBoundary];
[dataM appendData:[tail dataUsingEncoding:NSUTF8StringEncoding]];
}];
//遍历普通参数 key是接受普通参数的字段 obj是普通参数内容
[params enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
//创建一个可变字符串
NSMutableString *strM = [NSMutableString string];
[strM appendFormat:@"Content-Disposition: form-data; name=%@\r\n\r\n",key];
[strM appendFormat:@"%@",obj];
[dataM appendData:[strM dataUsingEncoding:NSUTF8StringEncoding]];
}];
//拼接结尾标识
NSString *tail = [NSString stringWithFormat:@"\r\n--%@--",CZBoundary];
[dataM appendData:[tail dataUsingEncoding:NSUTF8StringEncoding]];
//
// //拼接文件的二进制数据
return dataM;
}
-
多个文件上传调用示范
/ /分割符:必须不能包含中文 #define CZBoundary @"baidu" -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent: (UIEvent *)event { NSString *filePath1 = [[NSBundle mainBundle]pathForResource:@"1.png" ofType:nil]; NSData *fileData1 = [NSData dataWithContentsOfFile:filePath1]; NSString *filePath2 = [[NSBundle mainBundle]pathForResource:@"11.png" ofType:nil]; NSData *fileData2 = [NSData dataWithContentsOfFile:filePath2]; / /普通参数 NSString *username = @"file"; //接受文件数据的字段名:userfile[] 接受普通参数的字段 名:username //文件字典 NSDictionary *fileDict = @{@"1.png":fileData1,@"11.png":fileData2}; //普通参数字典 @"username"后台给的传入普通参数的字段名 value自己传入的参数 NSDictionary *params = @{@"username":username}; //确定要传入的文件 找后台要路径和传入服务器的字段名 确定参数 [self uploadFile:fileDict fieldName:@"userfile[]" params:params]; }