iOS开发之NSURLSessionUploadTask上传数据

简介: 苹果在 iOS9 之后已经废弃了NSURLConnection,NSURLSession成为其替代者,其基本知识网上很多,主要可以从NSURLSessionDataTask 、NSURLSessionDownloadTask 和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 )

img_19306d0855a4ea8e6168b54087e2a381.png
Paste_Image.png

客户端开发

环境:Xcode 7.3.1

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

img_f9466e866b98a0c2b6ddab5a27968c18.png
界面.png

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

img_1f5510b37062c73d6dacfa15021ec074.png
上传必填字段.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

最终效果

先看客户端的表象

img_216b8692cdcd9507e0bcbe3e853bc38e.gif
客户端演示.gif

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

img_b58d99bc5d7df94ec0be7a30ce39bc75.gif
服务器端.gif
目录
相关文章
|
26天前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
105 4
|
27天前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
4天前
|
iOS开发 开发者 MacOS
深入探索iOS开发中的SwiftUI框架
【10月更文挑战第21天】 本文将带领读者深入了解Apple最新推出的SwiftUI框架,这一革命性的用户界面构建工具为iOS开发者提供了一种声明式、高效且直观的方式来创建复杂的用户界面。通过分析SwiftUI的核心概念、主要特性以及在实际项目中的应用示例,我们将展示如何利用SwiftUI简化UI代码,提高开发效率,并保持应用程序的高性能和响应性。无论你是iOS开发的新手还是有经验的开发者,本文都将为你提供宝贵的见解和实用的指导。
85 66
|
14天前
|
开发框架 Android开发 iOS开发
安卓与iOS开发中的跨平台策略:一次编码,多平台部署
在移动应用开发的广阔天地中,安卓和iOS两大阵营各占一方。随着技术的发展,跨平台开发框架应运而生,它们承诺着“一次编码,到处运行”的便捷。本文将深入探讨跨平台开发的现状、挑战以及未来趋势,同时通过代码示例揭示跨平台工具的实际运用。
|
18天前
|
Java 调度 Android开发
安卓与iOS开发中的线程管理差异解析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自拥有独特的魅力。如同东西方文化的差异,它们在处理多线程任务时也展现出不同的哲学。本文将带你穿梭于这两个平台之间,比较它们在线程管理上的核心理念、实现方式及性能考量,助你成为跨平台的编程高手。
|
20天前
|
存储 前端开发 Swift
探索iOS开发:从新手到专家的旅程
本文将带您领略iOS开发的奇妙之旅,从基础概念的理解到高级技巧的掌握,逐步深入iOS的世界。文章不仅分享技术知识,还鼓励读者在编程之路上保持好奇心和创新精神,实现个人成长与技术突破。
|
23天前
|
安全 IDE Swift
探索iOS开发之旅:从初学者到专家
在这篇文章中,我们将一起踏上iOS开发的旅程,从基础概念的理解到深入掌握核心技术。无论你是编程新手还是希望提升技能的开发者,这里都有你需要的指南和启示。我们将通过实际案例和代码示例,展示如何构建一个功能齐全的iOS应用。准备好了吗?让我们一起开始吧!
|
28天前
|
安全 Swift iOS开发
Swift 与 UIKit 在 iOS 应用界面开发中的关键技术和实践方法
本文深入探讨了 Swift 与 UIKit 在 iOS 应用界面开发中的关键技术和实践方法。Swift 以其简洁、高效和类型安全的特点,结合 UIKit 丰富的组件和功能,为开发者提供了强大的工具。文章从 Swift 的语法优势、类型安全、编程模型以及与 UIKit 的集成,到 UIKit 的主要组件和功能,再到构建界面的实践技巧和实际案例分析,全面介绍了如何利用这些技术创建高质量的用户界面。
29 2
|
1月前
|
安全 数据处理 Swift
深入探索iOS开发中的Swift语言特性
本文旨在为开发者提供对Swift语言在iOS平台开发的深度理解,涵盖从基础语法到高级特性的全面分析。通过具体案例和代码示例,揭示Swift如何简化编程过程、提高代码效率,并促进iOS应用的创新。文章不仅适合初学者作为入门指南,也适合有经验的开发者深化对Swift语言的认识。
48 9
|
1月前
|
vr&ar Android开发 iOS开发
安卓与iOS开发中的用户界面设计原则
【10月更文挑战第41天】探索移动应用开发的精髓,本文将深入分析安卓和iOS平台上用户界面设计的核心原则。通过比较两大操作系统的设计哲学,我们将揭示如何打造直观、易用且美观的应用程序界面。无论你是初学者还是资深开发者,这篇文章都将为你提供宝贵的见解和实用的技巧,帮助你在竞争激烈的应用市场中脱颖而出。