AFNetworking详解

2020-12-24  本文已影响0人  Jey

AFNetworking3.0主要是对NSURLSession的封装,NSURLConnection是iOS9以前的

NSURLSession,网络通信核心类

序列化

1. 初始化session
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
/*
 1.调用父类的方法
 2.给url添加“/”
 3.给requestSerializer、responseSerializer设置默认值
 */
- (instancetype)initWithBaseURL:(NSURL *)url
           sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
    //调用父类初始化方法
    self = [super initWithSessionConfiguration:configuration];
    if (!self) {
        return nil;
    }

    // Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
    /*
     为了确保NSURL +URLWithString:relativeToURL: works可以正确执行,在baseurlpath的最后添加‘/’
     */
    //url有值且没有‘/’,那么在url的末尾添加‘/’
    if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
        url = [url URLByAppendingPathComponent:@""];
    }
    self.baseURL = url;
    //给requestSerializer、responseSerializer设置默认值
    self.requestSerializer = [AFHTTPRequestSerializer serializer];
    self.responseSerializer = [AFJSONResponseSerializer serializer];

    return self;
}
2. 设置了一些默认值,返回数据的格式之类
/*
 1.初始化一个session
 2.给manager的属性设置初始值
 */
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    self = [super init];
    if (!self) {
        return nil;
    }

    //设置默认的configuration,配置我们的session
    if (!configuration) {
        configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    }

    //持有configuration
    self.sessionConfiguration = configuration;

    // 设置为delegate的操作队列并发的线程数量1,也就是串行队列,返回数据
    self.operationQueue = [[NSOperationQueue alloc] init];
    self.operationQueue.maxConcurrentOperationCount = 1;

    /*
     -如果完成后需要做复杂(耗时)的处理,可以选择异步队列
     -如果完成后直接更新UI,可以选择主队列
     [NSOperationQueue mainQueue]
     */
    
    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];

    //默认为json解析
    self.responseSerializer = [AFJSONResponseSerializer serializer];

    //设置默认证书 无条件信任证书https认证
    self.securityPolicy = [AFSecurityPolicy defaultPolicy];

#if !TARGET_OS_WATCH
    //网络状态监听
    self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif

    //delegate= value taskid = key
    self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];

    //使用NSLock确保线程安全
    self.lock = [[NSLock alloc] init];
    self.lock.name = AFURLSessionManagerLockName;

    
    //异步的获取当前session的所有未完成的task。其实讲道理来说在初始化中调用这个方法应该里面一个task都不会有
    //后台任务重新回来初始化session,可能就会有先前的任务
    //https://github.com/AFNetworking/AFNetworking/issues/3499
    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        for (NSURLSessionDataTask *task in dataTasks) {
            [self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
        }

        for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
            [self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
        }

        for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
            [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
        }
    }];

    return self;
}
3. 创建一个NSMutableURLRequest
//1.生成request,2.通过request成成task
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                       URLString:(NSString *)URLString
                                      parameters:(id)parameters
                                  uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
                                downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
                                         success:(void (^)(NSURLSessionDataTask *, id))success
                                         failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
    NSError *serializationError = nil;
    /*
     1.先调用AFHTTPRequestSerializer的requestWithMethod函数构建request
     2.处理request构建产生的错误 – serializationError
     //relativeToURL表示将URLString拼接到baseURL后面

     */
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
    if (serializationError) {
        if (failure) {
            //http://fuckingclangwarnings.com/#semantic
            //xcode忽略编译器的警告,diagnostic:诊断的
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
            //completionQueue不存在返回dispatch_get_main_queue
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                failure(nil, serializationError);
            });
#pragma clang diagnostic pop
        }

        return nil;
    }

    //此时的request已经将参数拼接在url后面
    __block NSURLSessionDataTask *dataTask = nil;
    dataTask = [self dataTaskWithRequest:request
                          uploadProgress:uploadProgress
                        downloadProgress:downloadProgress
                       completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
        if (error) {
            if (failure) {
                failure(dataTask, error);
            }
        } else {
            if (success) {
                success(dataTask, responseObject);
            }
        }
    }];

    return dataTask;
}

给NSMutableURLRequest自带的属性赋值,将传入的参数进行编码处理等

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                 URLString:(NSString *)URLString
                                parameters:(id)parameters
                                     error:(NSError *__autoreleasing *)error
{
    //断言如果nil,直接打印出来
    NSParameterAssert(method);
    NSParameterAssert(URLString);

    //我们传进来的是一个字符串,在这里它帮你转成url
    NSURL *url = [NSURL URLWithString:URLString];

    NSParameterAssert(url);

    NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
    //设置请求方式(get、post、put。。。)
    mutableRequest.HTTPMethod = method;

    //将request的各种属性遍历,给NSMutableURLRequest自带的属性赋值
    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        //给设置过得的属性,添加到request(如:timeout)
        if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
            //通过kvc动态的给mutableRequest添加value
            [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
        }
    }

    //将传入的参数进行编码,拼接到url后并返回 coount=5&start=1
    mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
    NSLog(@"request'''''''''%@",mutableRequest);

    return mutableRequest;
}
#pragma mark - AFURLRequestSerialization
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(id)parameters
                                        error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(request);

    NSMutableURLRequest *mutableRequest = [request mutableCopy];

    //给mutableRequest.headfiled赋值
    /*
      1.请求行(状态行):get,url,http协议1.1
     2.请求头:conttent-type,accept-language
     3.请求体:get/post get参数拼接在url后面 post数据放在body
    
     */
    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
        if (![request valueForHTTPHeaderField:field]) {
            [mutableRequest setValue:value forHTTPHeaderField:field];
        }
    }];

    //将我们传入的字典转成字符串
    NSString *query = nil;
    if (parameters) {
        //自定义的解析方式
        if (self.queryStringSerialization) {
            NSError *serializationError;
            query = self.queryStringSerialization(request, parameters, &serializationError);

            if (serializationError) {
                if (error) {
                    *error = serializationError;
                }

                return nil;
            }
        } else {
            //默认解析方式,dic-   count=5&start=1
            switch (self.queryStringSerializationStyle) {
                case AFHTTPRequestQueryStringDefaultStyle:
                    //将parameters传入这个c函数
                    query = AFQueryStringFromParameters(parameters);
                    break;
            }
        }
    }

    //count=5&start=1
    NSLog(@"query:%@",query);
    //最后判断该request中是否包含了GET、HEAD、DELETE(都包含在HTTPMethodsEncodingParametersInURI)。因为这几个method的query是拼接到url后面的。而POST、PUT是把query拼接到http body中的。
    if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
        if (query && query.length > 0) {
            mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
        }
    } else {
        // #2864: an empty string is a valid x-www-form-urlencoded payload
        if (!query) {
            query = @"";
        }
        //函数会判断request的Content-Type是否设置了,如果没有,就默认设置为application/x-www-form-urlencoded
        //application/x-www-form-urlencoded是常用的表单发包方式,普通的表单提交,或者js发包,默认都是通过这种方式
        if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
            [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
        }
        //设置请求体
        [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
    }

    return mutableRequest;
}
4. 通过request生成task
dataTask = [self dataTaskWithRequest:request
                          uploadProgress:uploadProgress
                        downloadProgress:downloadProgress
                       completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
        if (error) {
            if (failure) {
                failure(dataTask, error);
            }
        } else {
            if (success) {
                success(dataTask, responseObject);
            }
        }
    }];
5. 给task添加代理

taskId作为key,delegate作为value,存储在一个字典中self.mutableTaskDelegatesKeyedByTaskIdentifier

 [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
/*
 注意addDelegateForDataTask:这个函数并不是AFURLSessionManagerTaskDelegate的函数,而是AFURLSessionManager的一个函数。这也侧面说明了AFURLSessionManagerTaskDelegate和NSURLSessionTask的关系是由AFURLSessionManager管理的。
 */
- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
                uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
              downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
             completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    // 初始化delegate
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;
    
    /*
     taskidentifier=key delegate=value,确保task唯一
     taskDescription自行设置的,区分是否是当前的session创建的
     */
    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    //函数字面意思是将一个session task和一个AFURLSessionManagerTaskDelegate类型的delegate变量绑在一起,而这个绑在一起的工作是由我们的AFURLSessionManager所做。至于绑定的过程,就是以该session task的taskIdentifier为key,delegate为value,赋值给mutableTaskDelegatesKeyedByTaskIdentifier这个NSMutableDictionary类型的变量。知道了这两者是关联在一起的话,马上就会产生另外的问题 —— 为什么要关联以及怎么关联在一起?
    [self setDelegate:delegate forTask:dataTask];
    //设置回调块
    delegate.uploadProgressBlock = uploadProgressBlock;
    delegate.downloadProgressBlock = downloadProgressBlock;
}

为task设置关联的delegate,,放入self.mutableTaskDelegatesKeyedByTaskIdentifier

- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    //task和delegate都不能为空
    NSParameterAssert(task);
    NSParameterAssert(delegate);

    //加锁确保中间代码块是原子操作,线程安全
    [self.lock lock];
    //将delegate存入字典,以taskid作为key,说明每个task都有各自的代理
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    //设置两个NSProgress的变量 - uploadProgress和downloadProgress,给session task添加了两个KVO事件
    [delegate setupProgressForTask:task];
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}

监听task的事件

#pragma mark - NSProgress Tracking

- (void)setupProgressForTask:(NSURLSessionTask *)task {
    __weak __typeof__(task) weakTask = task;

    //拿到上传下载期望的数据大小
    self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
    self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;
    
     //设置这两个NSProgress对应的cancel、pause和resume这三个状态,正好对应session task的cancel、suspend和resume三个状态
    [self.uploadProgress setCancellable:YES];
    [self.uploadProgress setCancellationHandler:^{
        __typeof__(weakTask) strongTask = weakTask;
        [strongTask cancel];
    }];
    [self.uploadProgress setPausable:YES];
    [self.uploadProgress setPausingHandler:^{
        __typeof__(weakTask) strongTask = weakTask;
        [strongTask suspend];
    }];
    if ([self.uploadProgress respondsToSelector:@selector(setResumingHandler:)]) {
        [self.uploadProgress setResumingHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask resume];
        }];
    }

    [self.downloadProgress setCancellable:YES];
    [self.downloadProgress setCancellationHandler:^{
        __typeof__(weakTask) strongTask = weakTask;
        [strongTask cancel];
    }];
    [self.downloadProgress setPausable:YES];
    [self.downloadProgress setPausingHandler:^{
        __typeof__(weakTask) strongTask = weakTask;
        [strongTask suspend];
    }];

    if ([self.downloadProgress respondsToSelector:@selector(setResumingHandler:)]) {
        [self.downloadProgress setResumingHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask resume];
        }];
    }

    //给task和progress添加kvo,监测进度大小
    [task addObserver:self
           forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))
              options:NSKeyValueObservingOptionNew
              context:NULL];
    [task addObserver:self
           forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))
              options:NSKeyValueObservingOptionNew
              context:NULL];

    [task addObserver:self
           forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))
              options:NSKeyValueObservingOptionNew
              context:NULL];
    [task addObserver:self
           forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))
              options:NSKeyValueObservingOptionNew
              context:NULL];

    //fractionCompleted:任务已经完成的比例,取值为0~1
    [self.downloadProgress addObserver:self
                            forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                               options:NSKeyValueObservingOptionNew
                               context:NULL];
    [self.uploadProgress addObserver:self
                          forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                             options:NSKeyValueObservingOptionNew
                             context:NULL];
}
```
#####6. session的系统回调,拿到绑定的delegate做数据处理
```
// 这里拼接data
- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data
{


// 调用的是AFURLSessionManagerTaskDelegate的didReceiveData方法
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
    [delegate URLSession:session dataTask:dataTask didReceiveData:data];

    if (self.dataTaskDidReceiveData) {
        self.dataTaskDidReceiveData(session, dataTask, data);
    }
}
```

最终都会到这里
```
/*
 task完成之后的回调,成功和失败都会回调这里
 函数讨论:
 注意这里的error不会报告服务期端的error,他表示的是客户端这边的error,比如无法解析hostname或者连不上host主机。
 */
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
  

    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];

    // delegate may be nil when completing a task in the background
    // 如果task是在后台完成的,可能delegate会为nil
    if (delegate) {
        [delegate URLSession:session task:task didCompleteWithError:error];

        // 该task结束了,就移除对应的delegate
        [self removeDelegateForTask:task];
    }
    //自定义Block回调
    if (self.taskDidComplete) {
        self.taskDidComplete(session, task, error);
    }
}
```

在AFURLSessionManagerTaskDelegate中的方法中,数据正常返回的话,开启并行队列解析数据375行,block返回
```
//AF实现的代理!被从urlsession那转发到这
/*
 第一个是获取数据,将responseSerializer和downloadFileURL或data存到userInfo里面
 第二个是根据error是否为空值,做下一步处理
 */
- (void)URLSession:(__unused NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{


#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
    //1)强引用self.manager,防止被提前释放;因为self.manager声明为weak,类似Block
    __strong AFURLSessionManager *manager = self.manager;

    __block id responseObject = nil;
    // 因为NSNotification这个类中本身有userInfo属性,可作为响应函数的参数
    // 不过我在AFNetworking源码中还未发现使用userInfo作为参数的做法,可能需要用户自己实现
    
    /**
     * userInfo中的key值例举如下:
     * AFNetworkingTaskDidCompleteResponseDataKey session 存储task获取到的原始response数据,与序列化后的response有所不同
     * AFNetworkingTaskDidCompleteSerializedResponseKey 存储经过序列化(serialized)后的response
     * AFNetworkingTaskDidCompleteResponseSerializerKey 保存序列化response的序列化器(serializer)
     * AFNetworkingTaskDidCompleteAssetPathKey 存储下载任务后,数据文件存放在磁盘上的位置
     * AFNetworkingTaskDidCompleteErrorKey 错误信息
     */
    //用来存储一些相关信息,来发送通知用的
    __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
    
    //存储responseSerializer响应解析对象
    userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;

    //Performance Improvement from #2672
    //具体可以查看#issue 2672。这里主要是针对大文件的时候,性能提升会很明显
    NSData *data = nil;
    if (self.mutableData) {
        data = [self.mutableData copy];
        //We no longer need the reference, so nil it out to gain back some memory.
        self.mutableData = nil;
    }
    
    //继续给userinfo填数据
    ////如果downloadFileURL存在,即是下载任务就设置下载完成后的文件存储url到字典中
    if (self.downloadFileURL) {
        userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
    } else if (data) {
        //否则就设置对应的NSData数据到字典中
        userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
    }
    // 如果task出错了,处理error信息
    // 所以对应的观察者在处理error的时候,比如可以先判断userInfo[AFNetworkingTaskDidCompleteErrorKey]是否有值,有值的话,就说明是要处理error
    if (error) {
        userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
        // 这里用group方式来运行task完成方法,表示当前所有的task任务完成,才会通知执行其他操作
        // 如果没有实现自定义的completionGroup和completionQueue,那么就使用AFNetworking提供的私有的dispatch_group_t和提供的dispatch_get_main_queue内容
        dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
            if (self.completionHandler) {
                self.completionHandler(task.response, responseObject, error);
            }
            //主线程中发送完成通知
            dispatch_async(dispatch_get_main_queue(), ^{
                [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
            });
        });
    } else {//在没有error时,会先对数据进行一次序列化操作,然后下面的处理就和有error的那部分一样了
        dispatch_async(url_session_manager_processing_queue(), ^{
            NSError *serializationError = nil;
            // 根据对应的task和data将response data解析成可用的数据格式,比如JSON serializer就将data解析成JSON格式
            responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
            // 注意如果有downloadFileURL,意味着data存放在了磁盘上了,所以此处responseObject保存的是data存放位置,供后面completionHandler处理。没有downloadFileURL,就直接使用内存中的解析后的data数据
            if (self.downloadFileURL) {
                responseObject = self.downloadFileURL;
            }
            //写入userInfo

            if (responseObject) {
                userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
            }
             // 序列化的时候出现错误

            if (serializationError) {
                userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
            }
            //回调结果
            //同理,在dispatch组中和特定队列执行回调块
            dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
                if (self.completionHandler) {
                    self.completionHandler(task.response, responseObject, serializationError);
                }
                    //主线程发送通知
                dispatch_async(dispatch_get_main_queue(), ^{
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
                });
            });
        });
    }
#pragma clang diagnostic pop
}

```
注意这里的是通过主队列发送出去
```
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
                if (self.completionHandler) {
                    self.completionHandler(task.response, responseObject, serializationError);
                }
                    //主线程发送通知
                dispatch_async(dispatch_get_main_queue(), ^{
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
                });
            });
```

#### 序列化
requestSerialization:请求前的配置
reponseSerialization:请求后

##### post上传文件-----------------------------------------------
```
// 1. 使用AFHTTPSessionManager的接口
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    NSDictionary *dic = @{@"businessType":@"CC_USER_CENTER",
                          @"fileType":@"image",
                          @"file":@"img.jpeg"
                          };
    [manager POST:@"http://xxx/api/demo/test/file" parameters:dic constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
        // 在这个block中设置需要上传的文件
        NSString *path = [[NSBundle mainBundle] pathForResource:@"1" ofType:@"png"];
        // 将本地图片数据拼接到formData中 指定name
        [formData appendPartWithFileURL:[NSURL fileURLWithPath:path] name:@"file" error:nil];
        
//             或者使用这个接口拼接 指定name和filename
//                    NSData *picdata  =[NSData dataWithContentsOfFile:path];
//                    [formData appendPartWithFileData:picdata name:@"image" fileName:@"image.jpg" mimeType:@"image/jpeg"];
        
    } progress:^(NSProgress * _Nonnull uploadProgress) {
        NSLog(@"progress --- %@",uploadProgress.localizedDescription);
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSLog(@"responseObject-------%@", responseObject);
        dispatch_async(dispatch_get_main_queue(), ^{
            
        });
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        NSLog(@"Error-------%@", error);
    }];
```

```
//构建一个multipartForm的request。并且通过`AFMultipartFormData`类型的formData来构建请求体
- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
                                              URLString:(NSString *)URLString
                                             parameters:(NSDictionary *)parameters
                              constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
                                                  error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(method);
    //method不能是get、head
    NSParameterAssert(![method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"]);

    NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error];
    // 使用initWithURLRequest:stringEncoding:来初始化一个AFStreamingMultipartFormData变量
    // 主要是为了构建bodyStream
    __block AFStreamingMultipartFormData *formData = [[AFStreamingMultipartFormData alloc] initWithURLRequest:mutableRequest stringEncoding:NSUTF8StringEncoding];

    
    if (parameters) {
        for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
            NSData *data = nil;
            if ([pair.value isKindOfClass:[NSData class]]) {
                data = pair.value;
            } else if ([pair.value isEqual:[NSNull null]]) {
                data = [NSData data];
            } else {
                //通常nslog打印会调用description,打印出来的是地址,但是可以重写description,来实现打印出我们想要的类型
                data = [[pair.value description] dataUsingEncoding:self.stringEncoding];
            }

            if (data) {
                // bodyStream构造最主要的部分就在这了(虽然后面requestByFinalizingMultipartFormData函数还会稍微处理一下)
                // 根据data和name构建Request的header和body,后面详解
                [formData appendPartWithFormData:data name:[pair.field description]];
            }
        }
    }
// 参考上面的例子,其实还是往formData中添加数据
    if (block) {
        block(formData);
    }
// 做最终的处理,比如设置一下MultipartRequest的bodyStream或者其特有的content-type等等,后面也会详解
    return [formData requestByFinalizingMultipartFormData];
}
```
1.使用AFStreamingMultipartFormData初始化formdata,里面是AFMultipartBodyStream的实例
2. 把参数转成data,拼接[formData appendPartWithFormData:data name:[pair.field description]];转成AFHTTPBodyPart,添加到一个数组中
3. block回去继续添加图片数据,中间有分隔符要注意下

注意:post文件,content-type是application/x-www-form-urlencoded,formdata是UTF-8 data流数据, AFStreamingMultipartFormData实现分片上传,避免内存暴增
普通post,content-type是applecation/json,formdata是json



上一篇下一篇

猜你喜欢

热点阅读