美文网首页iOS开发成长之路
NSURLSession笔记(二)上传文件

NSURLSession笔记(二)上传文件

作者: WeiHing | 来源:发表于2016-06-05 13:51 被阅读852次

    NSURLSession系列笔记:
    NSURLSession笔记 文件下载

    总体思路和NSURLConnection上传文件是相似的,而且并不会比NSURLConnection方便多少,以前写过的一篇NSURLConnection上传文件的笔记会写得更详细,这里就简单说一下NSURLSession的上传,毕竟它们的流程大致相同的。NSURLConnection笔记-上传文件

    • “不正确”的上传方式,使用dataTask。
    [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    }] resume];
    

    普通网络任务一般不用来做上传或者下载,但尽管如此,如果用来上传、小文件的下载的话还是能够实现的。

    • 正确的上传方式,使用uploadTask,主要有一下两种:
    NSURLSessionUploadTask *dataTask=[[NSURLSession sharedSession] uploadTaskWithRequest:request fromFile:fileName completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
       }];
    NSURLSessionUploadTask *dataTask=[[NSURLSession sharedSession] uploadTaskWithRequest:request fromData:bodyData completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
       }];
    

    Session会自动计算Header的Content-length。通常,还需要提供一些服务器需要的Header,Content-Type就往往需要提供。

    上传文件:

     // 1. 创建请求   
    NSURL *url = [NSURL URLWithString:@"xxxurl"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];    
    
    request.HTTPMethod = @"POST";    
    // 设置请求头,告诉服务器本次长传的时文件信息 // 这一块的内容是不变的!
    NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",bound];
    [request setValue:contentType forHTTPHeaderField:@"Content-Type"];
        
    // 设置请求体,拼接文件传输格式
    NSMutableString *bodyHeaderStr = [NSMutableString stringWithFormat:@"--%@\r\n",bound];
    [bodyHeaderStr appendFormat:@"Content-Disposition: form-data; name=%@; filename=%@\r\n",@"userfile",@"JSON"];
    [bodyHeaderStr appendString:@"Content-Type: application/octet-stream\r\n\r\n"];//两个换行
    NSData *fileData = [NSData dataWithContentsOfFile:@"本地文件路径"];  
    NSMutableString *bodyFooterStr = [NSMutableString stringWithFormat:@"\r\n--%@--",bound];
          //拼接成二进制数据
    NSMutableData *bodyData = [NSMutableData data];
    [bodyData appendData:[bodyHeaderStr dataUsingEncoding:NSUTF8StringEncoding]];
    [bodyData appendData:fileData];
    [bodyData appendData:[bodyFooterStr dataUsingEncoding:NSUTF8StringEncoding]];
    
    //request.HTTPBody = bodyData ;
        
    // 2. 发送请求
    [[[NSURLSession sharedSession] uploadTaskWithRequest:request fromData: bodyData completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    }] resume];
    

    后来用新浪微博的API来做了一下授权发布带图片的微博,上传图片。
    这里的 access_token 大家可以用自己的微博来测试 access_token->是通过自己的微博账号密码生成的 具体流程可以参照 http://www.cnblogs.com/ok-lanyan/archive/2012/07/15/2592070.html 以及官方api文档来做就好了

    - (IBAction)uploadPic:(id)sender {
        //1.构建URL
        NSURL *url=[NSURL URLWithString:@"https://api.weibo.com/2/statuses/upload.json"];
        //2.创建request请求
        //NSURLRequest 不可变的 NSMutableURLRequest可变的 可以设置请求属性
        NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:url];
        
        [request setHTTPMethod:@"POST"];
        [request setTimeoutInterval:120];
        [request setCachePolicy:NSURLRequestReturnCacheDataElseLoad];
        
        //请求头//upload task不会在请求头里添加content-type(上传数据类型)字段
        NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; charset=utf-8;boundary=%@", boundary];
        [request setValue:contentType forHTTPHeaderField:@"Content-Type"];
        //设置请求体
        //发送的微博需要这2个参数
        //access_token(微博令牌,根据用户名,密码生成的明文密码) status(微博内容)
        //pic (图片) ----因为图片转成字符串编码量太大如果直接拼接在URL里服务器无法识别其请求,所以要把图片数据放在请求体里
        
        //本地图片
        NSString *filePath = [[NSBundle mainBundle] pathForResource:@"桌面.jpg" ofType:nil];
        //拼接请求体
        NSData *bodyData=[self setBodydata:filePath];//(注意上面宏定义的请求体边界下面就要用上了)
        
        //3.创建网络会话
        NSURLSession *session=[NSURLSession sharedSession];
        //4.创建网络上传任务
        NSURLSessionUploadTask *dataTask=[session uploadTaskWithRequest:request fromData:bodyData completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            if (error == nil) {
                
                NSLog(@"%@",response);//202及是发布成功
                NSDictionary *jsonDic = [NSJSONSerialization JSONObjectWithData:data  options:NSJSONReadingMutableLeaves error:nil];
                        NSLog(@"jsonDic:%@",jsonDic);
                        NSLog(@"original_pic:%@",[jsonDic objectForKey:@"original_pic"]);
            };
        }];
        
        //5.发送网络任务
        [dataTask resume];
        
        
        //————————————————————————————POST请求体格式——————————————————————————————
        //这个格式比较繁琐,但是这是死格式,大家耐心看,就可以看出规律了。注意看红字分析
        
        //---->拼接成字符串,然后转成 NSData 返回
        
        /*
         HTTP请求头:
         ....
         multipart/form-data; charset=utf-8;boundary=AaB03x //上传数据类型 必须要设置其类型
         ....
         
         
         HTTP请求体:
         
         --AaB03x (边界到下一行用了换行,在oc里面 用 \r\n 来定义换一行 所以下面不要奇怪它的用法)
         Content-Disposition: form-data; name="key1"(这行到 value1 换了2行,所以,自然而然 \r\n\r\n )
         
         value1
         --AaB03x
         Content-disposition: form-data; name="key2"
         
         value2
         --AaB03x
         Content-disposition: form-data; name="key3"; filename="file"
         Content-Type: application/octet-stream
         
         图片数据...//NSData
         --AaB03x--(结束的分割线也不要落下)
         */
    }
    
    - (NSData *)setBodydata:(NSString *)filePath
    {
        //把文件转换为NSData
        NSData *fileData = [NSData dataWithContentsOfFile:filePath];
        
        //1.构造body string
        NSMutableString *bodyString = [[NSMutableString alloc] init];
        
        //2.拼接body string
        //(1)access_token
        [bodyString appendFormat:@"--%@\r\n", boundary];//(一开始的 --也不能忽略)
        [bodyString appendFormat:@"Content-Disposition: form-data; name=\"access_token\"\r\n\r\n"];
        [bodyString appendFormat:@"2.00cDQNuBJKVJQC6c99b892efwRBlYB\r\n"];
        
        //(2)status
        [bodyString appendFormat:@"--%@\r\n", boundary];
        [bodyString appendFormat:@"Content-Disposition: form-data; name=\"status\"\r\n\r\n"];
        [bodyString appendFormat:@"带图片的微博\r\n"];
        
        //(3)pic
        [bodyString appendFormat:@"--%@\r\n", boundary];
        [bodyString appendFormat:@"Content-Disposition: form-data; name=\"pic\"; filename=\"file\"\r\n"];
        [bodyString appendFormat:@"Content-Type: application/octet-stream\r\n\r\n"];
        
        
        //3.string --> data
        NSMutableData *bodyData = [NSMutableData data];
        //拼接的过程
        //前面的bodyString, 其他参数
        [bodyData appendData:[bodyString dataUsingEncoding:NSUTF8StringEncoding]];
        //图片数据
        [bodyData appendData:fileData];
        //4.结束的分隔线
        NSString *endStr = [NSString stringWithFormat:@"\r\n--%@--\r\n",boundary];
        //拼接到bodyData最后面
        [bodyData appendData:[endStr dataUsingEncoding:NSUTF8StringEncoding]];
        return bodyData;
    }
    

    ps:uploadTask没有对应的uploadDelegate,但是NSURLSessionDataDelegate,NSURLSessionDelegate,NSURLSessionTaskDelegate同样适用于UploadTask
    比如:-(void)URLSession: task: didSendBodyData: totalBytesSent: totalBytesExpectedToSend:获取上传进度

    相关文章

      网友评论

        本文标题:NSURLSession笔记(二)上传文件

        本文链接:https://www.haomeiwen.com/subject/oppirttx.html