天天看点

NSURLConnection(苹果原生上传单文件)

其实NSURLConnection在iOS7.0之后就逐渐被NSURLSession取代,尤其是iOS9.0出来之后,基本很少有公司再去适配iOS7.0之前的用户了,当然可能,某些比较老旧的项目依然保留着NSURLConnection+NSOperation实现网络请求。就像AFN依旧保留着AFHTTPRequestOperationManager接口。通过

#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= ) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= ) || TARGET_WATCH_OS
           

宏定义,实现跨版本的适配。

那么NSURLConnection和NSURLSession的区别到底在哪呢?

  • 异步请求不需要NSOperation包装
  • 支持后台运行的网络任务(后台上传下载)
  • 根据每个Session做配置,不需要在整个app层面共享配置
  • 支持网络操作的取消和断点续传(继承系统类,重写main方法)
  • 改进的了授权机制的处理

以下就是通过NSURLConnection实现的文件上传代码,老早之前写的了,因为开发中使用到的地方还是非常少的,除非是遇到AFN上传解决不了的时候,这时候才会去想到回归原生方法去实现上传,比如说上传某些音频文件的时候,有时候AFN就不能完成上传。

注意:

  • POST实现⽂文件上传,采⽤用NSURLConnection发送异步请求。
  • 调⽤用API需要知参数:

    服务器地址(上传文件脚本地址)

    服务器允许上传⽂文件key值 ⽂文件名(key)+本地⽂文件路径(value)

    服务器允许上传⽂文本⽂文件的key值(key)+⽂文本内容(value)

#import <Foundation/Foundation.h>

@interface XYUploadManager : NSObject

+ (instancetype)sharedXYUploadManager;

//上传文件的API
- (void)postFileToServer:(NSString *)serverUrl serverKey:(NSString *)serverKey fileDict:(NSDictionary *)fileDict paramater:(NSDictionary *)paramater;

//格式化上传数据
- (NSData *)getHttpBodyDataWithServerKey:(NSString *)serverKey fileDict:(NSDictionary *)fileDict;

//格式化上传数据,附带文本信息
- (NSData *)getHttpBodyDataWithServerKey:(NSString *)serverKey fileDict:(NSDictionary *)fileDict paramater:(NSDictionary *)paramater;

@end
           
//宏定义请求公共字符串
#define kBoundary @"Boundary"

#import "XYUploadManager.h"

@implementation XYUploadManager

/**
 *  单例上传文件工具类
 *
 *  @return 实例化单例对象
 */
+ (instancetype)sharedXYUploadManager{

    static id _instance;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

        _instance = [[self alloc] init];

    });

    return _instance;
}

/**
 *  上传文件API接口
 *
 *  @param serverUrl 请求服务器地址
 *  @param fileDict  文件名称:fileName(key)= 文件路径filePath(value)
 *  @param paramater 服务器接收上传文件key = 上传的文本信息value
 */
- (void)postFileToServer:(NSString *)serverUrl serverKey:(NSString *)serverKey fileDict:(NSDictionary *)fileDict paramater:(NSDictionary *)paramater{

    NSURL *url = [NSURL URLWithString:serverUrl];

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

    request.HTTPMethod = @"POST";

    NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",kBoundary];

    [request setValue:contentType forHTTPHeaderField:@"Content-Type"];

    request.HTTPBody = [self getHttpBodyDataWithServerKey:serverKey fileDict:fileDict paramater:paramater];

    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {

    }];

}

/**
 *  格式化上传数据(只有文件信息)
 *
 *  @param serverKey 服务器接受上传文件的key值
 *  @param fileDict  文件名称:fileName(key)= 文件路径filePath(value)
 *
 *  @return 上传的二进制数据
 */
- (NSData *)getHttpBodyDataWithServerKey:(NSString *)serverKey fileDict:(NSDictionary *)fileDict{

    NSMutableData *data = [NSMutableData data];

//    遍历上传参数的数据字典,将上传文件路径和保存的名称都取出来
    [fileDict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {

        NSString *fileName = key;

        NSString *filePath = obj;

        NSMutableString *headerStr = [NSMutableString stringWithFormat:@"\r\n--%@\r\n",kBoundary];

        [headerStr appendFormat:@"Content-Disposition: form-data; name=%@; filename=%@\r\n",serverKey,fileName];

//        获取上传文件的类型
        NSString *contentType = [self getFileMIMETypeWithFilePath:filePath];

        [headerStr appendFormat:@"Content-Type: %@\r\n\r\n",contentType];

//        获取上边界的二进制数据
        NSData *headerData = [headerStr dataUsingEncoding:NSUTF8StringEncoding];

        [data appendData:headerData];

//        获取上传文件的二进制数据
        NSData *fileData = [NSData dataWithContentsOfFile:filePath];

        [data appendData:fileData];

    }];

//    整个上传数据文件的下边界(或每个上传文件都可以有个自己的下边界)
    NSMutableString *footerStr = [NSMutableString stringWithFormat:@"\r\n--%@--\r\n",kBoundary];

//    获取下边界的二进制数据
    NSData *footerData = [footerStr dataUsingEncoding:NSUTF8StringEncoding];

    [data appendData:footerData];

    return data;
}

/**
 *  格式化上传数据(含文本信息)
 *
 *  @param serverKey 服务器接受上传文件的key值
 *  @param fileDict  文件名称:fileName(key)= 文件路径filePath(value)
 *  @param paramater 服务器接受文本文件key = 上传的文本信息value
 *
 *  @return 上传文件的二进制数据
 */

- (NSData *)getHttpBodyDataWithServerKey:(NSString *)serverKey fileDict:(NSDictionary *)fileDict paramater:(NSDictionary *)paramater{

    NSMutableData *data = [NSMutableData data];

//    遍历上传参数的数据字典,将上传文件路径和保存的名称都取出来
    [fileDict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {

        NSString *fileName = key;

        NSString *filePath = obj;

        NSMutableString *headerStr = [NSMutableString stringWithFormat:@"\r\n--%@\r\n",kBoundary];

        [headerStr appendFormat:@"Content-Disposition: form-data; name=%@; filename=%@\r\n",serverKey,fileName];

//        获取上传文件的类型
        NSString *contentType = [self getFileMIMETypeWithFilePath:filePath];

        [headerStr appendFormat:@"Content-Type: %@\r\n\r\n",contentType];

//        获取上边界的二进制数据
        NSData *headerData = [headerStr dataUsingEncoding:NSUTF8StringEncoding];

        [data appendData:headerData];

//        获取上传文件的二进制数据
        NSData *fileData = [NSData dataWithContentsOfFile:filePath];

        [data appendData:fileData];


    }];

//    文本信息
    [paramater enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {

//        key:服务器接收文本信息的key值
//        obj:上传到服务器的文本信息

        NSMutableString *headerStr = [NSMutableString stringWithFormat:@"\r\n--%@\r\n",kBoundary];

        [headerStr appendFormat:@"Content-Disposition: form-data; name=%@\r\n\r\n",key];

        NSData *headerData = [headerStr dataUsingEncoding:NSUTF8StringEncoding];

        [data appendData:headerData];

        NSString *str = obj;

        NSData *strData = [str dataUsingEncoding:NSUTF8StringEncoding];

        [data appendData:strData];


    }];

//    整个上传数据文件的下边界(或每个上传文件都可以有个自己的下边界)
    NSMutableString *footerStr = [NSMutableString stringWithFormat:@"\r\n--%@--\r\n",kBoundary];

//    获取下边界的二进制数据
    NSData *footerData = [footerStr dataUsingEncoding:NSUTF8StringEncoding];

    [data appendData:footerData];

    return data;

}

/**
 *  获取上传文件类型
 *
 *  @param path 本地文件的路径
 *
 *  @return Content-Type
 */
- (NSString *)getFileMIMETypeWithFilePath:(NSString *)path{

//    本地文件的url
    NSString *urlStr = [NSString stringWithFormat:@"file://%@",path];

//    中文路径百分号转译
    urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

    NSURL *url = [NSURL URLWithString:urlStr];

    NSURLRequest *request = [NSURLRequest requestWithURL:url];

//    定义响应对象,服务器的响应要放在这个响应对象中
    NSURLResponse *response = nil;

    [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:NULL];

    //    文件类型:response中的属性
    return response.MIMEType;

}


@end
           

通过这坨代码以及我刚才发的简单封装AFN的工具类的代码就能看出,为什么在实际开发中不使用苹果原生的网络访问方法了吧,太繁琐了。但是,第三方框架再牛逼也是基于对苹果原生的封装,如果想要提升自己,还是要从原生一步一步搞懂实现原理,将来才有可能成为像AFN作者这样的大牛,当然,如果只是为了完成开发的网络请求,直接AFN的GET/POST就能满足你的要求了!

继续阅读