ios NSURLSession后台传输

http://www.appcoda.com/background-transfer-service-ios7/

http://www.raywenderlich.com/51127/nsurlsession-tutorial

https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/URLLoadingSystem/Articles/UsingNSURLSession.html

http://blog.csdn.net/kyfxbl/article/details/18629227

 http://hayageek.com/ios-nsurlsession-example/

一个很好的IOS学习网站:

http://www.appcoda.com

遇到NSURLSession后台上传文件的问题,得到这个地方的指点:

http://eyeplum.me/2014/02/26/multipart-form-data-background-nsurlsession/ 后解决,非常感谢

具体的实现:

AppDelegate.m中,加入后台处理函数

- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler{
    
    self.backgroundTransferCompletionHandler = completionHandler;
    
}

ViewController.h加入delegate

@interface ViewController : UIViewController <UITableViewDelegate , UITableViewDataSource , NSURLSessionDelegate>

 

下面是具体的实现,这里任务需要,我们是串行地上传,其实并行上传更方便

//
//  ViewController.m
//  BackgroundTransfer
//
//  Created by feixiang on 16/6/14.
//  Copyright (c) 2014 ___FULLUSERNAME___. All rights reserved.
//

#import "ViewController.h"
#import "FileUploadInfo.h"
#import "AppDelegate.h"

// Define some constants regarding the tag values of the prototype cell's subviews.
#define CellLabelTagValue               10
#define CellStartPauseButtonTagValue    20
#define CellStopButtonTagValue          30
#define CellProgressBarTagValue         40
#define CellLabelReadyTagValue          50


@interface ViewController ()

@property (nonatomic, strong) NSURLSession *session;
@property (nonatomic, strong) NSURLSessionUploadTask *task;
@property (nonatomic) BOOL isDoing;

@property (nonatomic, strong) NSMutableArray *arrUploadList;
// 文件存放地址
@property (nonatomic, strong) NSString *documentsDirectory;

@property (nonatomic, strong) NSString *boundary;
@property (nonatomic, strong) NSString *fileParam;
@property (nonatomic, strong) NSURL *uploadURL;
@property (nonatomic, strong) NSNumber *currentIndex;

@end

@implementation ViewController


- (void)initUploadList{
    self.arrUploadList = [[NSMutableArray alloc] init];
    NSArray *fileList = [self getFiles:self.documentsDirectory];
    
    for(NSString* file in fileList)
    {
        NSString* filePath = [self getFilePath:file];
        [self.arrUploadList addObject:[[FileUploadInfo alloc] initWithFileTitle:file andFilePath:filePath]];
    }
    
}    


// 继承tableview的函数
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    return 1 ;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return self.arrUploadList.count ;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return 60.0;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"idCell"];
    if( cell == nil ){
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"idCell" ];
    }
    FileUploadInfo *uploader = [self.arrUploadList objectAtIndex:indexPath.row];
    UILabel *title = (UILabel *)[cell viewWithTag:CellLabelTagValue];
    // 设置属性
    title.text = uploader.fileTitle;
    return cell ;
}


- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.isDoing = NO ;
    
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    self.documentsDirectory = [paths objectAtIndex:0];
    self.tblFiles.delegate = self ;
    self.tblFiles.dataSource = self ;
    
    [self initUploadList];
    [self BgUploadInitSession];
    
}


// -------------<feixiang>后台传输函数----------

- (void)BgUploadInitSession{
    // 这里加入后台下载功能
    // https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/URLLoadingSystem/Articles/UsingNSURLSession.html
    NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:@"com.yuanfang"];
    // 后台下载用 backgroundSessionConfiguration,先用默认的设置
    //NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
    
    //sessionConfiguration.HTTPMaximumConnectionsPerHost = 5;
    self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration
                                                 delegate:self
                                            delegateQueue:nil];
    
    // 初始化上传地址
    self.boundary = @"----------V2ymHFg03ehbqgZCaKO6jy" ;
    self.uploadURL = [NSURL URLWithString:@"http://xxxx/Upload/"];

}

- (NSMutableURLRequest *)BgUploadSetHeader{
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL:self.uploadURL];
    [request setHTTPMethod:@"POST"];
    NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", self.boundary];
    [request setValue:contentType forHTTPHeaderField: @"Content-Type"];
    
    return request ;
}

- (NSURL *)BgUploadSetUrl:(NSString *)uploadFilePath{
    NSData *body = [self BgUploadPrepareData:uploadFilePath] ;
    NSString* uploadFile_tmp = [NSString stringWithFormat:@"%@_tmp" ,uploadFilePath ];
    [body writeToFile:uploadFile_tmp atomically:true];
    
    // 上传完成后需要将临时文件删除
    NSString *filePath = [[NSString stringWithFormat:@"file://%@", uploadFile_tmp] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSURL *fileUrl =  [NSURL URLWithString:filePath];
    
    return fileUrl;
}

// 这里删除文件有点问题
- (void)BgUploadRemoveTmpFile:(NSString *)tmpFilepath{
    tmpFilepath = [NSString stringWithFormat:@"%@_tmp",tmpFilepath];
    tmpFilepath = [[NSString stringWithFormat:@"file://%@", tmpFilepath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSFileManager *defaultManager;
    defaultManager = [NSFileManager defaultManager];
    NSError *error ;
    BOOL ret = [defaultManager removeItemAtPath:tmpFilepath error:&error];
    if( ret == NO )
        NSLog(@"\nerror:%@",error);
}

- (NSData*) BgUploadPrepareData:(NSString *)filePath
{
    NSFileManager *fileManager = [NSFileManager defaultManager];
    BOOL fileExists = [fileManager fileExistsAtPath:filePath];
    
    NSMutableData *body = [NSMutableData data];
    if( fileExists == YES ){
        NSString *fileName = [filePath lastPathComponent];
        
        NSData *dataOfFile = [[NSData alloc] initWithContentsOfFile:filePath];
        
        // 组装POST格式
        if (dataOfFile) {
            [body appendData:[[NSString stringWithFormat:@"--%@\r\n", self.boundary] dataUsingEncoding:NSUTF8StringEncoding]];
            [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"file\"; filename=\"%@\"\r\n", fileName] dataUsingEncoding:NSUTF8StringEncoding]];
            [body appendData:[@"Content-Type: application/zip\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
            [body appendData:dataOfFile];
            [body appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
        }
        [body appendData:[[NSString stringWithFormat:@"--%@--\r\n", self.boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    }
    return body;
}

- (void)BgUploadCommon:(NSString *)uploadFilePath{
    // 由于fromFile会覆盖原来的http请求的body内容。可以先构造一个request,然后将body信息存到文件里面,提供给task调用
    // 1 , 构造 HTTP Request POST HEADER
    NSMutableURLRequest *request = [self BgUploadSetHeader];
    // 2 , 将文件和保存文件form-data信息一起保存到磁盘临时文件中
    NSURL *fileUrl = [self BgUploadSetUrl:uploadFilePath];
    // 3,使用task的fromFile上传函数
    self.task = [self.session uploadTaskWithRequest:request fromFile:fileUrl];
    // 启动后台任务,下面回调函数接收消息
    [self.task resume];
}
// ----------end 后台传输--------


//----------------各个按钮事件
- (IBAction)startAll:(id)sender{
    if( self.isDoing == NO ){
        [self.buttonStart setTitle:@"STOP" forState:UIControlStateHighlighted];
        self.isDoing = YES;
    }else{
        [self.buttonStart setTitle:@"START" forState:UIControlStateHighlighted];
        self.isDoing = NO;
    }
    FileUploadInfo *uploader = [self.arrUploadList objectAtIndex:0];
    [self BgUploadCommon:uploader.filePath];
}
- (IBAction)stopUpload:(id)sender{
    if( self.task.state == NSURLSessionTaskStateRunning ){
         [self.task suspend];
    }
}
- (IBAction)resumeUpload:(id)sender{
    if( self.task.state == NSURLSessionTaskStateRunning ){
        [self.task resume];
    }
}

- (IBAction)cancelUpload:(id)sender{
    if( self.task.state == NSURLSessionTaskStateRunning ){
        [self.task cancel];
    }
}





//----------------NSURLSession回调函数-------------------------
// 上传进度中
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
   didSendBodyData:(int64_t)bytesSent
    totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
    NSLog(@"\n%f / %f", (double)totalBytesSent,
          (double)totalBytesExpectedToSend);
    
    // 更新界面
    //int index = [self getIndexWithTaskIdentifier:task.taskIdentifier];
    int index = [self.currentIndex intValue];
    FileUploadInfo *uploader = [self.arrUploadList objectAtIndex:index];
    
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        uploader.progress = (double)totalBytesSent / (double)totalBytesExpectedToSend;
        
        UITableViewCell *cell = [self.tblFiles cellForRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0]];
        UIProgressView *progressView = (UIProgressView *)[cell viewWithTag:CellProgressBarTagValue];
        progressView.progress = uploader.progress;
    }];
}

// 上传完成
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
    // 这里继续做下一个任务
    [self BgUploadBeginNextTask];
}

- (void)BgUploadBeginNextTask{
    //int index = [self getIndexWithTaskIdentifier:task.taskIdentifier] + 1 ;
    self.currentIndex = @([self.currentIndex intValue] + 1 );
    int index = [self.currentIndex intValue];
    FileUploadInfo *uploader = [self.arrUploadList objectAtIndex:index];
    
    NSLog(@"\n第 %@ 个任务 %@ 完成 ",self.currentIndex, uploader.filePath);
    
    UILocalNotification *localNotification = [[UILocalNotification alloc] init];
    localNotification.alertBody = [NSString stringWithFormat:@"%@ have been uploaded!",uploader.filePath];
    [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
    
    
    //先删除临时文件
    [self BgUploadRemoveTmpFile:uploader.filePath];
    if( index < [self.arrUploadList count] ){
        [self BgUploadCommon:uploader.filePath];
    }
}

// 后台传输完成,处理URLSession完成事件
-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session{
    AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
    
    // Check if all download tasks have been finished.
    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        if ([uploadTasks count] == 0) {
            if (appDelegate.backgroundTransferCompletionHandler != nil) {
                // Copy locally the completion handler.
                void(^completionHandler)() = appDelegate.backgroundTransferCompletionHandler;
                
                // Make nil the backgroundTransferCompletionHandler.
                appDelegate.backgroundTransferCompletionHandler = nil;
                
                [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                    completionHandler();
                    
                    // 这里继续做下一个任务
                    [self BgUploadBeginNextTask];
                }];
            }
        }
        
        
    }];
}

-(int)getIndexWithTaskIdentifier:(unsigned long)taskIdentifier{
    int index = 0;
    for (int i=0; i<[self.arrUploadList count]; i++) {
        FileUploadInfo *uploader = [self.arrUploadList objectAtIndex:i];
        if (uploader.taskIdentifier == taskIdentifier) {
            index = i;
            break;
        }
    }
    
    return index;
}

//----------------END NSURLSession回调函数-----------------------




// 获取目录下的所有文件
-(NSArray*)getFiles:(NSString *)dir
{
    NSFileManager* fm = [NSFileManager defaultManager];
    NSArray* array = [fm contentsOfDirectoryAtPath:dir error:nil ];
    NSMutableArray* fileList = [[NSMutableArray alloc]init];
    
    BOOL isDir = NO ;
    for(NSString* file in array)
    {
        [fm fileExistsAtPath:file isDirectory:&isDir];
        if( !isDir && ![file isEqualToString:@".DS_Store"])
            [fileList addObject:file];
    }
    return fileList ;
}



- (NSString*)getFilePath:(NSString *)filename{
    NSString *uploadFilePath = [self.documentsDirectory stringByAppendingPathComponent:filename];
    return uploadFilePath;
}



- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

 

 http://numbbbbb.gitbooks.io/-the-swift-programming-language-/

posted @ 2014-06-16 10:41  ifeixiang  阅读(6949)  评论(0编辑  收藏  举报