iOS ftp 上次
2016-04-08 17:14
966 查看
网络上大多数的文件的上传都是通过ASIHttpRequest库以form表单的形式完成的,比较简单,代码逻辑也比较清晰,在此笔者就不赘述了。而今天要跟大家分享的是在iOS端使用ftp的形式进行上传的方式。
由于网上关于在iOS端ftp上传的资料很少,而且刚好笔者也需要用到ftp,因此,便促成了此文的诞生。
但是在使用上也存在一些的限制,CFFTPStream 提供的ftp操作比较基础,它并不是完全的FTP客户端API,不支持所有的FTP操作。
例如:
FTPS(FTP over TLS)
删除文件(没法像Android里面的comment-net包那样可以直接删除服务器文件)
重命名文件
自定义FTP命令
另外,CFFTPStream的目录解析也局限于简单的目录,只能够解析一般的基于UNIX FTP服务的目录。
![](https://oscdn.geek-share.com/Uploads/Images/Content/202008/03/f79bc154e1d80196c46921eb355c8e4c)
第二步:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202008/03/e32e84397a3900e1c98340968658206d)
![](https://oscdn.geek-share.com/Uploads/Images/Content/202008/03/ae85a7c583d734c0b0e2311367bdce79)
文件路径输入框
服务器路径输入框
账号输入框
密码输入框
传输状态显示
上传按钮
使用CFFTPStream进行上传,首先要遵循NSStreamDelegate协议。并实现协议中的委托方法。
下面为NSStreamDelegate协议的内容。
@protocol NSStreamDelegate <NSObject>
@optional
- (void)stream:(NSStream *)aStreamhandleEvent:(NSStreamEvent)eventCode;
@end
size_t与uint8_t为c扩展的类型变量,在用于跨语言的时候比较方便,uint8_t为一个字节大小的无符号int类型,size_t在32位与64位机器上代表不同的大小,分别为4个字节与8个字节。
因为buffer被声明为一个数组,所以必须要使用自定义的getter方法,synthesised方法是不会被编译的。
点击上传按钮,并生成networkStream流
实现代理方法:
bytesWritten为实际写入的数据量,虽然缓冲区大小是固定的值,但每次写入并不一定是充满缓冲区,可以看到NSLog的输出值。(上传图片大小为428492字节,约430k)。
首先是 NSStreamEventOpenCompleted
打开事件完成的回调,然后不断发送 NSStreamEventHasSpaceAvailable
消息完成整个上传过程。
可以看到在传输的时候,bufferOffset并不是每次都是32768,所以self.bufferOffset!= self.bufferLimit是一个续传上次未完数据的过程,而当self.bufferOffset = self.bufferLimit时,即上次的32768已经传完了,此时将bufferOffset重新设置为0。
最后一次传输, bufferLimit
的大小为剩下的所有数据,并不一定填满缓冲区最大值。
2013-10-08 14:56:12.855FtpUpLoad[3278:c07] NSStreamEventOpenCompleted
2013-10-08 14:56:12.856FtpUpLoad[3278:c07] NSStreamEventHasSpaceAvailable
2013-10-08 14:56:12.856FtpUpLoad[3278:c07] bufferOffset is 0
2013-10-08 14:56:12.856FtpUpLoad[3278:c07] bufferLimit is 0
2013-10-08 14:56:12.856FtpUpLoad[3278:c07] NSStreamEventHasSpaceAvailable
2013-10-08 14:56:12.857FtpUpLoad[3278:c07] bufferOffset is 32768
2013-10-08 14:56:12.857FtpUpLoad[3278:c07] bufferLimit is 32768
。。。
2013-10-08 14:56:12.859FtpUpLoad[3278:c07] NSStreamEventHasSpaceAvailable
2013-10-08 14:56:12.859FtpUpLoad[3278:c07] bufferOffset is 19308
2013-10-08 14:56:12.859FtpUpLoad[3278:c07] bufferLimit is 32768
。。。
2013-10-08 14:56:12.883FtpUpLoad[3278:c07] bufferOffset is 2308
2013-10-08 14:56:12.883FtpUpLoad[3278:c07] bufferLimit is 2308
处理上传结果:
在此方法中关闭链接、循环、设置代理为空。
demo下载地址:
http://download.csdn.net/detail/yang8456211/6369199
由于网上关于在iOS端ftp上传的资料很少,而且刚好笔者也需要用到ftp,因此,便促成了此文的诞生。
一、 简介
在iOS端的ftp上传使用的是CFNetWork框架,它处于核心服务框架中,提供了一个抽象化的网络协议库。使用CFNetWork框架中的CFFTPStream类提供的API能够做很多ftp操作,例如上传文件、下载文件、下载目录列表,创建远程目录等等。但是在使用上也存在一些的限制,CFFTPStream 提供的ftp操作比较基础,它并不是完全的FTP客户端API,不支持所有的FTP操作。
例如:
FTPS(FTP over TLS)
删除文件(没法像Android里面的comment-net包那样可以直接删除服务器文件)
重命名文件
自定义FTP命令
另外,CFFTPStream的目录解析也局限于简单的目录,只能够解析一般的基于UNIX FTP服务的目录。
二、 配置
要使用CFFTPStream的功能,需要导入CFNetWork 框架,方便的是不用去下载第三方包,直接在Xcode里面添加就好了。1)添加CFNetWork framework
第一步:第二步:
2)新建工程,取名为FtpUpLoad
建立SingleView的项目,配置好nib文件。包括以下控件:文件路径输入框
服务器路径输入框
账号输入框
密码输入框
传输状态显示
上传按钮
三、 核心代码
1)YGViewController.h(根控制器)
#import <UIKit/UIKit.h> enum { kSendBufferSize = 32768//上传的缓冲区大小,可以设置 }; @interface YGViewController : UIViewController <UITextFieldDelegate,NSStreamDelegate>{ uint8_t _buffer[kSendBufferSize]; } @property (retain, nonatomic) IBOutlet UITextField *fileInput;//文件路径 @property (retain, nonatomic) IBOutlet UITextField *serverInput;//服务器路径 @property (retain, nonatomic) IBOutlet UITextField *accountInput;//账号 @property (retain, nonatomic) IBOutlet UITextField *passwordInput;//密码 @property (retain, nonatomic) IBOutlet UILabel *status;//状态栏 - (IBAction)sendAction:(id)sender;//点击上传事件 - (IBAction)textFieldDoneEditing:(id)sender;//Did End On Exit 事件 @end
使用CFFTPStream进行上传,首先要遵循NSStreamDelegate协议。并实现协议中的委托方法。
下面为NSStreamDelegate协议的内容。
@protocol NSStreamDelegate <NSObject>
@optional
- (void)stream:(NSStream *)aStreamhandleEvent:(NSStreamEvent)eventCode;
@end
2)YGViewController.m
定义相关内部变量与存取方法:@interface YGViewController () //内部变量 @property (nonatomic, readonly) BOOL isSending; @property (nonatomic, retain) NSOutputStream *networkStream; @property (nonatomic, retain) NSInputStream *fileStream; @property (nonatomic, readonly) uint8_t *buffer; @property (nonatomic, assign) size_t bufferOffset; @property (nonatomic, assign) size_t bufferLimit; @end //存取方法 @implementation YGViewController @synthesize fileInput = _fileInput; @synthesize serverInput = _serverInput; @synthesize status = _status; @synthesize accountInput = _accountInput; @synthesize passwordInput = _passwordInput; //buffer getter - (uint8_t *)buffer { return self->_buffer; }
size_t与uint8_t为c扩展的类型变量,在用于跨语言的时候比较方便,uint8_t为一个字节大小的无符号int类型,size_t在32位与64位机器上代表不同的大小,分别为4个字节与8个字节。
因为buffer被声明为一个数组,所以必须要使用自定义的getter方法,synthesised方法是不会被编译的。
点击上传按钮,并生成networkStream流
- (IBAction)sendAction:(id)sender { NSURL *url;//ftp服务器地址 NSString *filePath;//图片地址 NSString *account;//账号 NSString *password;//密码 CFWriteStreamRef ftpStream; //获得输入 url = [NSURL URLWithString:_serverInput.text]; filePath = _fileInput.text; account = _accountInput.text; password = _passwordInput.text; //添加后缀(文件名称) url = [NSMakeCollectable(CFURLCreateCopyAppendingPathComponent(NULL, (CFURLRef) url, (CFStringRef) [filePath lastPathComponent], false)) autorelease]; //读取文件,转化为输入流 self.fileStream = [NSInputStream inputStreamWithFileAtPath:filePath]; [self.fileStream open]; //为url开启CFFTPStream输出流 ftpStream = CFWriteStreamCreateWithFTPURL(NULL, (CFURLRef) url); self.networkStream = (NSOutputStream *) ftpStream; //设置ftp账号密码 [self.networkStream setProperty:account forKey:(id)kCFStreamPropertyFTPUserName]; [self.networkStream setProperty:password forKey:(id)kCFStreamPropertyFTPPassword]; //设置networkStream流的代理,任何关于networkStream的事件发生都会调用代理方法 self.networkStream.delegate = self; //设置runloop [self.networkStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [self.networkStream open]; //完成释放链接 CFRelease(ftpStream); }
实现代理方法:
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode { //aStream 即为设置为代理的networkStream switch (eventCode) { case NSStreamEventOpenCompleted: { NSLog(@"NSStreamEventOpenCompleted"); } break; case NSStreamEventHasBytesAvailable: { NSLog(@"NSStreamEventHasBytesAvailable"); assert(NO); // 在上传的时候不会调用 } break; case NSStreamEventHasSpaceAvailable: { NSLog(@"NSStreamEventHasSpaceAvailable"); NSLog(@"bufferOffset is %zd",self.bufferOffset); NSLog(@"bufferLimit is %zu",self.bufferLimit); if (self.bufferOffset == self.bufferLimit) { NSInteger bytesRead; bytesRead = [self.fileStream read:self.buffer maxLength:kSendBufferSize]; if (bytesRead == -1) { //读取文件错误 [self _stopSendWithStatus:@"读取文件错误"]; } else if (bytesRead == 0) { //文件读取完成 上传完成 [self _stopSendWithStatus:nil]; } else { self.bufferOffset = 0; self.bufferLimit = bytesRead; } } if (self.bufferOffset != self.bufferLimit) { //写入数据 NSInteger bytesWritten;//bytesWritten为成功写入的数据 bytesWritten = [self.networkStream write:&self.buffer[self.bufferOffset] maxLength:self.bufferLimit - self.bufferOffset]; assert(bytesWritten != 0); if (bytesWritten == -1) { [self _stopSendWithStatus:@"网络写入错误"]; } else { self.bufferOffset += bytesWritten; } } } break; case NSStreamEventErrorOccurred: { [self _stopSendWithStatus:@"Stream打开错误"]; assert(NO); } break; case NSStreamEventEndEncountered: { // 忽略 } break; default: { assert(NO); } break; } }
bytesWritten为实际写入的数据量,虽然缓冲区大小是固定的值,但每次写入并不一定是充满缓冲区,可以看到NSLog的输出值。(上传图片大小为428492字节,约430k)。
首先是 NSStreamEventOpenCompleted
打开事件完成的回调,然后不断发送 NSStreamEventHasSpaceAvailable
消息完成整个上传过程。
可以看到在传输的时候,bufferOffset并不是每次都是32768,所以self.bufferOffset!= self.bufferLimit是一个续传上次未完数据的过程,而当self.bufferOffset = self.bufferLimit时,即上次的32768已经传完了,此时将bufferOffset重新设置为0。
最后一次传输, bufferLimit
的大小为剩下的所有数据,并不一定填满缓冲区最大值。
2013-10-08 14:56:12.855FtpUpLoad[3278:c07] NSStreamEventOpenCompleted
2013-10-08 14:56:12.856FtpUpLoad[3278:c07] NSStreamEventHasSpaceAvailable
2013-10-08 14:56:12.856FtpUpLoad[3278:c07] bufferOffset is 0
2013-10-08 14:56:12.856FtpUpLoad[3278:c07] bufferLimit is 0
2013-10-08 14:56:12.856FtpUpLoad[3278:c07] NSStreamEventHasSpaceAvailable
2013-10-08 14:56:12.857FtpUpLoad[3278:c07] bufferOffset is 32768
2013-10-08 14:56:12.857FtpUpLoad[3278:c07] bufferLimit is 32768
。。。
2013-10-08 14:56:12.859FtpUpLoad[3278:c07] NSStreamEventHasSpaceAvailable
2013-10-08 14:56:12.859FtpUpLoad[3278:c07] bufferOffset is 19308
2013-10-08 14:56:12.859FtpUpLoad[3278:c07] bufferLimit is 32768
。。。
2013-10-08 14:56:12.883FtpUpLoad[3278:c07] bufferOffset is 2308
2013-10-08 14:56:12.883FtpUpLoad[3278:c07] bufferLimit is 2308
处理上传结果:
- (void)_stopSendWithStatus:(NSString *)statusString { if (self.networkStream != nil) { [self.networkStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; self.networkStream.delegate = nil; [self.networkStream close]; self.networkStream = nil; } if (self.fileStream != nil) { [self.fileStream close]; self.fileStream = nil; } [self _sendDidStopWithStatus:statusString]; }
在此方法中关闭链接、循环、设置代理为空。
demo下载地址:
http://download.csdn.net/detail/yang8456211/6369199
相关文章推荐
- 打造更轻量级的ViewControllers
- [IOS 开发] GCD常用方法
- IOS 将请求参数字典转换为参数字符串
- iOS-AFNetworking-上传图片
- iOS攻城狮修炼之路
- ios 开发,并不是我们想象的那样难
- iOS 设计模式之抽象工厂
- iOS __runtime
- LINK - Xamarin.iOS + MvvmCross: TableCell Without NiB e.g. XibFree
- ios 修改项目名称 修改类名
- iOS小demo之无限后台
- iOS之FMDB
- iOS开发常见报错及解决方案 by STP
- iOS开发--多线程
- Run loops 的具体执行顺序(ios oc)
- iOS学习笔记09-核心动画CoreAnimation
- IOS中TableView的用法
- ios数据存取
- iOS 音乐播放器的实现
- 常驻子线程实现(iOS oc)