标签:
NSURLSession是iOS7中新的网络接口,支持后台网络操作,除非用户强行关闭。
NSURLSession使用步骤:
1. 新建NSURLSessionConfiguration,用于NSURLSession的配置
2. 新建NSURLSession
3. 新建NSURLSessionTask对象
4. 执行task
其中NSURLSessionConfiguration可以不创建,NSURLSession使用单例对象sharedSession也能满足大多数开发需求。
NSURLSessionTask是一个抽象类,不能用它直接创建对象,而应该使用它的子类:
1. NSURLSessionDataTask 处理一般的NSData数据对象,比如通过GET或POST方式从服务器获取JSON或XML返回等等,但不支持后台获取
2. NSURLSessionUploadTask 用于上传文件,支持后台上传
3. NSURLSessionDownloadTask 用于下载文件,支持后台下载
每个Task都有下面三个方法:
- (void)suspend; //暂停任务
- (void)resume; //启动任务,或唤醒suspend状体的任务
- (void)cancel; //取消任务
GET和POST是两种最常用的与服务器进行交互的HTTP方法。
GET
1. GET的语义是获取指定URL的资源
2. 将数据按照variable=value的形式,添加到action所指向的URL后面,并且两者使用”?”连接,各变量之间使用”&”连接
3. 貌似不安全,因为在传输过程中,数据被放在请求的URL中
4. 传输的数据量小,主要受URL长度限制
POST
1. POST的语义是向指定URL的资源添加数据
2. 将数据放在数据体中,按照变量和值相对应的方式,传递到action所指向URL
3. 所有数据对用户来说不可见
4. 可以传输大量数据,上传文件只能使用Post
对于URL来说,Get是不安全的,Post的安全的,因为Get请求能直接从URL中获取用户信息;但对于服务器来说,Post是不安全的,Get是安全的。因为Post发送给服务器的数据体可以使用工具查看内容(比如firebug)。
假如有一个登录程序,使用get请求的方法登录,则代码如下所示:
- (void)getLogin
{
NSString *urlStr = [NSString stringWithFormat:@"http://localhost/login.php?username=%@&password=%@", self.username.text, self.password.text];
NSURL *url = [NSURL URLWithString:urlStr];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:urlStr] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@", str);
//JSON数据反序列化
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
//LoginUser--用户模型类
LoginUser *user = [[LoginUser alloc] init];
[user setValuesForKeysWithDictionary:dict];
NSLog(@"%@ %@", user.userId, user.userName);
}];
//NSURLSession中的任务默认都是挂起的,执行这条语句之后任务才会执行
[dataTask resume];
NSLog(@"%@----come here", [NSThread currentThread]);
}
其中,come here先于str打印。
- (void)postLogin
{
NSURL *url = [NSURL URLWithString:@"http://localhost/login.php"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
NSString *bodyStr = [NSString stringWithFormat:@"username=%@&password=%@", self.username.text, self.password.text];
request.HTTPBody = [bodyStr dataUsingEncoding:NSUTF8StringEncoding];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@----%@", [NSThread currentThread], str);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.label.text = str;
}];
}];
NSLog(@"come here");
[dataTask resume];
}
JSON序列化、反序列化的方法在前面或后面的代码中会提到,这里提一下解析XML数据的方法。
假如有XML文件如下:
<note>
<Email>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don‘t forget the meeting!</body>
</Email>
<Email>
<to>Steven</to>
<from>Alice</from>
<heading>Appointment</heading>
<body>How about to watch a movie</body>
</Email>
<Email>
<to>李四</to>
<from>王五</from>
<heading>李四有个儿子</heading>
<body>李四要带他儿子去见隔壁老王</body>
</Email>
</note>
则,解析上面的XML数据的代码如下:
@interface ViewController () <NSXMLParserDelegate>
//存储每一个Email节点的内容
@property (nonatomic, strong) NSMutableArray *dataList;
//获取节点数据
@property (nonatomic, strong) NSMutableString *elementContent;
//把每一个Email节点的数据转化为Email对象
@property (nonatomic, strong) Email *email;
- (IBAction)parserXMLFile:(id)sender;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (IBAction)parserXMLFile:(id)sender
{
NSURL *url = [NSURL URLWithString:@"http://localhost/xmlExample.xml"];
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:10.0f];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
[parser parse];
}];
[dataTask resume];
}
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
NSLog(@"开始解析xml文件,进行初始化工作");
if (!_dataList) {
_dataList = [[NSMutableArray alloc] init];
}else{
[_dataList removeAllObjects];
}
if (!_elementContent) {
_elementContent = [[NSMutableString alloc] init];
}else{
[_elementContent setString:@""];
}
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
NSLog(@"开始解析节点%@", elementName);
//把之前的数据清空
[self.elementContent setString:@""];
if ([elementName isEqualToString:@"Email"]) {
_email = [[Email alloc] init];
}
}
//这个方法可能会调用多次
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
NSLog(@"查找到内容: %@", string);
[self.elementContent appendString:string];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
NSLog(@"节点%@解析结束", elementName);
if ([elementName isEqualToString:@"Email"]) {
[self.dataList addObject:self.email];
}else if (![elementName isEqualToString:@"note"]) {
[self.email setValue:self.elementContent forKey:elementName];
}
}
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
NSLog(@"xml文件解析结束");
[self.dataList enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
Email *email = obj;
NSLog(@"%d-%@", idx, email);
}];
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
NSLog(@"解析文件出现错误: %@", parseError);
}
@end
其中,Email类的代码如下:
@interface Email : NSObject
@property (nonatomic, copy) NSString *to;
@property (nonatomic, copy) NSString *from;
@property (nonatomic, copy) NSString *heading;
@property (nonatomic, copy) NSString *body;
@property (nonatomic, copy) NSString *emailId;
@end
@implementation Email
//打印Email时就会调用这个方法
- (NSString *)description
{
NSMutableString *string = [NSMutableString stringWithString:@"\n(\n"];
[string appendFormat:@"to: %@\nfrom: %@\nheading: %@\nbody: %@", self.to, self.from, self.heading, self.body];
[string appendString:@"\n)"];
return string;
}
@end
NSURL *url = [NSURL URLWithString:@"http://localhost/postjson.php"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:2.0f];
request.HTTPMethod = @"POST";
Contact *contact = [[Contact alloc] init];
contact.name = @"steven";
contact.phone = @"13750406504";
contact.identity = @"steven-13750406504";
id obj = [contact dictionaryWithValuesForKeys:@[@"name", @"phone", @"identity"]];
//也可以上传一组数组
// NSDictionary *dict1 = @{
// @"name" : @"Alice",
// @"phone" : @"13588888888",
// @"identity" : @"Alice-13588888888"
// };
//
// NSDictionary *dict2 = @{
// @"name" : @"Steven",
// @"phone" : @"13500000000",
// @"identity" : @"Steven-13500000000"
// };
//
// NSArray *array = @[dict1, dict2];
//序列化对象数据
request.HTTPBody = [NSJSONSerialization dataWithJSONObject:obj options:0 error:NULL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@", result);
}];
[dataTask resume];
上传一个文件有它固定的格式,这里使用multipart/form-data的方式上传,上传文件时需要把上传的格式用NSMutableString拼接好。先贴代码:
static NSString *boundaryStr = @"--";
static NSString *randomIDStr;
static NSString *uploadID;
@implementation UploadFile
- (instancetype)init
{
if (self = [super init]) {
//随意写一个
randomIDStr = @"steven";
//后端开发人员会提供
uploadID = @"uploadFile";
}
return self;
}
//格式固定,不可随意更改,少一个字母都会出错
- (NSString *)topStringWithMimeType:(NSString *)mimeType uploadFileName:(NSString *)uploadFileName
{
NSMutableString *strM = [NSMutableString string];
[strM appendFormat:@"%@%@\n", boundaryStr, randomIDStr];
[strM appendFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\n", uploadID, uploadFileName];
[strM appendFormat:@"Content-Type: %@\n\n", mimeType];
NSLog(@"%@", strM);
return [strM copy];
}
- (NSString *)bottomString
{
NSMutableString *strM = [NSMutableString string];
[strM appendFormat:@"%@%@\n", boundaryStr, randomIDStr];
//appednString,不是appendFormat!
[strM appendString:@"Content-Disposition: form-data; name=\"submit\"\n\n"];
[strM appendString:@"Submit\n"];
[strM appendFormat:@"%@%@--\n", boundaryStr, randomIDStr];
NSLog(@"%@", strM);
return [strM copy];
}
- (void)uploadFileWithURL:(NSURL *)url data:(NSData *)data
{
// 1> 数据体
//第一个参数是上传的文件的类型,这里提前设置为image,实际开发中需要另行判断
//第二个参数指文件上传到服务器保存时用什么名字保存
NSString *topStr = [self topStringWithMimeType:@"image/jpg" uploadFileName:@"girl2.jpg"];
NSString *bottomStr = [self bottomString];
NSMutableData *dataM = [NSMutableData data];
[dataM appendData:[topStr dataUsingEncoding:NSUTF8StringEncoding]];
[dataM appendData:data];
[dataM appendData:[bottomStr dataUsingEncoding:NSUTF8StringEncoding]];
// Request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:2.0f];
request.HTTPBody = dataM;
// 2> 设置Request的头属性
request.HTTPMethod = @"POST";
// 3> 设置Content-Length
NSString *strLength = [NSString stringWithFormat:@"%ld", (long)dataM.length];
[request setValue:strLength forHTTPHeaderField:@"Content-Length"];
// 4> 设置Content-Type
NSString *strContentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", randomIDStr];
[request setValue:strContentType forHTTPHeaderField:@"Content-Type"];
NSURLSession *session = [NSURLSession sharedSession];
//girl2.png是要上传的文件,也可以把文件转为data类型再上传
NSString *path = [[NSBundle mainBundle] pathForResource:@"girl2.jpg" ofType:nil];
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromFile:[NSURL URLWithString:path] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@", result);
}];
[uploadTask resume];
}
NSURL *url = [NSURL URLWithString:@"http://localhost/uploads/Google.mkv"];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
NSFileManager *fm = [[NSFileManager alloc] init];
[fm moveItemAtPath:location.path toPath:self.cacheFile error:nil];
NSLog(@"文件下载成功");
}];
[downloadTask resume];
@interface ViewController () <NSURLSessionDownloadDelegate>
//开始下载按钮
- (IBAction)downloadFile:(id)sender;
//暂停下载按钮
- (IBAction)stopDownload:(id)sender;
//显示当前下载进度
@property (weak, nonatomic) IBOutlet UILabel *progLabel;
@property (nonatomic, strong) NSString *cacheFile;
//只保存下载链接和上一次下载到的数据的位置
@property (nonatomic, strong) NSData *resumeData;
@property (nonatomic, strong) NSURLSessionDownloadTask *downloadTask;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (IBAction)downloadFile:(id)sender {
NSURL *url = [NSURL URLWithString:@"http://localhost/uploads/Google.and.the.World.Brain.mkv"];
[self downloadFileWithURL:url];
}
- (IBAction)stopDownload:(id)sender {
__weak typeof(self) selfVc = self;
[self.downloadTask cancelByProducingResumeData:^(NSData *resumeData) {
selfVc.resumeData = resumeData;
selfVc.downloadTask = nil;
}];
}
//验证NSURLSession文件下载的操作在后台运行
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"哈哈");
}
//保存文件到沙盒,避免重复下载
- (void)setCacheFile:(NSString *)urlStr
{
NSString *cacheDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
//这里用MD5加密的方式对下载的文件进行命名
urlStr = [urlStr myMD5];
_cacheFile = [cacheDir stringByAppendingPathComponent:urlStr];
}
- (long long)cacheFileSize
{
NSDictionary *dict = [[NSFileManager defaultManager] attributesOfItemAtPath:self.cacheFile error:NULL];
return [dict[NSFileSize] longLongValue];
}
- (void)downloadFileWithURL:(NSURL *)url
{
self.cacheFile = [url absoluteString];
//如果是相同的下载链接,MD5加密结果一样,因此可以根据当前文件名指向的文件的大小,判断文件是否重复下载
if ([self cacheFileSize] != 0) {
NSLog(@"文件已存在");
return;
}
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//之前下载过就调用downloadTaskWithResumeData继续下载
if (self.resumeData == nil) {
self.downloadTask = [session downloadTaskWithURL:url];
}else {
self.downloadTask = [session downloadTaskWithResumeData:self.resumeData];
}
[self.downloadTask resume];
}
/**
* 下载完毕后调用
*
* @param location 文件临时地址
*/
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
NSLog(@"finish downloading");
NSFileManager *fm = [[NSFileManager alloc] init];
[fm moveItemAtPath:location.path toPath:self.cacheFile error:nil];
}
/**
* 每次写入沙盒完毕调用
*
* @param bytesWritten 这次写入的大小
* @param totalBytesWritten 已经写入沙盒的大小
* @param totalBytesExpectedToWrite 文件总大小
*/
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
//下载操作在后台运行,这里的代码在主线程运行
NSLog(@"下载进度: %f", (double)totalBytesWritten);
self.progLabel.text = [NSString stringWithFormat:@"下载进度: %.2f%%", 100 * (double)totalBytesWritten/totalBytesExpectedToWrite];
}
/**
* 恢复下载时调用
* @param fileOffset 上次下载到的位置
* @param expectedTotalBytes 文件总大小
*/
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{
NSLog(@"恢复下载: %lld %lld", fileOffset, expectedTotalBytes);
}
/**
* 下载失败时会进入此方法,也可以用来恢复保存数据
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
self.resumeData = error.userInfo[NSURLSessionDownloadTaskResumeData];
}
其中使用MD5加密的代码如下所示:
#import "NSString+Password.h"
#import <CommonCrypto/CommonDigest.h>
//随意写一串字符串
static NSString *token = @"fdhakfhieoafdklkhu3904njkgez.z‘vz;fz546489e35/.4,#498##84934/hifoefoa";
@implementation NSString (Password)
- (NSString *)myMD5
{
NSString *str = [NSString stringWithFormat:@"%@%@", self, token];
return [str MD5];
}
#pragma mark 使用MD5加密字符串
- (NSString *)MD5
{
const char *cStr = [self UTF8String];
unsigned char digest[CC_MD5_DIGEST_LENGTH];
CC_MD5(cStr, strlen(cStr), digest);
NSMutableString *result = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
[result appendFormat:@"%02x", digest[i]];
}
return result;
}
NSURLSession(Get & Post,JSON、XML数据解析,文件上传下载)
标签:
原文地址:http://blog.csdn.net/u011330638/article/details/51713354