iOS (Swift & Objective-C & Xcode)iOS Developer收藏

iOS后台上传

2018-01-09  本文已影响430人  7b33a23272c4

iOS 后台上传的处理逻辑,大概和后台下载的逻辑相差无几。基本的逻辑是:首先创建一个NSURLSessionConfiguration,然后通过这个configuration,创建一个NSURLSession,接着是创建相关的NSURLSessionTask,最后就是处理相关的回调方法。

一、创建NSURLSession

创建一个后台下载的session

- (NSURLSession *)getDownloadURLSession {
    NSString *identifier = [self backgroundSessionIdentifier];
    NSURLSessionConfiguration* sessionConfig = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:identifier];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig
                                            delegate:self
                                       delegateQueue:[NSOperationQueue mainQueue]];
    return session;
}

获取后台下载的标识

- (NSString *)backgroundSessionIdentifier {
    NSString *bundleId = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];
    NSString *identifier = [NSString stringWithFormat:@"%@.BackgroundSession", bundleId];
    return identifier;
}

二、创建上传的NSURLSessionTask

NSURLSessionUploadTask 的父类是NSURLSessionDataTask,它的父类是NSURLSessionTask。在创建上传的task的时候,有几个注意点(坑点)。下面介绍总结的三种不同类型的后台上传方式。

  1. 直接通过文件上传(最简单)
    这种上传方式是,网络上最容易搜索到的一种上传方法,直接拿到文件的本地路径,然后进行上传。其中- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData;不支持后台上传。
- (void)bgUploadFromFile{
    NSLog(@"%s", __func__);
    NSURL *url = [NSURL URLWithString:kStreamUploadUrl];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request setHTTPMethod:@"POST"];
    NSString *path = [[NSBundle mainBundle] pathForResource:@"icon.jpg" ofType:nil];
    self.uploadTask = [self.backgroundSession uploadTaskWithRequest:request fromFile:[NSURL fileURLWithPath:path]];
    [self.uploadTask resume];
}
  1. 通过form文件流进行上传
    使用这种方式上传form类型的数据的时候有个坑点,那么就是必须要给予两个请求头:Content-LengthContent-Type,没有这两个请求头,点击是没有反应的!
- (void)bgUploadStreamForm
{
    NSLog(@"%s", __func__);
    NSURL *url = [NSURL URLWithString:kFormUploadUrl];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request setHTTPMethod:@"POST"];
    NSString *path = [[NSBundle mainBundle]pathForResource:@"icon" ofType:@"jpg"];
    NSData *bodydata = [self buildBodyDataWithPicPath:path];
    NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; charset=utf-8;boundary=%@",boundary];
    [request setValue:contentType forHTTPHeaderField:@"Content-Type"];
    [request setValue:[NSString stringWithFormat:@"%zd", bodydata.length] forHTTPHeaderField:@"Content-Length"];
    request.HTTPBodyStream = [NSInputStream inputStreamWithData:bodydata];
    self.uploadTask = [self.backgroundSession uploadTaskWithStreamedRequest:request];
    [self.uploadTask resume];
}

拼接form类型的数据,上传中需要的额外参数,也可以在form当中拼接,提交给服务器

-(NSData*)buildBodyDataWithPicPath:(NSString *)path{
    
    NSMutableData *bodyData = [NSMutableData data];
    NSMutableString *bodyStr = [NSMutableString string];
    [bodyStr appendFormat:@"--%@\r\n",boundary];//\n:换行 \n:切换到行首
    [bodyStr appendFormat:@"Content-Disposition: form-data; name=\"sampleFile\"; filename=\"icon.jpg\""];
    [bodyStr appendFormat:@"\r\n\r\n"];
    
    NSData *start = [bodyStr dataUsingEncoding:NSUTF8StringEncoding];
    [bodyData appendData:start];
    NSData *picData = [NSData dataWithContentsOfFile:path];
    [bodyData appendData:picData];

    bodyStr = [NSMutableString string];
    [bodyStr appendFormat:@"\r\n--%@--",boundary];
    
    NSData *endData = [bodyStr dataUsingEncoding:NSUTF8StringEncoding];
    [bodyData appendData:endData];
    return bodyData;
    
}
  1. 文件流进行上传
    这种上传方式对于客户端来讲的话,也是比较方便简单,性能好的一种方法,但是特殊的地方就是服务器需要特殊处理,简单来讲就是有点反服务器的常规。还有请求头和上面一样必须要做处理。
- (void)bgUploadStreamFile {
    
    NSLog(@"%s", __func__);
    NSURL *url = [NSURL URLWithString:kStreamUploadUrl];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request setHTTPMethod:@"POST"];
    NSString *path = [[NSBundle mainBundle]pathForResource:@"icon" ofType:@"jpg"];
    NSFileManager *fileMgr = [NSFileManager defaultManager];
    NSDictionary *fileAttri = [fileMgr attributesOfItemAtPath:path error:nil];
    NSNumber *fileSize = [fileAttri valueForKey:@"NSFileSize"];
    request.HTTPBodyStream = [NSInputStream inputStreamWithFileAtPath:path];
    [request setValue:[NSString stringWithFormat:@"%zd", fileSize.integerValue] forHTTPHeaderField:@"Content-Length"];
    [request setValue:@"application/octet-stream" forHTTPHeaderField:@"Content-Type"];
    self.uploadTask = [self.backgroundSession uploadTaskWithStreamedRequest:request];
    [self.uploadTask resume];
}
  1. 这里简单聊聊分片上传

到这里其实还有坑,这时候2,3后台上传还是不起作用,还是有坑,下面继续走

三、上传的代理

  1. 必须实现这个代理,上面的2,3方式的上传才能够进行
/* Sent if a task requires a new, unopened body stream.  This may be
 * necessary when authentication has failed for any request that
 * involves a body stream.
 */
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
 needNewBodyStream:(void (^)(NSInputStream * _Nullable bodyStream))completionHandler {
    
    NSInputStream *inputStream = task.originalRequest.HTTPBodyStream ;;
    if (completionHandler) {
        completionHandler(inputStream);
    }
}
  1. 上传的进度回调方法
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
    
    NSLog(@"progress: %f" ,totalBytesSent/(float)totalBytesExpectedToSend);
}
  1. 首个上传任务在后台上传成功的回调
/* If an application has received an
 * -application:handleEventsForBackgroundURLSession:completionHandler:
 * message, the session delegate will receive this message to indicate
 * that all messages previously enqueued for this session have been
 * delivered.  At this time it is safe to invoke the previously stored
 * completion handler, or to begin any internal updates that will
 * result in invoking the completion handler.
 */
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
    NSLog(@"%s", __func__);
}
  1. AppDelegate中首个后台上传任务在后台完成是的回调
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler {
    NSLog(@"%s", __func__);
}

这几个回调是比较重要的上传回调方法,部分和下面类似,细节处理可查看上传的回调处理

四、有了这些东西没有上传测试服务器请看这里

上传测试服务器:https://github.com/onezens/fileUploadServer

demo:https://github.com/onezens/backgroundUploadDemo

上一篇下一篇

猜你喜欢

热点阅读