AFNetworking3.1.0源码分析(八)AFHTTPRequestSerializer 之AFStreamingMultipartFormData下

本章接着上章继续讲解AFStreamingMultipartFormData设置上传文件(文本,图像,视频)时对请求的设置。

1:首先说下在iOS网络请求中以POST/PUT形式发送请求是,设置请求体的两种形式:HTTPBody 和 HTTPBodyStream

HTTPBody:适用于普通的post/put请求(大部分接口请求都使用这种)因为发送的请求体占用的内存很少。

HTTPBodyStream(实际上是NSInputStream对象:)适用于发送大文件(文本,图像,视频)等,因为其占用内存比较多,使用读写流的方式,节约内存,控制内存的使用率。


2:在读写数据流的过程中使用到两个类:NSInputStream 和 NSOutputStream:

NSOutputStream :写文件,它是要将已存在的内存(buffer)里的数据写入文件
NSInputStream :读文件,所以要记住它是要将文件的内容读到内存(自己声明的一段buffer)里


3:AFNetworking的实现过程:

3.1:初始化AFStreamingMultipartFormData

- (instancetype)initWithURLRequest:(NSMutableURLRequest *)urlRequest
                    stringEncoding:(NSStringEncoding)encoding
{
    self = [super init];
    if (!self) {
        return nil;
    }
    //持有外部创建的NSMutableURLRequest
    self.request = urlRequest;
    //设置字符串转换成NSdata的编码(使用utf8),在创建httpbodystream的每一个分段(AFHTTPBodyPart)的时候使用到此编码
    self.stringEncoding = encoding;
    /*
     设置请求体的分割符:[NSString stringWithFormat:@"Boundary+%08X%08X", arc4random(), arc4random()];
     请求头MIMEType用到:([self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", self.boundary] forHTTPHeaderField:@"Content-Type"];)
     */
    self.boundary = AFCreateMultipartFormBoundary();
    /*
        初始化AFMultipartBodyStream request的httpBodyStream用到([self.request setHTTPBodyStream:self.bodyStream])
     */
    self.bodyStream = [[AFMultipartBodyStream alloc] initWithStringEncoding:encoding];

    return self;
}

  3.1.1 初始化AFMultipartBodyStream:

- (instancetype)initWithStringEncoding:(NSStringEncoding)encoding {
    self = [super init];
    if (!self) {
        return nil;
    }
   /*设置编码code,使用utf8,在序列化自身的时候使用到:
    (AFMultipartBodyStream *bodyStreamCopy = [[[self class] allocWithZone:zone] initWithStringEncoding:self.stringEncoding];)
    */
    self.stringEncoding = encoding;
    /*
        保存请求体httpbodystream中的分段对象(AFHTTPBodyPart)
     */
    
    self.HTTPBodyParts = [NSMutableArray array];
    
    //设置默认inputstream读取内存缓冲区的最大值
    self.numberOfBytesInPacket = NSIntegerMax;

    return self;
}


3.2 解析参数并构造请求体中的每段数据:

3.2.1 解析请求参数:AFQueryStringPairsFromDictionary(parameters)

        3.2.2 构造请求体中的每段数据:

- (void)appendPartWithFormData:(NSData *)data
                          name:(NSString *)name
{
    NSParameterAssert(name);

    //设置每段数据的头信息
    NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];
    [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"", name] forKey:@"Content-Disposition"];

    
    [self appendPartWithHeaders:mutableHeaders body:data];
}
- (void)appendPartWithHeaders:(NSDictionary *)headers
                         body:(NSData *)body
{
    NSParameterAssert(body);

    //构造请求体
    AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
    bodyPart.stringEncoding = self.stringEncoding;//计算文字大小(字符串转换为data)的编码
    bodyPart.headers = headers;//分段请求头信息
    bodyPart.boundary = self.boundary;//分段字符串
    bodyPart.bodyContentLength = [body length]; //请求体每段内容的数据长度(比如发送的内容是图片就是图片的大小)
    bodyPart.body = body;

    [self.bodyStream appendHTTPBodyPart:bodyPart];//将构造好的分段数据添加到AFMultipartBodyStream 里的数组中
}

3.2.3自定义添加分段数据信息(可以自定义参数设置每个片段的信息):

- (void)appendPartWithFileData:(NSData *)data
                          name:(NSString *)name
                      fileName:(NSString *)fileName
                      mimeType:(NSString *)mimeType
{
    NSParameterAssert(name);
    NSParameterAssert(fileName);
    NSParameterAssert(mimeType);

    NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];
    [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
    [mutableHeaders setValue:mimeType forKey:@"Content-Type"];

    [self appendPartWithHeaders:mutableHeaders body:data];
}

3.3设置最终的网络请求参数:

- (NSMutableURLRequest *)requestByFinalizingMultipartFormData {
    //如果用户未设置任何参数,返回原始默认的(普通form)请求方式
    if ([self.bodyStream isEmpty]) {
        return self.request;
    }

    // 循环设置请求体中每个片段的起始标示(因为起始的分割符合结束的分隔符是不一样的:(起始:  --------分隔符)(结束: ------分隔符-----))
    [self.bodyStream setInitialAndFinalBoundaries];
    //设置请求体httpBodyStream
    [self.request setHTTPBodyStream:self.bodyStream];

    //设置请求的MIMEType
    [self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", self.boundary] forHTTPHeaderField:@"Content-Type"];
    //设置请求发送的数据长度
    [self.request setValue:[NSString stringWithFormat:@"%llu", [self.bodyStream contentLength]] forHTTPHeaderField:@"Content-Length"];

    return self.request;
}

3.3.1计算请求内容的length(包含每个段的headers 和内容)

- (unsigned long long)contentLength {
    unsigned long long length = 0;

    //开始分段标识
    NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding];
    length += [encapsulationBoundaryData length];
   
    //header
    NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding];
    length += [headersData length];
     //内容(图片、文本、视频等)
    length += _bodyContentLength;
  //结束分段标识
    NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]);
    length += [closingBoundaryData length];

    return length;
}








©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值