文档章节

ios NSURLSession后台传输

ifeixiang
 ifeixiang
发布于 2014/10/31 17:49
字数 1224
阅读 72
收藏 0

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-/

© 著作权归作者所有

ifeixiang
粉丝 16
博文 270
码字总数 99506
作品 0
广州
程序员
私信 提问
iOS基础深入补完计划--NSURLSession使用详解(附Demo)

目录 前言 API Demo 前言 本文主要是把NSURLSession以及NSURLSessionTask相关的代理方法全部整理了一下。 旨在大体了解在一个iOS网络请求中、一个任务究竟经理了什么。 而我们、又能做些什么...

kirito_song
2018/05/10
0
0
iOS源码补完计划--AFNetworking 3.1.0源码研读

参拜一下AFNetworking的源码。 第四篇源码、暂时来看也是iOS方向的最后一篇、撸完准备趁着热乎撸一撸网络协议。 目录 准备工作 功能模块 AFURLSessionManager/AFHTTPSessionManager AFNetwo...

kirito_song
2018/05/25
0
0
AFNetworking 2.0 发布,iOS 网络框架

AFNetworking 是一款在 iOS 开发中广泛使用的网络框架,其刚刚发布最新的 2.0 版本,主要改进: 架构重构 支持 NSURLSession Serialization Modules Expanded UIKit Extensions Real-time f...

oschina
2013/09/28
9K
5
面试官自述:面向高级开发人员的iOS面试问题

当您准备进行技术性iOS面试时,了解您可能会询问哪些主题以及经验丰富的iOS开发人员期望什么是非常重要的。 这是许多硅谷公司用来衡量iOS候选人资历水平的一系列问题。 这些问题涉及iOS开发的...

菇哒微课
2018/04/26
0
0
IOS7 Tech Talk总结

导航 一、kick off A、苹果推出了iOS7,他们希望开发者打造的apps能够更具有吸引力,首先是第一印象要好,App Store的截图制作就要好。 在开发软件的时候,应该注重字体的颜色和图像,要用透...

gaoios
2014/01/13
0
0

没有更多内容

加载失败,请刷新页面

加载更多

二、Docker

1、Docker - The TLDR(Too Long,Don't Read,Linxu 终端工具 ) Docker是在Linux和Windows上运行的软件。它创建、管理和编排容器。该软件以开源方式开发,在Github上作为Moby开源项目的一部分。...

倪伟伟
38分钟前
2
0
Python猫荐书系列之七:Python入门书籍有哪些?

本文原创并首发于公众号【Python猫】,未经授权,请勿转载。 原文地址:https://mp.weixin.qq.com/s/ArN-6mLPzPT8Zoq0Na_tsg 最近,猫哥的 Python 技术学习群里进来了几位比较特殊的同学:一...

豌豆花下猫
今天
5
0
Guava RateLimiter限流源码解析和实例应用

在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流 缓存 缓存的目的是提升系统访问速度和增大系统处理容量 降级 降级是当服务出现问题或者影响到核心流程时,需要暂时屏蔽掉,待高...

算法之名
今天
13
0
国产达梦数据库与MySQL的区别

背景 由于项目上的需要,把项目实现国产化,把底层的MySQL数据库替换为国产的达梦数据库,花了一周的时间研究了国产的数据库-达梦数据库,它和MySQL有一定的区别,SQL的写法也有一些区别。 ...

TSMYK
今天
2
0
老也有错?35岁程序员是一道坎,横亘在每个技术职场人的心中

随着互联网的高速发展变革,大龄恐惧症越来越多地在技术圈被人讨论。很多程序员在工作5-10年以后,都会开始思考5年、10年甚至更久以后的自己,会是怎样一种生活工作状态,以及是否会被时代抛...

我最喜欢三大框架
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部