对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;
}