转自:http://www.cnblogs.com/kenshincui/p/4042190.html
扩展--文件上传
在做WEB应用程序开发时,如果要上传一个文件往往会给form设置一个enctype=”multipart/form-data”的属性,不设置这个值在后台无法正常接收文件。在WEB开发过程中,form的这个属性其实本质就是指定请求头中Content-Type类型,当然使用GET方法提交就不用说了,必须使用URL编码。但是如果使用POST方法传递数据其实也是类似的,同样需要进行编码,具体编码方式其实就是通过enctype属性进行设置的。常用的属性值有:
- application/x-www-form-urlencoded:默认值,发送前对所有发送数据进行url编码,支持浏览器访问,通常文本内容提交常用这种方式。
- multipart/form-data:多部分表单数据,支持浏览器访问,不进行任何编码,通常用于文件传输(此时传递的是二进制数据) 。
- text/plain:普通文本数据类型,支持浏览器访问,发送前其中的空格替换为“+”,但是不对特殊字符编码。
- application/json:json数据类型,浏览器访问不支持 。
- text/xml:xml数据类型,浏览器访问不支持。
要实现文件上传,必须采用POST上传,同时请求类型必须是multipart/form-data。在Web开发中,开发人员不必过多的考虑mutiparty/form-data更多的细节,一般使用file控件即可完成文件上传。但是在iOS中如果要实现文件上传,就没有那么简单了,我们必须了解这种数据类型的请求是如何工作的。
下面是在浏览器中上传一个文件时,发送的请求头:
这是发送的请求体内容:
在请求头中,最重要的就是Content-Type,它的值分为两部分:前半部分是内容类型,前面已经解释过了;后面是边界boundary用来分隔表单中不同部分的数据,后面一串数字是浏览器自动生成的,它的格式并不固定,可以是任意字符。和请求体中的源代码部分进行对比不难发现其实boundary的内容和请求体的数据部分前的字符串相比少了两个“--”。请求体中Content-Disposition中指定了表单元素的name属性和文件名称,同时指定了Content-Type表示文件类型。当然,在请求体中最重要的就是后面的数据部分,它其实就是二进制字符串。由此可以得出以下结论,请求体内容由如下几部分按顺序执行组成:
--boundary
Content-Disposition:form-data;name=”表单控件名称”;filename=”上传文件名称”
Content-Type:文件MIME Types
文件二进制数据;
--boundary--
了解这些信息后,只要使用POST方法给服务器端发送请求并且请求内容按照上面的格式设置即可。
下面是实现代码:
// // KCMainViewController.m // UrlConnection // // Created by Kenshin Cui on 14-3-22. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "KCMainViewController.h" #define kUrl @"http://192.168.1.208/FileUpload.aspx" #define kBOUNDARY_STRING @"KenshinCui" @interface KCMainViewController ()<NSURLConnectionDataDelegate>{ UITextField *_textField; UIButton *_button; } @end @implementation KCMainViewController #pragma mark - UI方法 - (void)viewDidLoad { [super viewDidLoad]; [self layoutUI]; } #pragma mark - 私有方法 #pragma mark 界面布局 -(void)layoutUI{ //地址栏 _textField=[[UITextField alloc]initWithFrame:CGRectMake(10, 50, 300, 25)]; _textField.borderStyle=UITextBorderStyleRoundedRect; _textField.textColor=[UIColor colorWithRed:0 green:146/255.0 blue:1.0 alpha:1.0]; _textField.text=@"pic.jpg"; [self.view addSubview:_textField]; //上传按钮 _button=[[UIButton alloc]initWithFrame:CGRectMake(10, 500, 300, 25)]; [_button setTitle:@"上传" forState:UIControlStateNormal]; [_button setTitleColor:[UIColor colorWithRed:0 green:146/255.0 blue:1.0 alpha:1.0] forState:UIControlStateNormal]; [_button addTarget:self action:@selector(uploadFile) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:_button]; } #pragma mark 取得请求链接 -(NSURL *)getUploadUrl:(NSString *)fileName{ NSString *urlStr=[NSString stringWithFormat:@"%@?file=%@",kUrl,fileName]; urlStr=[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; NSURL *url=[NSURL URLWithString:urlStr]; return url; } #pragma mark 取得mime types -(NSString *)getMIMETypes:(NSString *)fileName{ return @"image/jpg"; } #pragma mark 取得数据体 -(NSData *)getHttpBody:(NSString *)fileName{ NSMutableData *dataM=[NSMutableData data]; NSString *strTop=[NSString stringWithFormat:@"--%@\nContent-Disposition: form-data; name=\"%@\"; filename=\"%@\"\nContent-Type: %@\n\n",kBOUNDARY_STRING,fileName,[self getMIMETypes:fileName]]; NSString *strBottom=[NSString stringWithFormat:@"\n--%@--",kBOUNDARY_STRING]; NSString *filePath=[[NSBundle mainBundle] pathForResource:fileName ofType:nil]; NSData *fileData=[NSData dataWithContentsOfFile:filePath]; [dataM appendData:[strTop dataUsingEncoding:NSUTF8StringEncoding]]; [dataM appendData:fileData]; [dataM appendData:[strBottom dataUsingEncoding:NSUTF8StringEncoding]]; return dataM; } #pragma mark 上传文件 -(void)uploadFile{ NSString *fileName=_textField.text; NSMutableURLRequest *request= [NSMutableURLRequest requestWithURL:[self getUploadUrl:fileName] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:5.0f]; request.HTTPMethod=@"POST"; NSData *data=[self getHttpBody:fileName]; //通过请求头设置 [request setValue:[NSString stringWithFormat:@"%lu",(unsigned long)data.length] forHTTPHeaderField:@"Content-Length"]; [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",kBOUNDARY_STRING] forHTTPHeaderField:@"Content-Type"]; //设置数据体 request.HTTPBody=data; //发送请求 [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc]init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { if(connectionError){ NSLog(@"error:%@",connectionError.localizedDescription); } }]; } @end