NSURLSession详解

对NSURLSession整体感知

相关核心类介绍 :

NSURLRequest常用属性与方法

// 判断是否支持安全编码
@property (class, readonly) BOOL supportsSecureCoding;

// 只读属性 获取请求对象的URL
@property (nullable, readonly, copy) NSURL *URL;

// 只读属性 缓存策略
@property (readonly) NSURLRequestCachePolicy cachePolicy;

//只读属性 获取请求的超时时限
@property (readonly) NSTimeInterval timeoutInterval;

//主文档地址 这个地址用来存放缓存
@property (nullable, readonly, copy) NSURL *mainDocumentURL;

// 获取网络请求的服务类型
@property (readonly) NSURLRequestNetworkServiceType networkServiceType;

//获取是否允许使用服务商蜂窝网络
@property (readonly) BOOL allowsCellularAccess;

/* 类方法创建实例  */
// 创建的请求对象 默认使用NSURLRequestUseProtocolCachePolicy缓存逻辑 默认请求超时时限为60s
+ (instancetype)requestWithURL:(NSURL *)URL;

// 创建的请求对象 创建时设置缓存逻辑和超时时限
+ (instancetype)requestWithURL:(NSURL *)URL cachePolicy:(NSURLRequestCachePolicy)cachePolicy timeoutInterval:(NSTimeInterval)timeoutInterval;

/* 实例方法创建实例  */
// init方法进行对象的创建 默认使用NSURLRequestUseProtocolCachePolicy缓存逻辑 默认请求超时时限为60s
- (instancetype)initWithURL:(NSURL *)URL;

// init方法进行对象的创建
- (instancetype)initWithURL:(NSURL *)URL cachePolicy:(NSURLRequestCachePolicy)cachePolicy timeoutInterval:(NSTimeInterval)timeoutInterval;
属性:

// 缓存策略
typedef NS_ENUM(NSUInteger, NSURLRequestCachePolicy)
{
    //默认的缓存协议
    NSURLRequestUseProtocolCachePolicy = 0,

    //无论有无本地缓存数据 都进行从新请求
    NSURLRequestReloadIgnoringLocalCacheData = 1,

    //忽略本地和远程的缓存数据 未实现的策略
    NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4, 

    //无论有无缓存数据 都进行从新请求
    NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData,

    //先检查缓存 如果没有缓存再进行请求
    NSURLRequestReturnCacheDataElseLoad = 2,

    //类似离线模式,只读缓存 无论有无缓存都不进行请求
    NSURLRequestReturnCacheDataDontLoad = 3,

    //未实现的策略
    NSURLRequestReloadRevalidatingCacheData = 5, // Unimplemented
};

网络请求的服务类型

typedef NS_ENUM(NSUInteger, NSURLRequestNetworkServiceType)
{

    NSURLNetworkServiceTypeDefault = 0,   // Standard internet traffic

    NSURLNetworkServiceTypeVoIP = 1,  // Voice over IP control traffic

    NSURLNetworkServiceTypeVideo = 2, // Video traffic

    NSURLNetworkServiceTypeBackground = 3, // Background traffic

    NSURLNetworkServiceTypeVoice = 4     // Voice data
};

HTTP/HTTPS协议相关请求的属性和方法()

// HPPT请求方式 默认为“GET”
@property (copy) NSString *HTTPMethod;

//通过字典设置HTTP请求头的键值数据
@property (nullable, copy) NSDictionary<NSString *, NSString *> *allHTTPHeaderFields;

//设置http请求头中的字段值
- (void)setValue:(nullable NSString *)value forHTTPHeaderField:(NSString *)field;

//向http请求头中添加一个字段
- (void)addValue:(NSString *)value forHTTPHeaderField:(NSString *)field;

//设置http请求体 用于POST请求
@property (nullable, copy) NSData *HTTPBody;

//设置http请求体的输入流
@property (nullable, retain) NSInputStream *HTTPBodyStream;

//设置发送请求时是否发送cookie数据
@property BOOL HTTPShouldHandleCookies;

//设置请求时是否按顺序收发 默认禁用 在某些服务器中设为YES可以提高网络性能
@property BOOL HTTPShouldUsePipelining;

NSURLSessionConfiguration

注:

1、NSURLSession会拷贝configuration。所以session一旦初始化结束就不会再更改configuration。除非初始化一个session。

2、如果NSURLRequest中也做了一些指定。session也会遵循NSURLRequest的限定,但是如果configuration有更加严格的限定,仍以configuration为主。

// 默认配置使用的是持久化的硬盘缓存,存储证书到用户钥匙链。存储cookie到shareCookie。
@property (class, readonly, strong) NSURLSessionConfiguration *defaultSessionConfiguration;

// 非持久化存储(当程序作废session时,所有的ephemeral session 数据会立即清除。此外,程序终止或者收到内存警告或者内存压力时立即清除。)
@property (class, readonly, strong) NSURLSessionConfiguration *ephemeralSessionConfiguration;

/* 后台传输,系统进程(identifier 是configuration的唯一标示,不能为空或nil.)
(如果程序是被系统正常终止的和重新启动,可以使用同一个identifier创建configuration和session,并且能恢复终止时的传输状态。如果程序是被用户在手动退出的,session会取消所有的后台任务,届时不能再唤醒application,如果想要再次开始传输,必须用户手动开启application。)*/
+ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));


/* 如果在后台任务正在传输时程序退出,可以使用这个identifier在程序重新启动时创建一个新的configuration和session关联之前传输*/
@property (nullable, readonly, copy) NSString *identifier;

/* default cache policy for requests */
@property NSURLRequestCachePolicy requestCachePolicy;

/* 请求超时时间,默认60s */
@property NSTimeInterval timeoutIntervalForRequest;

/* 给指定resource设定一个超时时间,resource需要在时间到达之前完成。默认是7天。 */
@property NSTimeInterval timeoutIntervalForResource;

/* 指定网络传输类型 */
@property NSURLRequestNetworkServiceType networkServiceType;

/* 是否使用蜂窝网络,默认是yes. */
@property BOOL allowsCellularAccess;

/* 存储cookie,清除存储,直接set为nil即可。
对于默认和后台的session,使用sharedHTTPCookieStorage。
对于短暂的session,cookie仅仅储存到内存,session失效时会自动清除。*/
@property(retain) NSHTTPCookieStorage *HTTPCookieStorage

/* 默认为yes,是否提供来自shareCookieStorge的cookie,如果想要自己提供cookie,可以使用HTTPAdditionalHeaders来提供。*/
@property BOOL HTTPShouldSetCookies


/* 证书存储,如果不使用,可set为nil.
默认和后台session,默认使用的sharedCredentialStorage.
短暂的session使用一个私有存储在内存中。session失效会自动清除。*/
@property(retain) NSURLCredentialStorage *URLCredentialStorage

/* 缓存NSURLRequest的response。
默认的configuration,默认值的是sharedURLCache。
后台的configuration,默认值是nil
短暂的configuration,默认一个私有的cache于内存,session失效,cache自动清除。*/
@property(retain) NSURLCache *URLCache

// 表示当后台传输结束时,是否启动app.这个属性只对 backgroundSessionConfigurationWithIdentifier: 生效,其他configuration类型会自动忽略该值。默认值是YES。
@property BOOL sessionSendsLaunchEvents

/*是否由系统根据性能自动裁量后台任务。默认值是NO。同sessionSendsLaunchEvent一样,只对后台configuration有效。
如果传输的是大数据,最好设置成YES。让系统在最佳性能是安排任务。这属性仅当application在前台时会有用。到后台时系统会默认它的值是YES。   */
@property(getter=isDiscretionary) BOOL discretionary

// 同时连接一个host的最大数。iOS默认是4.APP是作为一个整体来看的。
@property NSInteger HTTPMaximumConnectionsPerHost

DataTask-用来请求资源,然后服务器返回数据,再内存中存储为NSData格式。default,ephemeral,shared Session支持data task。background session不支持。 
Upload Task-和DataTask类似,只不过在请求的时候提供了request body。并且background Session支持 upload task。 
Download Task-下载内容到硬盘上,所有类型的Session都支持。

Stream Task- 通过数据流的方式传输数据。

示例:

基本get请求

NSString *urlStr = @"http://127.0.0.1/一张屏幕截图.png";
    // 如果链接字符串中有中文或其他特殊字符的话需要先编码一下
    urlStr = [urlStr stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
    NSURL *url = [NSURL URLWithString:urlStr];
    
//    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    // 可以配置缓存策略与请求超时时间
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:60];
    // 默认是get请求
    request.HTTPMethod = @"GET";
    
    // 使用全局session
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSLog(@"返回的数据:%@", data);
        
        dispatch_async(dispatch_get_main_queue(), ^{
            UIImage *image = [UIImage imageWithData:data];
            UIImageView *imagV = [[UIImageView alloc] initWithFrame:CGRectMake(50, 50, 200, 300)];
            imagV.image = image;
            [self.view addSubview:imagV];
        });
        
    }];
    [dataTask resume];

post请求

NSString *urlStr = @"http://127.0.0.1/upload/abc/Snip20180629_3.png";
    // 如果链接字符串中有中文或其他特殊字符的话需要先编码一下
    urlStr = [urlStr stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
    NSURL *url = [NSURL URLWithString:urlStr];
    
    // 可以配置缓存策略与请求超时时间
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:60];
    // 默认是get请求
    request.HTTPMethod = @"POST";
    
    // 设置请求体
    request.HTTPBody = [@"username=lisi&password=123456" dataUsingEncoding:NSUTF8StringEncoding];
    
    // 使用全局session
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSLog(@"返回的数据:%@", data);
        
        dispatch_async(dispatch_get_main_queue(), ^{
            UIImage *image = [UIImage imageWithData:data];
            UIImageView *imagV = [[UIImageView alloc] initWithFrame:CGRectMake(50, 50, 200, 300)];
            imagV.image = image;
            [self.view addSubview:imagV];
        });
        
    }];
    
    [dataTask resume];
普通下载
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    
    NSString *urlStr = @"http://127.0.0.1/p.002-p.003.mp3";
    // 如果链接字符串中有中文或其他特殊字符的话需要先编码一下
    urlStr = [urlStr stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
    NSURL *url = [NSURL URLWithString:urlStr];

    // 因为NSURLSessionDelegate是只读属性,所以只能通过类方法设置代理
    NSURLSession *downLoadSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]];
    NSURLSessionDownloadTask *downLoadTask = [downLoadSession downloadTaskWithURL:url];
    // 注:如果使用block回调, 就不会执行代理方法
//    NSURLSessionDownloadTask *downLoadTask = [downLoadSession downloadTaskWithURL:url completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//
//    }];
    [downLoadTask resume];

}

#pragma mark - NSURLSessionDownloadDelegate

/*  下载过程中调用
 bytesWritten :单次下载大小
 totalBytesWritten:当前一共下载的大小
 totalBytesExpectedToWrite:文件总大小
 */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
    
        NSLog(@"下载进度:%f", totalBytesWritten * 1.00 / totalBytesExpectedToWrite);

    
}

/* 下载完成后调用,默认是tmp目录下,下载完成后会自动删除,所以需要自己手动移动到Documents
 session:当前下载的会话
 downloadTask:当前下载的任务
 location:默认临时文件的存储路径

 */
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location{
    NSLog(@"下载完成");
    NSLog(@"下载文件的路径:%@", location);
    NSString *pathStr = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
    // 下载的文件必须改个名字才能转存到documents目录
    pathStr = [pathStr stringByAppendingString:@"/DownLoadFile"];
    NSLog(@"documents目录:%@", pathStr);
    NSError *error;
    NSFileManager *fillM = [NSFileManager defaultManager];
    
    BOOL isSuccess = [fillM moveItemAtPath:location.path toPath:pathStr error:&error];
    if (!isSuccess) {
        NSLog(@"失败");
        NSLog(@"错误信息:%@,,,,,,,失败原因%@, 恢复建议:%@", error.userInfo, error.localizedFailureReason, error.localizedRecoverySuggestion);
    }
}

// 下载恢复时调用
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
 didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes{
    
}

#pragma mark - NSURLSessionTaskDelegate

// 不管成功失败,都会调用,只是失败的时候error有值
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(nullable NSError *)error{
    
}

大文件断点续传

1、正常的暂停,下载

2、进程被杀死之后恢复下载

- (IBAction)startOrPuseBtnClick:(UIButton *)sender {
    
    if (sender.tag == 10) {
        // 开始
        if (self.isFirst) {
            [self startDownTaskWithUrl:@"http://127.0.0.1/Microsoft PowerPoint.app.zip"];
            
        }else{
            
            if (self.resumeData) {
                self.downLoadTask = [[self backGroundDownSession] downloadTaskWithResumeData:self.resumeData];
                [self.downLoadTask resume];
            }
        }
        
    }else if (sender.tag == 20){
        // 暂停
        self.isFirst = NO;
        [self puseDownload];
        
    }
}

// 伪单例 (session没必要每次都创建新的,而且后台config配置需要保证identifier唯一)
- (NSURLSession *)backGroundDownSession{

    static NSURLSession *session = nil;
    static dispatch_once_t onceT;
    dispatch_once(&onceT, ^{
        NSURLSessionConfiguration *sessionConfigtion = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"lianxia"];
        session = [NSURLSession sessionWithConfiguration:sessionConfigtion
                            delegate:self
                       delegateQueue:[[NSOperationQueue alloc] init]];
    });
    return session;
}

// 根据url开启对应的下载任务
- (void)startDownTaskWithUrl:(NSString *)downloadUrlStr{
    
    // 对链接进行编码
    NSString *urlStr = [downloadUrlStr stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
    NSURL *downLoadUrl = [NSURL URLWithString:urlStr];
    NSURLSessionDownloadTask *downLoadTask = [self.backGroundDownSession downloadTaskWithURL:downLoadUrl];
    self.downLoadTask = downLoadTask;
    [downLoadTask resume];
    
}

// 暂停下载
- (void)puseDownload{
    
    // 取消下载 resumeData:下载的数据
    [self.downLoadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
        self.resumeData = [resumeData mutableCopy];
    }];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.downProgressView.progress = 0.00;
    self.isFirst = YES;
   
}

#pragma mark - NSURLSessionDownloadDelegate

/*  下载过程中调用
 bytesWritten :单次下载大小
 totalBytesWritten:当前一共下载的大小
 totalBytesExpectedToWrite:文件总大小
 */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
    
        NSLog(@"下载进度:%f", totalBytesWritten * 1.00 / totalBytesExpectedToWrite);
    dispatch_async(dispatch_get_main_queue(), ^{
        self.downProgressView.progress = totalBytesWritten * 1.00 / totalBytesExpectedToWrite;
        self.progressLB.text = [NSString stringWithFormat:@"%.2f%%", totalBytesWritten * 1.00 / totalBytesExpectedToWrite * 100];
    });
    
}

/* 下载完成后调用,默认是tmp目录下,下载完成后会自动删除,所以需要自己手动移动到Documents
 session:当前下载的会话
 downloadTask:当前下载的任务
 location:默认临时文件的存储路径

 */
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location{
    self.isFirst = NO;
    self.resumeData = nil;
    
    NSLog(@"下载完成");
    NSLog(@"下载文件的路径:%@", location);
    NSString *pathStr = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
    // 下载的文件必须改个名字才能转存到documents目录
    pathStr = [pathStr stringByAppendingString:@"/DownLoadFile"];
    NSLog(@"documents目录:%@", pathStr);
    NSError *error;
    NSFileManager *fillM = [NSFileManager defaultManager];
    
    BOOL isSuccess = [fillM moveItemAtPath:location.path toPath:pathStr error:&error];
    if (!isSuccess) {
        NSLog(@"失败");
        NSLog(@"错误信息:%@,,,,,,,失败原因%@, 恢复建议:%@", error.userInfo, error.localizedFailureReason, error.localizedRecoverySuggestion);
    }
}

// 下载恢复时调用
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
 didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes{
    
}

#pragma mark - NSURLSessionTaskDelegate

/*
 不管成功失败,都会调用,只是失败的时候error有值
 在下载失败时,error的userinfo属性可以通过NSURLSessionDownloadTaskResumeData
 这个key来取到resumeData,再通过resumeData恢复下载
 */
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(nullable NSError *)error{
    
    if (error) {
        if ([error.userInfo objectForKey:NSURLSessionDownloadTaskResumeData]) {
            NSData *resumeData = [error.userInfo objectForKey:NSURLSessionDownloadTaskResumeData];
            self.downLoadTask = [self.backGroundDownSession downloadTaskWithResumeData:resumeData];
            [self.downLoadTask resume];
        }
    }
}

后台下载

后台下载只需要在下载的基础上添加如下两个方法就行(如果想实现下载完成后通知用户,可以实现以下本地通知)

// APPdelegate

- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler{

    // 保存completionHandler
    [self.handlerDict setObject:identifier forKey:@"backgroundDownLoadIdentifier"];
    [self.handlerDict setObject:completionHandler forKey:@"backgroundDownLoadHandler"];

}

// viewcontroller

- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session{
    
    AppDelegate *appDelegatttt = (AppDelegate *)[UIApplication sharedApplication].delegate;
    _handleForBack = [appDelegatttt.handlerDict objectForKey:@"backgroundDownLoadHandler"];
    if (_handleForBack) {
        [appDelegatttt.handlerDict removeObjectForKey:@"backgroundDownLoadHandler"];
        _handleForBack();
    }
    
}

上传:上传有两种方式

1、以数据流的方式上传(文件大小不受限制)

// 1.创建url
    NSString *urlString = @"http://www.xxxx.com/upload.php";
    // urlString = [urlString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]];
    NSURL *url = [NSURL URLWithString:urlString];

    // 2.创建请求
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    // 文件上传使用post
    request.HTTPMethod = @"POST";

    // 3.开始上传   request的body data将被忽略,而由fromData提供
    [[[NSURLSession sharedSession] uploadTaskWithRequest:request fromData:[NSData dataWithContentsOfFile:@"/Users/lifengfeng/Desktop/test.jpg"]     completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (error == nil) {
            NSLog(@"upload success:%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
        } else {
            NSLog(@"upload error:%@",error);
        }
    }] resume];

2、以拼接表单的方式进行上传

  • 上传的关键是请求体部分的表单拼接,获取本地上传文件的类型(MIME Types),至于具体的网络上传则很简单。 另外拼接表单的方式会有大小限制,即HTML的MAX_FILE_SIZE限制(可以自己设定,一般2MB)。

表单拼接的格式如下:

--boundary
Content-Disposition:form-data;name=”表单控件名称”;filename=”上传文件名称”
Content-Type:要上传文件MIME Types

要上传文件二进制数据;

--boundary--

示例:

// 1.创建url  采用Apache本地服务器
    NSString *urlString = @"http://localhost/upload/upload.php";
    urlString = [urlString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]];
    NSURL *url = [NSURL URLWithString:urlString];

    // 2.创建请求
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    // 文件上传使用post
    request.HTTPMethod = @"POST";

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

    [request setValue:contentType forHTTPHeaderField:@"Content-Type"];
    // test.jpg
    // 3.拼接表单,大小受MAX_FILE_SIZE限制(2MB)  FilePath:要上传的本地文件路径  formName:表单控件名称,应于服务器一致
    NSData* data = [self getHttpBodyWithFilePath:@"/Users/lifengfeng/Desktop/test.jpg" formName:@"file" reName:@"newName.png"];
    request.HTTPBody = data;
    // 根据需要是否提供,非必须,如果不提供,session会自动计算
    [request setValue:[NSString stringWithFormat:@"%lu",data.length] forHTTPHeaderField:@"Content-Length"];

    // 4.1 使用dataTask
    [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (error == nil) {
            NSLog(@"upload success:%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
        } else {
            NSLog(@"upload error:%@",error);
        }

    }] resume];
#if 0
    // 4.2 开始上传 使用uploadTask   fromData:可有可无,会被忽略
    [[[NSURLSession sharedSession] uploadTaskWithRequest:request fromData:nil     completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (error == nil) {
            NSLog(@"upload success:%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
        } else {
            NSLog(@"upload error:%@",error);
        }
    }] resume];
#endif

需要用的两个自定义的方法:

/// filePath:要上传的文件路径   formName:表单控件名称  reName:上传后文件名
- (NSData *)getHttpBodyWithFilePath:(NSString *)filePath formName:(NSString *)formName reName:(NSString *)reName
{
    NSMutableData *data = [NSMutableData data];
    NSURLResponse *response = [self getLocalFileResponse:filePath];
    // 文件类型:MIMEType  文件的大小:expectedContentLength  文件名字:suggestedFilename
    NSString *fileType = response.MIMEType;

    // 如果没有传入上传后文件名称,采用本地文件名!
    if (reName == nil) {
        reName = response.suggestedFilename;
    }

    // 表单拼接
    NSMutableString *headerStrM =[NSMutableString string];
    [headerStrM appendFormat:@"--%@\r\n",@"boundary"];
    // name:表单控件名称  filename:上传文件名
    [headerStrM appendFormat:@"Content-Disposition: form-data; name=%@; filename=%@\r\n",formName,reName];
    [headerStrM appendFormat:@"Content-Type: %@\r\n\r\n",fileType];
    [data appendData:[headerStrM dataUsingEncoding:NSUTF8StringEncoding]];

    // 文件内容
    NSData *fileData = [NSData dataWithContentsOfFile:filePath];
    [data appendData:fileData];

    NSMutableString *footerStrM = [NSMutableString stringWithFormat:@"\r\n--%@--\r\n",@"boundary"];
    [data appendData:[footerStrM  dataUsingEncoding:NSUTF8StringEncoding]];
//    NSLog(@"dataStr=%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    return data;
}

/// 获取响应,主要是文件类型和文件名
- (NSURLResponse *)getLocalFileResponse:(NSString *)urlString
{
    urlString = [urlString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]];
    // 本地文件请求
    NSURL *url = [NSURL fileURLWithPath:urlString];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    __block NSURLResponse *localResponse = nil;
    // 使用信号量实现NSURLSession同步请求
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        localResponse = response;
        dispatch_semaphore_signal(semaphore);
    }] resume];
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    return  localResponse;
}

 

 

 

 

 

 

 

 

转载于:https://my.oschina.net/yejiexiaobai/blog/1855884

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值