iOS开发之NSURLSessionUploadTask上传数据

苹果在 iOS9 之后已经废弃了NSURLConnectionNSURLSession成为其替代者,其基本知识网上很多,主要可以从NSURLSessionDataTaskNSURLSessionDownloadTaskNSURLSessionUploadTask入手学习。最近在写案例时发现其中的NSURLSessionUploadTask还是有着不少的坑,在开发时有时候很难一次性成功。所以将研究的过程记录与分享一下。我会以一个完整的案例来讲解如何使用。

服务器开发

环境:IDEA 14 +Tomcat 8.x+JDK 8

1、编写服务器端代码 由于上传数据与下载数据不同,下载的时候只要把数据丢进服务器就可以了。但是上传需要服务器自己来处理。所以以Java Servlet来写服务器端,由于Servlet 3.0 以后可以直接处理文件上传,所以相对比较简单,代码如下,注释很详细。

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;

@MultipartConfig //标识Servlet支持文件上传
public class UploadServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        //存储路径为我们的根目录
        String storePath = req.getServletContext().getRealPath("/");
        //获取part对象,参数为客户端表单中的name属性的值
        Part part = req.getPart("myfile");
        //Servlet3没有提供直接获取文件名的方法,需要从请求头中解析出来
        //获取请求头
        String header = part.getHeader("Content-Disposition");
        //获取文件名
        String fileName = header.substring(header.lastIndexOf("=") + 2, header.length() - 1);
        //把文件写到指定路径
        part.write(storePath + File.separator + fileName);
        //回写数据给客户端
        resp.setCharacterEncoding("UTF-8");
        PrintWriter pw = resp.getWriter();
        pw.print("上传成功");
    }

}

2、部署代码 找到Tomcat根目录下的conf文件夹,打开server.xml,在最后加上一行代码,path就是访问的项目路径,docBase就是项目编译后的位置。

<Context path="/AppTestAPI" docBase="E:\AppTestAPI\out\artifacts\AppTestAPI" auth="Container" /> 

3、启动服务器 通过浏览器访问http://localhost能出现如下的界面,至此完成服务器端工作。( 由于我修改了默认端口,所以没有加8080 )

Paste_Image.png

客户端开发

环境:Xcode 7.3.1

1、创建项目 创建一个项目,布置界面,设置支持http网络访问。主界面如下:

界面.png

2、编写代码 NSURLSession使用都是一个套路:创建请求,创建任务,执行任务,成功回调。但是在使用NSURLSessionUploadTask进行上传时最麻烦的是上传数据的构造,其遵循严格的规范,如下图,不能随意书写,不能随意书写,不能随意书写~,重要的事情说三遍,否则坑得你生活不能自理。

上传必填字段.png

下面是ViewController的代码,其中最核心的是getData方法。

#import "ViewController.h"

 //分隔符
#define YFBoundary @"AnHuiWuHuYungFan"
//换行
#define YFEnter [@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]
//NSString转NSData
#define YFEncode(string) [string dataUsingEncoding:NSUTF8StringEncoding]

@interface ViewController ()<NSURLSessionTaskDelegate>

@property (weak, nonatomic) IBOutlet UIProgressView *uploadProgress;
@property (weak, nonatomic) IBOutlet UILabel *uploadInfo;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    //1、确定URL
    NSURL *url = [NSURL URLWithString:@"http://192.168.0.5/AppTestAPI/UploadServlet"];    
    //2、确定请求
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];  
    //3、设置请求头
    NSString *head = [NSString stringWithFormat:@"multipart/form-data;boundary=%--@", YFBoundary];
    [request setValue:head forHTTPHeaderField:@"Content-Type"];
    //4、设置请求方式,上传时必须是Post请求
    request.HTTPMethod = @"POST";
    //5、创建NSURLSession
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    //6、获取上传的数据
    NSData *uploadData = [self getData];
    //7、创建上传任务 上传的数据来自getData方法
    NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:uploadData completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        
       //上传成功以后改变UILabel文本为服务器返回的数据
       self.uploadInfo.text =[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
    
    }];
    //8、执行上传任务
    [task resume];
    
}


/**
 *  设置请求体
 *
 *  @return 请求体内容
 */
-(NSData *)getData
{

    NSMutableData *data = [NSMutableData data];
    
    //1、开始标记
    //--
    [data appendData:YFEncode(@"--")];
    //boundary
    [data appendData:YFEncode(YFBoundary)];
    //换行符
    [data appendData:YFEnter];
    //文件参数名 Content-Disposition: form-data; name="myfile"; filename="wall.jpg"
    [data appendData:YFEncode(@"Content-Disposition:form-data; name=\"myfile\"; filename=\"wall.jpg\"")];
    //换行符
    [data appendData:YFEnter];
    //Content-Type 上传文件的类型 MIME
    [data appendData:YFEncode(@"Content-Type:image/jpeg")];
    //换行符
    [data appendData:YFEnter];
    //换行符
    [data appendData:YFEnter];
    //2、上传的文件数据
    
    //图片数据  并且转换为Data
    UIImage *image = [UIImage imageNamed:@"wall.jpg"];
    NSData *imagedata = UIImageJPEGRepresentation(image, 1.0);
    [data appendData:imagedata];
    //换行符
    [data appendData:YFEnter];
 
    //3、结束标记
    //--
    [data appendData:YFEncode(@"--")];
    //boundary
    [data appendData:YFEncode(YFBoundary)];
    //--
    [data appendData:YFEncode(@"--")];
    //换行符
    [data appendData:YFEnter];
    
    return data;
    
}

#pragma mark - 代理方法 只要给服务器上传数据就会调用 可以计算出上传进度
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{

    //设置进度条
    self.uploadProgress.progress = 1.0 * totalBytesSent / totalBytesExpectedToSend;
}

@end

最终效果

先看客户端的表象

客户端演示.gif

再看服务器端最终上传的数据,不重要以为是图片,静静观察一会儿~~~有变化

服务器端.gif

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏xx_Cc的学习总结专栏

iOS-控制器View的创建和生命周期

36560
来自专栏哈雷彗星撞地球

SDWebImageV3.7.5源码解析

SDWebImage更新到如今这个版本,过程做了许多改进,性能已经非常的好了。以前就粗略的看过SDWebImage的源码,但是未做记录整理。再次阅读还是受益良多...

10530
来自专栏一“技”之长

iOS9系列专题二——全新的搜索功能api 原

        iOS9中为我们提供了许多新的api,搜索功能的加强无疑是其中比较显眼的一个。首先,我们先设想一下:如果在你的app中定义一种标识符,在siri...

12350
来自专栏小蠢驴iOS专题

手把手教你使用Bugly收集线上崩溃信息

1.5K30
来自专栏一“技”之长

iOS网络编程之五——请求回执类NSURLResponse属性简介

        NSURLResponse类中存放请求的回执信息,在发送网络请求时,如果请求成功,首先会接收到服务端的回执信息,直接开始接收具体的返回数据。NS...

8630
来自专栏DannyHoo的专栏

iOS开发中OC与JS交互(UIWebView)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010105969/article/details/...

59430
来自专栏冰霜之地

Weex 是如何在 iOS 客户端上跑起来的

2016年4月21日,阿里巴巴在Qcon大会上宣布跨平台移动开发工具Weex开放内测邀请。Weex能够完美兼顾性能与动态性,让移动开发者通过简捷的前端语法写出N...

58130
来自专栏一“技”之长

IOS中调用系统拨打电话与发送短信 原

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString string...

9820
来自专栏xx_Cc的学习总结专栏

iOS-网络编程(三)AFNetworking使用

41070
来自专栏xx_Cc的学习总结专栏

iOS-网络编程(二)文件上传和断点离线下载

44180

扫码关注云+社区

领取腾讯云代金券