IOS 开发 NSURLConnection使用大全(包括请求,上传,下载)详解

本文详细讲解了iOS中使用NSURLConnection进行网络请求的各个方面,包括发送GET、POST请求,设置缓存策略、超时时间,以及文件上传等。还探讨了GET和POST请求的区别,并分析了文件上传的原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

网络开发方案

在iOS中,常见的发送HTTP请求的方案包括:

  • 苹果官方

这里写图片描述

  • 第三方框架

这里写图片描述

NSURLConnection 在IOS9之后,已经被苹果废弃,取而代之的是iOS7之后出现的NSURLSession

1.NSURLConnection发送网络请求

这里写图片描述

1.设置URL NSURL:确定要访问的资源
2.创建请求 NSURLRequest:根据 URL 建立请求,向服务器索要数据
3.发送请求 NSURLConnection:建立网络连接,将请求发送给服务器
4.数据处理 response,data,error处理
- (void)viewDidLoad {
    [super viewDidLoad];

    // 1.URL : 注意,在客户端开发中,协议头一定要手写上去
    NSURL *URL= [NSURL URLWithString:@"http://www.baidu.com"];

    // 2.请求 (request)
    NSURLRequest *request = [NSURLRequest requestWithURL:URL];

    // 3.发送请求--默认是GET请求
    /*
     参数1 : request
     参数2 : queue : 队列,并发队列,completionHandler所在的队列;如果你想在completionHandler里面刷新UI,队列需要时主队列
     参数3 : completionHandler : 请求之后,得到了响应体的回调,这个回调是在子线程执行的(异步回调)
            提示 : completionHandler 其实是在耗时操作执行结束之后回调
            提示  :sendAsynchronousRequest 是耗时的
     */
    [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {

        NSLog(@"%@",[NSThread currentThread]);
        /*
         response : 响应头
         data : 响应体(是程序猿需要的数据,二进制的)
         connectionError : 错误信息
        */

        //是服务器告诉客户端的额外信息
        NSLog(@"响应头 %@",response);
        // 响应体
        NSLog(@"响应体 %@",data);
         //错误信息
        NSLog(@"错误信息 %@",connectionError);

        // 错误处理
        if (connectionError == nil && data != nil && data.length > 0) {
            // 获取data
            // 4.处理响应 : 响应体就是程序猿需要的数据
            NSLog(@"%@",data);
        } else {
            NSLog(@"%@",connectionError);
        }

    }];
}

2.NSURLConnection请求缓存策略和超时时长

缓存策略:cachePolicy

    /*
     cachePolicy : 缓存策略
     NSURLRequestUseProtocolCachePolicy = 0,    使用HTTP协议的默认缓存
     NSURLRequestReloadIgnoringLocalCacheData = 1,  忽略本地缓存,只加载"最新"网络数据 (股票)
     NSURLRequestReturnCacheDataElseLoad = 2,   优先加载缓存数据,如果没有缓存数据就加载最新的数据
     NSURLRequestReturnCacheDataDontLoad = 3,   只加载本地缓存数据 (离线APP / 离线地图)
     */
NSURLRequest *request = [NSURLRequest requestWithURL:URL cachePolicy:0 timeoutInterval:15.0];

超时时长:timeoutInterval

  • 默认网络时长是 60 s
  • 建议超时时长 15~30 秒之间
  • SDWebImage 的默认超时时长是 15 秒
  • AFN 的默认超时时长是 60 秒

3.NSURLConnection可变请求NSMutableURLRequest

设置请求超时等待时间(超过这个时间就算超时,请求失败)- (void)setTimeoutInterval:(NSTimeInterval)seconds;

设置请求方法(比如GET和POST)- (void)setHTTPMethod:(NSString *)method;

设置请求体- (void)setHTTPBody:(NSData *)data;

设置请求头- (void)setValue:(NSString )value forHTTPHeaderField:(NSString )field;

- (void)viewDidLoad {
    [super viewDidLoad];

    // 1.URL : 注意,在客户端开发中,协议头一定要手写上去
    NSURL *URL = [NSURL URLWithString:@"http://www.baidu.com"];

    // 2.创建可变的请求对象,可以告诉服务器一些你想告诉的额外信息
    NSMutableURLRequest *requestM = [NSMutableURLRequest requestWithURL:URL cachePolicy:0 timeoutInterval:15.0];

    // 可以设置请求方法 :
    // 提示 : 苹果的网络框架默认是GET请求
    requestM.HTTPMethod = @"GET";
//    requestM.HTTPMethod = @"POST";
//    requestM.HTTPMethod = @"HEAD";

    // 发送请求时,告诉服务器我的客户端是苹果设备 (User-Agent: iPhone AppleWebKit)
    [requestM setValue:@"iPhone AppleWebKit" forHTTPHeaderField:@"User-Agent"];

    // 3.发送请求
    // NSURLConnection : 默认是GET请求
    [NSURLConnection sendAsynchronousRequest:requestM queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {

        // 4.处理响应 : 响应体就是程序猿需要的数据 (错误处理)
        if (connectionError == nil && data != nil && data.length > 0) {

        } else {
            NSLog(@"%@",connectionError);
        }

    }];
}

4.NSURLConnection响应

此处不谈论状态行,只讨论响应头和响应体

  • 响应头 response

这里写图片描述

  • 响应体 data
    • data 服务器返回的二进制数据,程序员最关心的内容
    • 拿到响应体之后,无法直接使用,需要进行反序列化,转换成OC对象.
        // 4.处理响应 : 响应体就是程序猿需要的数据 (错误处理)
        if (connectionError == nil && data != nil && data.length > 0) {
            // data二进制数据不能直接展示在界面上,客户端无法直接展示/使用二进制数据,我们就需要进行数据解析,把二进制数据转换成客户端可以直接展示/使用的数据

            // 1. dataHTML5字符串类型的数据处理
            NSString *html = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            // baseURL : 相对URL / 相对路径
            [weakSelf.myWebView loadHTMLString:html baseURL:URL];

            // 2. datajson类型的数据处理
            NSDictionary *dict=[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];

        } else {
            NSLog(@"%@",connectionError);
        }

5.NSURLConnectionDataDelegate代理方法

//当接收到服务器的响应(连通了服务器)时会调用
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
//当接收到服务器的数据时会调用(可能会被调用多次,每次只传递部分数据)
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
//当服务器的数据加载完毕时就会调用
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
//请求错误(失败)的时候调用(请求超时\断网\没有网\,一般指客户端错误)
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 

6.NSURLConnection–GET请求

这里写图片描述

- (IBAction)loginClick:(id)sender
{
    NSString *URLString = [NSString stringWithFormat:@"http://localhost/php/login/login.php?username=%@&password=%@",self.userNameTextField.text,self.psdTextField.text];

    // 注意!!! : GET请求,在发松请求之前,最好把URL做一个百分号转义,为了防止URL里面有中文/空格...;造成的URL不识别
    // 提示 : POST请求不需要转义
    // URLQueryAllowedCharacterSet : 转义 ? 后面的查询字符串(URL的参数)
    URLString = [URLString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];

    // URL : URL里面不能有汉字/空格/特殊符号...,如果有服务器不识别.
    NSURL *URL = [NSURL URLWithString:URLString];

    // 请求 : 默认就是GET请求
    NSURLRequest *request = [NSURLRequest requestWithURL:URL];

    // 发送异步请求
    [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
        // 处理响应
        if (connectionError ==nil && data != nil) {

            // 反序列化
            NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
            // 登录是否成功的判断
            if ([result[@"userId"] intValue] == 1) {
                NSLog(@"登录成功");
            } else {
                NSLog(@"登录失败");
            }

        } else {
            NSLog(@"%@",connectionError);
        }
    }];
}

7.NSURLConnection–POST请求

- (IBAction)loginClick:(id)sender
{
    // URL
    NSURL *URL = [NSURL URLWithString:@"http://localhost/php/login/login.php"];

    // 可变请求
    NSMutableURLRequest *requestM = [NSMutableURLRequest requestWithURL:URL];
    // 设置请求方法为POST : 请求方法是可以小写的,但是,建议大写
    requestM.HTTPMethod = @"POST";
    // 设置请求体
    NSString *body = [NSString stringWithFormat:@"username=%@&password=%@",self.userNameTextField.text,self.psdTextField.text];
    requestM.HTTPBody = [body dataUsingEncoding:NSUTF8StringEncoding];

    // 发送请求
    [NSURLConnection sendAsynchronousRequest:requestM queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
        // 处理响应
        if (connectionError == nil && data != nil) {
            // 反序列化
            NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
            if ([result[@"userId"] intValue] == 1) {
                NSLog(@"登陆成功");
            } else {
                NSLog(@"登陆失败");
            }

        } else {
            NSLog(@"%@",connectionError);
        }
    }];
}

8.GET / POST 请求对比

GET : http://localhost/php/login/login.php?username=zhouyu&password=123456

  • 从服务器获取数据,效率比POST高.
  • GET请求能够被缓存
  • 在 HTTP 协议定义中,没有对GET请求的数据大小限制,不过因为浏览器不同一般限制在 2~8K 之间.
  • GET发送请求时,URL中除了资源路径以外,所有的参数(查询字符串)也包装在URL中,并且服务器的访问日志会记录,不要传递敏感信息.
  • 参数格式
    • 在资源路径末尾添加 ? 表示追加参数.
    • 每一个变量及值按照 变量名=变量值 方式设定,不能包含空格或者中文.
    • 多个参数使用 & 连接.
    • 注意 : URL 字符串中如果包含空格或者中文,需要添加百分号转义.

POST :

requestM.HTTPMethod
requestM.HTTPBody
  • 向服务器发送数据,也可以获得服务器处理之后的结果,效率不如GET.
  • POST请求不能被缓存.
  • POST提交数据比较大,大小靠服务器的设定值限制,PHP通常限定 2M.
  • POST发送请求时,URL中只有资源路径,但不包含参数,服务器日志不会记录参数,相对更安全.
  • 参数被包装成二进制的数据体,格式与 GET 基本一致,只是不包含 ?.
    注意 : 所有涉及到用户隐私的数据(密码,银行卡号)一定记住使用 POST 方式传递.

9.NSURLConnection文件上传–单个,多个,文本信息

分为四种情况:

  • 单个文件上传,不带文本信息
  • 单个文件上传,带文本信息
  • 多个文件上传,不带文本信息
  • 多文件上传的主方法,可以上传文本信息

文件上传管理者 FileUpLoadManager (单例设计模式)

FileUploadManger.h

#import <Foundation/Foundation.h>

@interface FileUploadManger : NSObject

+ (instancetype)sharedManger;

/**
 *  单个文件上传,不带文本信息
 *
 *  @param URLString      文件上传的地址
 *  @param serverFileName 服务器接收文件的字段名
 *  @param filePath       文件的路径
 */
- (void)uploadFileWithURLString:(NSString *)URLString serverFileName:(NSString *)serverFileName filePath:(NSString *)filePath;

/**
 *  单个文件上传,带文本信息
 *
 *  @param URLString      文件上传的地址
 *  @param serverFileName 服务器接收文件的字段名
 *  @param filePath       文件的路径
 *  @param textDict       文本信息
 */
- (void)uploadFileWithURLString:(NSString *)URLString serverFileName:(NSString *)serverFileName filePath:(NSString *)filePath textDict:(NSDictionary *)textDict;

/**
 *  多个文件上传,不带文本信息
 *
 *  @param URLString      文件上传的地址
 *  @param serverFileName 服务器接收文件的字段名
 *  @param filePaths      文件的路径数组
 */
- (void)uploadFilesWithURLString:(NSString *)URLString serverFileName:(NSString *)serverFileName filePaths:(NSArray *)filePaths;

/**
 *  多文件上传的主方法,可以上传文本信息
 *
 *  @param URLString      文件上传的地址
 *  @param serverFileName 服务器接收文件的字段名
 *  @param filePaths      文件的路径数组
 *  @param textDict       文件上传时的附带的文本信息
 */
- (void)uploadFilesWithURLString:(NSString *)URLString serverFileName:(NSString *)serverFileName filePaths:(NSArray *)filePaths textDict:(NSDictionary *)textDict;

@end

FileUploadManger.m

#import "FileUploadManger.h"

@implementation FileUploadManger

+ (instancetype)sharedManger {
    static FileUploadManger *instance;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[FileUploadManger alloc] init];
    });
    return instance;
}

#pragma mark - 单个文件上传,不带文本信息
- (void)uploadFileWithURLString:(NSString *)URLString serverFileName:(NSString *)serverFileName filePath:(NSString *)filePath {
    [self uploadFilesWithURLString:URLString serverFileName:serverFileName filePaths:@[filePath] textDict:nil];
}

#pragma mark - 单个文件上传,带文本信息
- (void)uploadFileWithURLString:(NSString *)URLString serverFileName:(NSString *)serverFileName filePath:(NSString *)filePath textDict:(NSDictionary *)textDict {
    [self uploadFilesWithURLString:URLString serverFileName:serverFileName filePaths:@[filePath] textDict:textDict];
}

#pragma mark - 多个文件上传,不带文本信息
- (void)uploadFilesWithURLString:(NSString *)URLString serverFileName:(NSString *)serverFileName filePaths:(NSArray *)filePaths{
    [self uploadFilesWithURLString:URLString serverFileName:serverFileName filePaths:filePaths textDict:nil];
}

#pragma mark - 多文件上传的主方法,可以上传文本信息
- (void)uploadFilesWithURLString:(NSString *)URLString serverFileName:(NSString *)serverFileName filePaths:(NSArray *)filePaths textDict:(NSDictionary *)textDict {
    // URL
    NSURL *URL = [NSURL URLWithString:URLString];

    // 可变请求
    NSMutableURLRequest *requestM = [NSMutableURLRequest requestWithURL:URL];

    // 设置请求头信息 Content-Type:
    [requestM setValue:@"multipart/form-data; boundary=itcast" forHTTPHeaderField:@"Content-Type"];
    // 设置请求方法
    requestM.HTTPMethod = @"POST";
    // 设置请求体
    requestM.HTTPBody = [self getHTTPBodyWithServerFileName:serverFileName filePaths:filePaths textDict:textDict];

    // 发送请求
    [NSURLConnection sendAsynchronousRequest:requestM queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
        // 处理响应
        if (connectionError == nil && data != nil) {

            // 反序列化
            NSArray *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
            NSLog(@"%@",result);

        } else {
            NSLog(@"%@",connectionError);
        }
    }];
}

/**
 *  获取多文件请求体信息(二进制)
 *
 *  @param serverFileName 服务器接收文件的字段名
 *  @param filePaths      文件的路径数组
 *  @param textDict       文件上传时的附带的文本信息
 *
 *  @return 返回二进制的请求体信息
 */
- (NSData *)getHTTPBodyWithServerFileName:(NSString *)serverFileName filePaths:(NSArray *)filePaths textDict:(NSDictionary *)textDict {
    // 定义可变的二进制容器,用于拼接整个请求体二进制信息
    NSMutableData *dataM = [NSMutableData data];

    // 拼接图片文件的二进制请求体信息
    [filePaths enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

        // 定义可变的字符串拼接图片二进制数据之前的字符串
        NSMutableString *stringM = [NSMutableString string];

        // 拼接文件开始的分隔符
        [stringM appendString:@"\r\n--mac\r\n"];
        // 拼接表单数据
        [stringM appendFormat:@"Content-Disposition: form-data; name=%@; filename=%@\r\n",serverFileName,[obj lastPathComponent]];
        // 拼接文件类型 : 我不希望告诉服务器我的文件类型,采用8进制流的类型
        [stringM appendString:@"Content-Type: application/octet-stream\r\n"];
        // 拼接单纯的换行
        [stringM appendString:@"\r\n"];
        // 直接把图片二进制数据之前的字符串转成二进制,拼接到我们的请求体信息里面去
        [dataM appendData:[stringM dataUsingEncoding:NSUTF8StringEncoding]];

        // 拼接图片/文件的二进制数据
        [dataM appendData:[NSData dataWithContentsOfFile:obj]];

        // 拼接最后的换行
        NSString *br = @"\r\n";
        [dataM appendData:[br dataUsingEncoding:NSUTF8StringEncoding]];
    }];

    // 拼接文件上传时的文本信息的请求体信息
    [textDict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {

        // 定义可变的字符串,拼接文件上传时的文本信息
        NSMutableString *stringM = [NSMutableString string];
        // 拼接文本信息开始的分隔符
        [stringM appendString:@"--mac\r\n"];
        // 拼接表单数据
        [stringM appendFormat:@"Content-Disposition: form-data; name=%@\r\n",key];
        // 拼接单纯的换行
        [stringM appendString:@"\r\n"];
        // 拼接文本信息
        [stringM appendFormat:@"%@\r\n",obj];

        // 把文本信息的请求体二进制添加到dataM
        [dataM appendData:[stringM dataUsingEncoding:NSUTF8StringEncoding]];
    }];

    // 拼接文件上传时的结束分隔符
    NSString *end = @"--mac--";
    [dataM appendData:[end dataUsingEncoding:NSUTF8StringEncoding]];

    return dataM.copy;
}

@end

外界调用 ViewController.m

#import "ViewController.h"
#import "FileUploadManger.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    // 参数1
    NSString *URLString = @"http://localhost/php/upload/upload-m.php";
    // 参数2 - 服务器的字段
    NSString *serverFileName = @"userfile[]";
    // 参数3 - 需要上传的文件路径
    NSString *filePath1 = [[NSBundle mainBundle] pathForResource:@"mm01.jpg" ofType:nil];
    NSString *filePath2 = [[NSBundle mainBundle] pathForResource:@"mm02.jpg" ofType:nil];
    NSArray *filePaths = @[filePath1,filePath2];
    // 参数3
    NSDictionary *textDict = [NSDictionary dictionaryWithObject:@"上传的文件都是图片" forKey:@"status"];

    // 单个文件上传,不带文本信息
    [[FileUploadManger sharedManger] uploadFileWithURLString:URLString serverFileName:serverFileName filePath:filePath2];

    // 单个文件上传,带文本信息
//    [[FileUploadManger sharedManger] uploadFileWithURLString:URLString serverFileName:serverFileName filePath:filePath1 textDict:textDict];

    // 多个文件上传,不带文本信息
//    [[FileUploadManger sharedManger] uploadFilesWithURLString:URLString serverFileName:serverFileName filePaths:filePaths];

    // 多个文件上传,并且可以上传文本信息
//    [[FileUploadManger sharedManger] uploadFilesWithURLString:URLString serverFileName:serverFileName filePaths:filePaths textDict:textDict];
}

@end

10.NSURLConnection文件上传原理分析

  • 单个文件上传的原理

    单个文件上传需要注意两点

    第一点 : Content-Type (请求头里面的Content-Type)

     1.文件上传时的Content-Type : "Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryECBUhMhhznU4PqiU"
     2.Content-Type标示 : 告诉服务器我的这个请求是在干什么,是在做文件上传还是普通的请求
     3.普通的请求的Content-Type : "Content-Type: application/x-www-form-urlencoded"
     4.boundary : 文件上传的分隔符,可以自定义,由非中文的字符组成,四个中画线是可以省略的
     5.自定义boundary : boundary=mac
     6.自定义boundary之后的Content-Type : "Content-Type: multipart/form-data; boundary=mac"

第二点 : 请求体 (二进制)

 ------WebKitFormBoundaryECBUhMhhznU4PqiU
 Content-Disposition: form-data; name="userfile"; filename="mm01.jpg"
 Content-Type: image/jpeg


 ------WebKitFormBoundaryECBUhMhhznU4PqiU--

1.分析请求体信息

第一行 : 文件上传的开始的分隔符,前面的两个中划线不能省略,可以自定义,但是一定要跟boundary一模一样 (–mac)

第二行 : 需要处理的表单数据
name=”userfile” 标示 : 服务器接收文件的字段名,”userfile”是服务器提供给客户端使用的;客户端开发者千万不要修改
filename=”mm01.jpg” 标示 : 文件保存到服务器上的文件名,可以使用文件的原名,还可以在上传之前自定义一个名字,提示,一般直接使用原名

第三行 : 要上传的文件的类型 .

第四行 : 单纯的换行 (\r\n) .

第六行 :文件上传结束的分隔符,前面和后面的两个中划线都不能省略

2.总结请求体 (总结请求体的格式)

 --mac\r\n
 Content-Disposition: form-data; name="userfile"; filename="mm01.jpg"\r\n
 Content-Type: image/jpeg\r\n
 \r\n
 data\r\n
 --mac--

总结文件上传的步骤
第一步 : 设置请求头信息里面的Content-Type
第二步 : 设置文件上传的请求体信息

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    // 参数1
    NSString *URLString = @"http://localhost/php/upload/upload.php";
    // 参数2
    NSString *serverFileName = @"userfile";
    // 参数3
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"sunli.jpg" ofType:nil];

    [self uploadFileWithURLString:URLString serverFileName:serverFileName filePath:filePath];
}

/**
 *  单个文件上传的主方法
 *
 *  @param URLString      文件上传的路径
 *  @param serverFileName 服务器接收文件的字段名
 *  @param filePath       要上传的文件的路径(有了文件路径就可以获取文件名和文件的二进制)
 */
- (void)uploadFileWithURLString:(NSString *)URLString serverFileName:(NSString *)serverFileName filePath:(NSString *)filePath
{
    // URL
    NSURL *URL = [NSURL URLWithString:URLString];

    // 可变请求
    NSMutableURLRequest *requestM = [NSMutableURLRequest requestWithURL:URL];
    // 设置请求头信息 Content-Type:
    [requestM setValue:@"multipart/form-data; boundary=itcast" forHTTPHeaderField:@"Content-Type"];
    // 设置请求方法
    requestM.HTTPMethod = @"POST";
    // 设置请求体
    requestM.HTTPBody = [self getHTTPBodyWithServerFileName:serverFileName filePath:filePath];

    // 发送请求
    [NSURLConnection sendAsynchronousRequest:requestM queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
        // 处理响应
        if (connectionError == nil && data != nil) {

            // 反序列化
            NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
            NSLog(@"%@",result);

        } else {
            NSLog(@"%@",connectionError);
        }
    }];
}

/**
 *  获取文件上传的请求体信息(二进制)
 *
 *  @param serverFileName 服务器接收文件的字段名
 *  @param filePath       要上传的文件的路径
 *
 *  @return 返回文件上传的请求体二进制信息
 */
- (NSData *)getHTTPBodyWithServerFileName:(NSString *)serverFileName filePath:(NSString *)filePath {
    /*
     --mac\r\n
     Content-Disposition: form-data; name="userfile"; filename="mm01.jpg"\r\n
     Content-Type: image/jpeg\r\n
     \r\n
     data
     \r\n--mac--
     */

    // 定义可变的二进制容器,拼接请求体的二进制信息
    NSMutableData *dataM = [NSMutableData data];

    // 定义可变字符串,拼接开始的请求体字符串信息
    NSMutableString *stringM = [NSMutableString string];
    // 拼接文件开始上传的分隔符
    [stringM appendString:@"--mac\r\n"];
    // 拼接表单数据
    [stringM appendFormat:@"Content-Disposition: form-data; name=%@; filename=%@\r\n",serverFileName,[filePath lastPathComponent]];
    // 拼接要上传的文件的类型 : 如果你不想告诉服务器你的文件类型具体是什么,就可以使用 "Content-Type: application/octet-stream"
    [stringM appendString:@"Content-Type: image/jpeg\r\n"];
    // 拼接单穿的换行
    [stringM appendString:@"\r\n"];

    // 在这里(拼接文件的二进制数据之前),把前面的请求体字符串转换成二进制,先拼接一次
    NSData *stringM_data = [stringM dataUsingEncoding:NSUTF8StringEncoding];
    [dataM appendData:stringM_data];

    // 拼接文件的二进制数据
    NSData *file_data = [NSData dataWithContentsOfFile:filePath];
    [dataM appendData:file_data];

    NSString *end = @"\r\n--mac--";
    [dataM appendData:[end dataUsingEncoding:NSUTF8StringEncoding]];

    return dataM.copy;
}
  • 多个文件上传的原理

    多个文件上传需要注意两点

第一点 : Content-Type (请求头里面的Content-Type)
     1.文件上传时的Content-Type : "Content-Type: multipart/form-data; boundary=----WebKitFormBoundarymWW2zh14kRXTZzbV"
     2.Content-Type标示 : 告诉服务器我的这个请求是在干什么,是在做文件上传还是普通的请求
     3.普通的请求的Content-Type : "Content-Type: application/x-www-form-urlencoded"
     4.boundary : 文件上传的分隔符,可以自定义,由非中文的字符组成,四个中画线是可以省略的
     5.自定义boundary : boundary=mac
     6.自定义boundary之后的Content-Type : "Content-Type: multipart/form-data; boundary=mac"

第二点 : 请求体信息 (二进制)

 ------WebKitFormBoundarymWW2zh14kRXTZzbV
 Content-Disposition: form-data; name="userfile[]"; filename="mm01.jpg"
 Content-Type: image/jpeg


 ------WebKitFormBoundarymWW2zh14kRXTZzbV
 Content-Disposition: form-data; name="userfile[]"; filename="mm02.jpg"
 Content-Type: image/jpeg


 ------WebKitFormBoundarymWW2zh14kRXTZzbV
 Content-Disposition: form-data; name="status"

 今天大家都不在状态!为什么?
 ------WebKitFormBoundarymWW2zh14kRXTZzbV--

1.分析请求体信息

--mac\r\n   (第一张图片开始的分隔符)
 Content-Disposition: form-data; name="userfile[]"; filename="mm01.jpg"\r\n
 Content-Type: image/jpeg\r\n
 \r\n
 data\r\n   (第一张图片的二进制信息)
 --itcast\r\n   (第二张图片开始的分隔符)
 Content-Disposition: form-data; name="userfile[]"; filename="mm02.jpg"\r\n
 Content-Type: image/jpeg\r\n
 \r\n
 data\r\n   (第二张图片的二进制信息)
 --itcast\r\n   (文本信息的开始分隔符)
 Content-Disposition: form-data; name="status"\r\n  (status : 标示服务器接收文本信息的字段名,千万不要修改)
 \r\n
 今天大家都不在状态!为什么?\r\n
 --mac--

总结多文件上传的步骤 :
第一步 : 设置请求头信息里面的Contnent-Type
第二步 : 设置请求体二进制信息

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    // 参数1
    NSString *URLString = @"http://localhost/php/upload/upload-m.php";
    // 参数2
    NSString *serverFileName = @"userfile[]";
    // 参数3
    NSString *filePath1 = [[NSBundle mainBundle] pathForResource:@"mm01.jpg" ofType:nil];
    NSString *filePath2 = [[NSBundle mainBundle] pathForResource:@"mm02.jpg" ofType:nil];
    NSArray *filePaths = @[filePath1,filePath2];
    // 参数3
    //Content-Disposition: form-data; name="status"
    //今天是个好日子
    NSDictionary *textDict = [NSDictionary dictionaryWithObject:@"今天大家都不在状态!为什么?" forKey:@"status"];

    [self uploadFilesWithURLString:URLString serverFileName:serverFileName filePaths:filePaths textDict:textDict];
}

/**
 *  多文件上传的主方法,可以上传文本信息
 *
 *  @param URLString      文件上传的地址
 *  @param serverFileName 服务器接收文件的字段名
 *  @param filePaths      文件的路径数组
 *  @param textDict       文件上传时的附带的文本信息
 */
- (void)uploadFilesWithURLString:(NSString *)URLString serverFileName:(NSString *)serverFileName filePaths:(NSArray *)filePaths textDict:(NSDictionary *)textDict {
    // URL
    NSURL *URL = [NSURL URLWithString:URLString];

    // 可变请求
    NSMutableURLRequest *requestM = [NSMutableURLRequest requestWithURL:URL];

    // 设置请求头信息 Content-Type:
    [requestM setValue:@"multipart/form-data; boundary=itcast" forHTTPHeaderField:@"Content-Type"];
    // 设置请求方法
    requestM.HTTPMethod = @"POST";
    // 设置请求体
    requestM.HTTPBody = [self getHTTPBodyWithServerFileName:serverFileName filePaths:filePaths textDict:textDict];

    // 发送请求
    [NSURLConnection sendAsynchronousRequest:requestM queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
        // 处理响应
        if (connectionError == nil && data != nil) {

            // 反序列化
            NSArray *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
            NSLog(@"%@",result);

        } else {
            NSLog(@"%@",connectionError);
        }
    }];
}

/**
 *  获取多文件请求体信息(二进制)
 *
 *  @param serverFileName 服务器接收文件的字段名
 *  @param filePaths      文件的路径数组
 *  @param textDict       文件上传时的附带的文本信息
 *
 *  @return 返回二进制的请求体信息
 */
- (NSData *)getHTTPBodyWithServerFileName:(NSString *)serverFileName filePaths:(NSArray *)filePaths textDict:(NSDictionary *)textDict
{
    /*
     --mac\r\n
     Content-Disposition: form-data; name="userfile[]"; filename="mm01.jpg"\r\n
     Content-Type: image/jpeg\r\n
     \r\n
     data\r\n
     --mac\r\n
     Content-Disposition: form-data; name="userfile[]"; filename="mm02.jpg"\r\n
     Content-Type: image/jpeg\r\n
     \r\n
     data\r\n
     --mac\r\n
     Content-Disposition: form-data; name="status"\r\n
     \r\n
     今天大家都不在状态!为什么?\r\n
     --mac--
     */

    // 定义可变的二进制容器,用于拼接整个请求体二进制信息
    NSMutableData *dataM = [NSMutableData data];

    // 拼接图片文件的二进制请求体信息
    [filePaths enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

        // 定义可变的字符串拼接图片二进制数据之前的字符串
        NSMutableString *stringM = [NSMutableString string];

        // 拼接文件开始的分隔符
        [stringM appendString:@"\r\n--mac\r\n"];
        // 拼接表单数据
        [stringM appendFormat:@"Content-Disposition: form-data; name=%@; filename=%@\r\n",serverFileName,[obj lastPathComponent]];
        // 拼接文件类型 : 我不希望告诉服务器我的文件类型,采用8进制流的类型
        [stringM appendString:@"Content-Type: application/octet-stream\r\n"];
        // 拼接单纯的换行
        [stringM appendString:@"\r\n"];
        // 直接把图片二进制数据之前的字符串转成二进制,拼接到我们的请求体信息里面去
        [dataM appendData:[stringM dataUsingEncoding:NSUTF8StringEncoding]];

        // 拼接图片/文件的二进制数据
        [dataM appendData:[NSData dataWithContentsOfFile:obj]];

        // 拼接最后的换行
        NSString *br = @"\r\n";
        [dataM appendData:[br dataUsingEncoding:NSUTF8StringEncoding]];
    }];

    // 拼接文件上传时的文本信息的请求体信息
    [textDict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {

        // 定义可变的字符串,拼接文件上传时的文本信息
        NSMutableString *stringM = [NSMutableString string];
        // 拼接文本信息开始的分隔符
        [stringM appendString:@"--mac\r\n"];
        // 拼接表单数据
        [stringM appendFormat:@"Content-Disposition: form-data; name=%@\r\n",key];
        // 拼接单纯的换行
        [stringM appendString:@"\r\n"];
        // 拼接文本信息
        [stringM appendFormat:@"%@\r\n",obj];

        // 把文本信息的请求体二进制添加到dataM
        [dataM appendData:[stringM dataUsingEncoding:NSUTF8StringEncoding]];
    }];

    // 拼接文件上传时的结束分隔符
    NSString *end = @"--mac--";
    [dataM appendData:[end dataUsingEncoding:NSUTF8StringEncoding]];

    return dataM.copy;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值